"Fossies" - the Fresh Open Source Software Archive

Member "ettercap-0.8.3.1/src/interfaces/gtk3/ec_gtk3.c" (1 Aug 2020, 57992 Bytes) of package /linux/privat/ettercap-0.8.3.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ec_gtk3.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.8.3_vs_0.8.3.1.

    1 /*
    2     ettercap -- GTK+3/GNOME GUI
    3 
    4     Copyright (C) ALoR & NaGA
    5 
    6     This program is free software; you can redistribute it and/or modify
    7     it under the terms of the GNU General Public License as published by
    8     the Free Software Foundation; either version 2 of the License, or
    9     (at your option) any later version.
   10 
   11     This program is distributed in the hope that it will be useful,
   12     but WITHOUT ANY WARRANTY; without even the implied warranty of
   13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14     GNU General Public License for more details.
   15 
   16     You should have received a copy of the GNU General Public License
   17     along with this program; if not, write to the Free Software
   18     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   19 
   20 */
   21 
   22 #include <ec.h>
   23 
   24 #include <ec_gtk3.h>
   25 #include <ec_capture.h>
   26 #include <ec_version.h>
   27 
   28 #include <pcap.h>
   29 #include <string.h>
   30 
   31   /* \Device\NPF{...} and description are huge. There should be 2 buffers
   32    * for this; one for dev-name and 1 for description. Note: dev->description
   33    * on WinPcap can contain <tab> and newlines!
   34    */
   35 #define IFACE_LEN  100
   36 
   37 /* globals */
   38 
   39 GtkApplication *etterapp = NULL;
   40 GtkWidget *window = NULL;   /* main window */
   41 GtkWidget *notebook = NULL;
   42 GtkWidget *main_menu = NULL;
   43 GtkUIManager *menu_manager = NULL;
   44 guint merge_id;
   45 GTimer *progress_timer = NULL;
   46 
   47 GtkWidget     *notebook_frame = NULL;
   48 GtkWidget     *textview = NULL;
   49 GtkWidget     *infobar = NULL;
   50 GtkWidget     *infolabel = NULL;
   51 GtkWidget     *infoframe = NULL;
   52 static guint   infotimer = 0;
   53 GtkTextBuffer *msgbuffer = NULL;
   54 GtkTextMark   *endmark = NULL;
   55 static gboolean       progress_canceled = FALSE;
   56 static GtkWidget     *progress_dialog = NULL;
   57 static GtkWidget     *progress_bar = NULL;
   58 
   59 
   60 /* proto */
   61 
   62 void gtkui_start(void);
   63 
   64 static void gtkui_init(void);
   65 static void gtkui_cleanup(void);
   66 static void gtkui_update(int target);
   67 static void gtkui_msg(const char *msg);
   68 gboolean gtkui_infobar_expired(gpointer data);
   69 static void gtkui_error(const char *msg);
   70 static void gtkui_fatal_error(const char *msg);
   71 static gboolean gtkui_flush_msg(gpointer data);
   72 static void gtkui_progress(char *title, int value, int max);
   73 
   74 GtkApplication* gtkui_setup(void * activate_func, gpointer activate_param);
   75 static void gtkui_build_widgets(GApplication* app, gpointer data);
   76 
   77 static void toggle_unoffensive(GSimpleAction *action, GVariant *value, gpointer data);
   78 static void toggle_nopromisc(GSimpleAction *action, GVariant *value, gpointer data);
   79 
   80 static void gtkui_file_open(GSimpleAction *action, GVariant *value, gpointer data);
   81 static void read_pcapfile(gchar *file);
   82 static void gtkui_file_write(GSimpleAction *action, GVariant *value, gpointer data);
   83 static void write_pcapfile(void);
   84 static void gtkui_set_iface_unified(GtkComboBox *combo, gpointer data);
   85 static void gtkui_set_iface_bridge(GtkComboBox *combo, gpointer data);
   86 static gboolean gtkui_bridged_switch(GtkSwitch *switcher, gboolean state, gpointer data);
   87 static gboolean gtkui_autostart_switch(GtkSwitch *switcher, gboolean state, gpointer data);
   88 static void gtkui_sniff(GtkButton *button, gpointer data);
   89 static void gtkui_pcap_filter(GSimpleAction *action, GVariant *value, gpointer data);
   90 static void gtkui_set_netmask(GSimpleAction *action, GVariant *value, gpointer data);
   91 static gboolean gtkui_progress_cancel(GtkWidget *window, gpointer data);
   92 
   93 #define ENABLED "true"
   94 #define DISABLED "false"
   95 
   96 /* wrapper functions which inject the real function call into the main
   97  * idle loop, ensugin only th emain thread performs GTK operations
   98 */
   99 static gboolean gtkui_cleanup_shim(gpointer data)
  100 {
  101    /* variable not used */
  102    (void) data;
  103 
  104    gtkui_cleanup();
  105    return FALSE;
  106 }
  107 
  108 static void gtkui_cleanup_wrap(void)
  109 {
  110    g_idle_add(gtkui_cleanup_shim, NULL);
  111 }
  112 
  113 static gboolean gtkui_msg_shim(gpointer data)
  114 {
  115    gtkui_msg(data);
  116    SAFE_FREE(data);
  117    return FALSE;
  118 }
  119 
  120 static void gtkui_msg_wrap(const char *msg)
  121 {
  122     char *copy = strdup(msg);
  123     if (msg) {
  124        g_idle_add(gtkui_msg_shim, copy);
  125     } else {
  126        FATAL_ERROR("out of memory");
  127     }
  128 }
  129 
  130 static gboolean gtkui_error_shim(gpointer data)
  131 {
  132    gtkui_error(data);
  133    SAFE_FREE(data);
  134    return FALSE;
  135 }
  136 
  137 static void gtkui_error_wrap(const char *msg)
  138 {
  139 
  140    char *copy = strdup(msg);
  141    if (msg) {
  142       g_idle_add(gtkui_error_shim, copy);
  143    } else {
  144       FATAL_ERROR("out of memory");
  145    }
  146 }
  147 
  148 static gboolean gtkui_fatal_error_shim(gpointer data) {
  149    gtkui_fatal_error(data);
  150    SAFE_FREE(data);
  151    return FALSE;
  152 }
  153 
  154 static void gtkui_fatal_error_wrap(const char *msg) {
  155 
  156    char *copy = strdup(msg);
  157    if (msg) {
  158       gtkui_fatal_error_shim(copy);
  159       //g_idle_add(gtkui_fatal_error_shim, copy);
  160    } else {
  161       FATAL_ERROR("out of memory");
  162    }
  163 }
  164 
  165 struct gtkui_input_data {
  166    char *title;
  167    char *input;
  168    size_t n;
  169    void (*callback)(void);
  170 };
  171 
  172 struct gtkui_progress_data {
  173    char *title;
  174    int value;
  175    int max;
  176 };
  177 
  178 static gboolean gtkui_progress_shim(gpointer data) {
  179 
  180    struct gtkui_progress_data *gpd = data;
  181    gdouble delay; 
  182    gulong usec;
  183 
  184    delay = g_timer_elapsed(progress_timer, &usec);
  185    delay += usec / 1000000;
  186 
  187    /* render progress bar if not canceled or lasting longer than 750 ms */
  188    if (!progress_canceled && delay >= 0.75)
  189       gtkui_progress(gpd->title, gpd->value, gpd->max);
  190    SAFE_FREE(gpd->title);
  191    SAFE_FREE(gpd);
  192    return FALSE;
  193 }
  194 
  195 static int gtkui_progress_wrap(char *title, int value, int max) {
  196 
  197    struct gtkui_progress_data *gpd;
  198 
  199    if (value <= 1) {
  200       g_timer_start(progress_timer);
  201       progress_canceled = FALSE;
  202    }
  203 
  204    if (progress_canceled == TRUE) {
  205       return UI_PROGRESS_INTERRUPTED;
  206    }
  207 
  208    if (!title) {
  209     return UI_PROGRESS_UPDATED;
  210    }
  211 
  212    gpd = malloc(sizeof *gpd);
  213    if (gpd) {
  214       gpd->title = strdup(title);
  215       gpd->value = value;
  216       gpd->max = max;
  217       g_idle_add(gtkui_progress_shim, gpd);
  218    } else {
  219       FATAL_ERROR("out of memory");
  220    }
  221 
  222    return value == max
  223       ? UI_PROGRESS_FINISHED
  224       : UI_PROGRESS_UPDATED;
  225 }
  226 
  227 
  228 
  229 
  230 
  231 /********************************************/
  232 
  233 void set_gtk_interface(void)
  234 {
  235    struct ui_ops ops;
  236 
  237    /* wipe the struct */
  238    memset(&ops, 0, sizeof(ops));
  239 
  240    /* register the functions */
  241    ops.init = &gtkui_init;
  242    ops.start = &gtkui_start;
  243    ops.type = UI_GTK;
  244    ops.cleanup = &gtkui_cleanup_wrap;
  245    ops.msg = &gtkui_msg_wrap;
  246    ops.error = &gtkui_error_wrap;
  247    ops.fatal_error = &gtkui_fatal_error_wrap;
  248    ops.input = &gtkui_input;
  249    ops.progress = &gtkui_progress_wrap;
  250    ops.update = &gtkui_update;
  251 
  252    ui_register(&ops);
  253 
  254    DEBUG_MSG("GTK3 -> gtk+3 %d.%d.%d\n", gtk_major_version, gtk_minor_version, gtk_micro_version);
  255 }
  256 
  257 
  258 /*
  259  * prepare GTK, create the menu/messages window, enter the first loop 
  260  */
  261 static void gtkui_init(void)
  262 {
  263    DEBUG_MSG("gtkui_init");
  264 
  265    if(!gtk_init_check(0, NULL)) {
  266     FATAL_ERROR("GTK3 failed to initialize. Is X running?");
  267        return;
  268    }
  269 
  270    gtkui_conf_read();
  271 
  272    /* try to explicitely enforce dark theme if preferred */
  273    if (EC_GBL_CONF->gtkui_prefer_dark_theme)
  274       g_object_set(gtk_settings_get_default(), 
  275             "gtk-application-prefer-dark-theme", TRUE,
  276             NULL);
  277 
  278    etterapp = gtkui_setup(gtkui_build_widgets, NULL);
  279 
  280    /* initialize timer */
  281    progress_timer = g_timer_new();
  282 
  283    /* gui init loop, calling gtkui_sniff (--> g_application_quit) will cause
  284     * this to exit so we can proceed to the main loop
  285     * later. */
  286    g_application_run(G_APPLICATION(etterapp), 0, NULL);
  287    g_object_unref(G_OBJECT(etterapp));
  288 
  289    EC_GBL_UI->initialized = 1;
  290 }
  291 
  292 /*
  293  * exit ettercap 
  294  */
  295 void gtkui_exit(GSimpleAction *action, GVariant *value, gpointer data)
  296 {
  297    int left, top, width, height;
  298 
  299    (void) action;
  300    (void) value;
  301    (void) data;
  302    DEBUG_MSG("gtkui_exit");
  303 
  304    g_timer_destroy(progress_timer);
  305 
  306    gtk_window_get_position(GTK_WINDOW (window), &left, &top);
  307    gtk_window_get_size(GTK_WINDOW (window), &width, &height);
  308    gtkui_conf_set("window_left", left);
  309    gtkui_conf_set("window_top", top);
  310    gtkui_conf_set("window_width", width);
  311    gtkui_conf_set("window_height", height);
  312  
  313    g_object_unref(etterapp);
  314    gtkui_conf_save();
  315    clean_exit(0);
  316 }
  317 
  318 /*
  319  * reset to the previous state
  320  */
  321 static void gtkui_cleanup(void)
  322 {
  323    DEBUG_MSG("gtk_cleanup");
  324 
  325    
  326 }
  327 
  328 
  329 /*
  330  * process an UI update notification
  331  */
  332 static void gtkui_update(int target)
  333 {
  334     switch (target) {
  335         case UI_UPDATE_HOSTLIST:
  336             g_idle_add((GSourceFunc)gtkui_refresh_host_list, NULL);
  337             break;
  338         case UI_UPDATE_PLUGINLIST:
  339             g_idle_add((GSourceFunc)gtkui_refresh_plugin_list, NULL);
  340             break;
  341     }
  342 
  343 }
  344 
  345 /*
  346  * print a USER_MSG() extracting it from the queue
  347  */
  348 static void gtkui_msg(const char *msg)
  349 {
  350    GtkTextIter iter;
  351    gchar *unicode = NULL;
  352 
  353    DEBUG_MSG("gtkui_msg: %s", msg);
  354 
  355    if((unicode = gtkui_utf8_validate((char *)msg)) == NULL)
  356          return;
  357 
  358    gtk_text_buffer_get_end_iter(msgbuffer, &iter);
  359    gtk_text_buffer_insert(msgbuffer, &iter, unicode, -1);
  360    gtk_text_view_scroll_to_mark(GTK_TEXT_VIEW (textview), 
  361                                 endmark, 0, FALSE, 0, 0);
  362    return;
  363 }
  364 
  365 /* flush pending messages */
  366 gboolean gtkui_flush_msg(gpointer data)
  367 {
  368    /* variable not used */
  369    (void) data;
  370 
  371    ui_msg_flush(MSG_ALL);
  372 
  373    return(TRUE);
  374 }
  375 
  376 /*
  377  * display about dialog
  378  */
  379 void gtkui_about(GSimpleAction *action, GVariant *value, gpointer data)
  380 {
  381    GtkWidget *dialog, *content, *scroll, *vbox, *logo, *label;
  382    GtkWidget *textview, *header, *stack, *stackswitch;
  383    GtkTextBuffer *textbuf;
  384    GtkTextIter iter;
  385    GError *error = NULL;
  386    const gchar *path, *unicode;
  387    gchar *license, *authors;
  388    gsize length;
  389    
  390    (void) action;
  391    (void) value;
  392    (void) data;
  393 
  394    header = gtk_header_bar_new();
  395    gtk_header_bar_set_title(GTK_HEADER_BAR(header), "About");
  396    gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(header), TRUE);
  397    gtk_header_bar_set_decoration_layout(GTK_HEADER_BAR(header), ":close");
  398 
  399    dialog = gtk_dialog_new();
  400    gtk_window_set_title(GTK_WINDOW(dialog), "About");
  401    gtk_window_set_titlebar(GTK_WINDOW(dialog), header);
  402    gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
  403    gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(window));
  404    gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
  405    gtk_window_set_default_size(GTK_WINDOW(dialog), 450, 300);
  406 
  407    stack = gtk_stack_new();
  408    gtk_stack_set_transition_type(GTK_STACK(stack), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT);
  409    stackswitch = gtk_stack_switcher_new();
  410    gtk_stack_switcher_set_stack(GTK_STACK_SWITCHER(stackswitch), GTK_STACK(stack));
  411    gtk_header_bar_set_custom_title(GTK_HEADER_BAR(header), stackswitch);
  412 
  413 
  414    /* General page */
  415    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
  416 
  417    path = INSTALL_DATADIR "/" PROGRAM "/" LOGO_FILE_SMALL;
  418    if(g_file_test(path, G_FILE_TEST_EXISTS))
  419       logo = gtk_image_new_from_file(path);
  420    else /* if neither path is valid gtk will use a broken image icon */
  421       logo = gtk_image_new_from_file("./share/" LOGO_FILE_SMALL);
  422    gtk_box_pack_start(GTK_BOX(vbox), logo, TRUE, TRUE, 0);
  423 
  424    label = gtk_label_new("");
  425    gtk_label_set_markup(GTK_LABEL(label), 
  426          "<span size=\"xx-large\" weight=\"bold\">" 
  427          PROGRAM " " EC_VERSION 
  428          "</span>");
  429    gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
  430 
  431    label = gtk_label_new("www.ettercap-project.org");
  432    gtk_label_set_selectable(GTK_LABEL(label), TRUE);
  433    gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
  434    label = gtk_label_new("#ettercap on FreeNode IRC");
  435    gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
  436    label = gtk_label_new(" ");
  437    gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 30);
  438    gtk_stack_add_titled(GTK_STACK(stack), vbox, "general", "General");
  439 
  440    /* Authors page */
  441    scroll= gtk_scrolled_window_new(NULL, NULL); 
  442    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  443    gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN);
  444 
  445    /* load the authors file */
  446    g_file_get_contents("./AUTHORS",
  447          &authors, &length, &error);
  448    if (error != NULL) {
  449       /* no debug message */
  450       g_error_free(error);
  451       error = NULL;
  452 
  453       /* 2nd try */
  454       g_file_get_contents(INSTALL_DATADIR "/" PROGRAM "/AUTHORS",
  455             &authors, &length, &error);
  456       if (error != NULL) {
  457          DEBUG_MSG("failed to load authors file: %s", error->message);
  458          gtkui_error("Failed to load AUTHORS file.");
  459          g_error_free(error);
  460          error = NULL;
  461       }
  462    }
  463    textview = gtk_text_view_new();
  464    gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
  465    textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
  466    if (authors && (unicode = gtkui_utf8_validate(authors)) != NULL) {
  467       gtk_text_buffer_get_end_iter(textbuf, &iter);
  468       gtk_text_buffer_insert(textbuf, &iter, unicode, -1);
  469    }
  470    gtk_container_add(GTK_CONTAINER(scroll), textview);
  471    gtk_stack_add_titled(GTK_STACK(stack), scroll, "authors", "Authors");
  472 
  473    /* License page */
  474    scroll= gtk_scrolled_window_new(NULL, NULL); 
  475    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scroll), 
  476          GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  477    gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN);
  478 
  479    /* load license file */
  480    g_file_get_contents("./LICENSE",
  481          &license, &length, &error);
  482    if (error != NULL) {
  483       /* no debug message */
  484       g_error_free(error);
  485       error = NULL;
  486 
  487       /* 2nd try */
  488       g_file_get_contents(INSTALL_DATADIR "/" PROGRAM "/LICENSE",
  489             &license, &length, &error);
  490 #ifndef OS_WINDOWS
  491       if (error != NULL) {
  492          DEBUG_MSG("failed to load license file: %s, try system path ...", error->message);
  493          g_error_free(error);
  494          error = NULL;
  495 
  496          /* 3rd try */
  497          g_file_get_contents("/usr/share/common-licenses/GPL-2",
  498                &license, &length, &error);
  499          }
  500 #endif
  501          if (error != NULL) {
  502             DEBUG_MSG("failed to load license file: %s", error->message);
  503             gtkui_error("Failed to load LICENSE file.");
  504             g_error_free(error);
  505             error = NULL;
  506       }
  507    }
  508 
  509    textview = gtk_text_view_new();
  510    gtk_text_view_set_editable(GTK_TEXT_VIEW(textview), FALSE);
  511    textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview));
  512    if (license && (unicode = gtkui_utf8_validate(license)) != NULL) {
  513       gtk_text_buffer_get_end_iter(textbuf, &iter);
  514       gtk_text_buffer_insert(textbuf, &iter, unicode, -1);
  515    }
  516    gtk_container_add(GTK_CONTAINER(scroll), textview);
  517 
  518    gtk_stack_add_titled(GTK_STACK(stack), scroll, "license", "License");
  519 
  520    content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
  521    gtk_container_add(GTK_CONTAINER(content), stack);
  522 
  523    // TODO Ctrl+w shall close the window
  524 
  525    gtk_widget_show_all(GTK_WIDGET(dialog));
  526 
  527 
  528    gtk_dialog_run(GTK_DIALOG(dialog));
  529 
  530    if (authors)
  531       g_free(authors);
  532    if (license)
  533       g_free(license);
  534 
  535    gtk_widget_destroy(dialog);
  536 
  537 }
  538 
  539 /*
  540  * reimplementation of gtk_message_dialog_new() to display a message 
  541  * dialog with a header-bar since the GTK implementation has hardcoded 
  542  * disabled this feature in the convenience function gtk_message_dialog_new()
  543  * and gtk_message_dialog_new() is also not meant anymore to display
  544  * images or icons indicating the type of message
  545  */
  546 GtkWidget* gtkui_message_dialog(GtkWindow *parent, GtkDialogFlags flags, 
  547                                 GtkMessageType type, GtkButtonsType buttons, 
  548                                 const char *msg)
  549 {
  550    GtkWidget *dialog, *label, *icon, *button, *content, *box, *header;
  551 
  552    dialog = gtk_dialog_new();
  553 
  554 
  555    /* implement flags */
  556    if (parent)
  557       gtk_window_set_transient_for(GTK_WINDOW(dialog), parent);
  558 
  559    if (flags & GTK_DIALOG_MODAL)
  560       gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
  561 
  562    if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
  563       gtk_window_set_destroy_with_parent(GTK_WINDOW(dialog), TRUE);
  564 
  565    if (flags & GTK_DIALOG_USE_HEADER_BAR) {
  566       header = gtk_header_bar_new();
  567       gtk_header_bar_set_decoration_layout(GTK_HEADER_BAR(header), ":close");
  568       gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(header), TRUE);
  569       gtk_window_set_titlebar(GTK_WINDOW(dialog), header);
  570       gtk_widget_show(header);
  571    }
  572 
  573 
  574    /* buttons */
  575    switch (buttons) {
  576       case GTK_BUTTONS_OK:
  577          button = gtk_dialog_add_button(GTK_DIALOG(dialog), 
  578                "_OK", GTK_RESPONSE_OK);
  579          gtk_widget_grab_default(button);
  580          break;
  581 
  582       case GTK_BUTTONS_CLOSE:
  583          button = gtk_dialog_add_button(GTK_DIALOG(dialog),
  584                "_Close", GTK_RESPONSE_CLOSE);
  585          gtk_widget_grab_default(button);
  586          break;
  587 
  588       case GTK_BUTTONS_CANCEL:
  589          button = gtk_dialog_add_button(GTK_DIALOG(dialog),
  590                "_Cancel", GTK_RESPONSE_CANCEL);
  591          gtk_widget_grab_default(button);
  592          break;
  593 
  594       case GTK_BUTTONS_YES_NO:
  595          button = gtk_dialog_add_button(GTK_DIALOG(dialog),
  596                "_Yes", GTK_RESPONSE_YES);
  597          gtk_widget_grab_default(button);
  598 
  599          button = gtk_dialog_add_button(GTK_DIALOG(dialog),
  600                "_No", GTK_RESPONSE_NO);
  601          break;
  602 
  603       case GTK_BUTTONS_OK_CANCEL:
  604          button = gtk_dialog_add_button(GTK_DIALOG(dialog),
  605                "_OK", GTK_RESPONSE_OK);
  606          gtk_widget_grab_default(button);
  607 
  608          button = gtk_dialog_add_button(GTK_DIALOG(dialog),
  609                "_Cancel", GTK_RESPONSE_CANCEL);
  610          break;
  611 
  612       default: // GTK_BUTTONS_NONE
  613          break;
  614    }
  615 
  616    /* create horizontal box for icon and message text */
  617    box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
  618    content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
  619    gtk_container_set_border_width(GTK_CONTAINER(content), 10);
  620    gtk_container_add(GTK_CONTAINER(content), box);
  621 
  622    /* icon depending on message type */
  623    switch (type) {
  624       case GTK_MESSAGE_INFO:
  625          gtk_window_set_title(GTK_WINDOW(dialog), "Information");
  626          icon = gtk_image_new_from_icon_name("dialog-information", GTK_ICON_SIZE_DIALOG);
  627          gtk_box_pack_start(GTK_BOX(box), icon, FALSE, FALSE, 0);
  628          break;
  629       case GTK_MESSAGE_WARNING:
  630          gtk_window_set_title(GTK_WINDOW(dialog), "Warning");
  631          icon = gtk_image_new_from_icon_name("dialog-warning", GTK_ICON_SIZE_DIALOG);
  632          gtk_box_pack_start(GTK_BOX(box), icon, FALSE, FALSE, 0);
  633          break;
  634       case GTK_MESSAGE_QUESTION:
  635          gtk_window_set_title(GTK_WINDOW(dialog), "Question");
  636          icon = gtk_image_new_from_icon_name("dialog-question", GTK_ICON_SIZE_DIALOG);
  637          gtk_box_pack_start(GTK_BOX(box), icon, FALSE, FALSE, 0);
  638          break;
  639       case GTK_MESSAGE_ERROR:
  640          gtk_window_set_title(GTK_WINDOW(dialog), "Error");
  641          icon = gtk_image_new_from_icon_name("dialog-error", GTK_ICON_SIZE_DIALOG);
  642          gtk_box_pack_start(GTK_BOX(box), icon, FALSE, FALSE, 0);
  643          break;
  644       default: // GTK_MESSAGE_OTHER
  645          break;
  646    }
  647 
  648    /* message text */
  649    label = gtk_label_new(msg);
  650    gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);
  651 
  652    gtk_widget_show_all(box);
  653 
  654    return dialog;
  655 }
  656 
  657 /* 
  658  * some minor notifications don't need a dedicated dialog
  659  * instead an the infobar widget is being used
  660  */
  661 GtkWidget* gtkui_infobar_new(GtkWidget *infoframe)
  662 {
  663    infobar = gtk_info_bar_new();
  664    gtk_widget_set_no_show_all(infobar, TRUE);
  665 
  666    infolabel = gtk_label_new("");
  667    gtk_widget_show(infolabel);
  668    
  669    gtk_container_add(GTK_CONTAINER(
  670             gtk_info_bar_get_content_area(GTK_INFO_BAR(infobar))), infolabel);
  671 
  672    gtk_info_bar_add_button(GTK_INFO_BAR(infobar), "_OK", GTK_RESPONSE_OK);
  673 
  674    if (infoframe == NULL)
  675       infoframe = gtk_frame_new(NULL);
  676    gtk_widget_set_no_show_all(infoframe, TRUE);
  677    gtk_frame_set_shadow_type(GTK_FRAME(infoframe), GTK_SHADOW_NONE);
  678    gtk_container_add(GTK_CONTAINER(infoframe), infobar);
  679    g_signal_connect(G_OBJECT(infobar), "response", 
  680          G_CALLBACK(gtkui_infobar_hide), NULL);
  681 
  682    return infoframe;
  683 }
  684 
  685 /*
  686  * show infobar
  687  */
  688 void gtkui_infobar_show(GtkMessageType type, const gchar *msg)
  689 {
  690 
  691    if (!infobar && !infoframe)
  692       return;
  693 
  694    if (infobar == NULL)
  695       infoframe = gtkui_infobar_new(infoframe);
  696 
  697    gtk_label_set_text(GTK_LABEL(infolabel), msg);
  698    gtk_info_bar_set_message_type(GTK_INFO_BAR(infobar), type);
  699    gtk_info_bar_set_default_response(GTK_INFO_BAR(infobar), GTK_RESPONSE_OK);
  700 
  701    gtk_widget_show(infobar);
  702    gtk_widget_show(infoframe);
  703 
  704    infotimer = g_timeout_add_seconds(3, gtkui_infobar_expired, infobar);
  705 
  706 }
  707 
  708 /*
  709  * callback when info bar timer expired
  710  */
  711 gboolean gtkui_infobar_expired(gpointer data)
  712 {
  713    gtkui_infobar_hide(GTK_WIDGET(data), 0, NULL);
  714    /* stop timer */
  715    return FALSE;
  716 }
  717    
  718 
  719 /*
  720  * callback wrapper to hide infobar necessary due to still (Feb 2018) 
  721  * unfixed animation bug: https://bugzilla.gnome.org/show_bug.cgi?id=710888
  722  * implementing suggested workaround to remove and reattach widget
  723  */
  724 void gtkui_infobar_hide(GtkWidget *widget, gint response, gpointer data)
  725 {
  726    (void) response;
  727    (void) data;
  728    (void) widget;
  729 
  730 
  731    if (!infobar || !infoframe)
  732       return;
  733 
  734    if (infotimer)
  735       g_source_remove(infotimer);
  736 
  737    gtk_widget_hide(infobar);
  738    gtk_widget_hide(infoframe);
  739    gtk_widget_destroy(infobar);
  740    infobar = NULL;
  741 }
  742 
  743 /*
  744  * print an error
  745  */
  746 static void gtkui_error(const char *msg)
  747 {
  748    gchar *unicode = NULL;
  749    
  750    DEBUG_MSG("gtkui_error: %s", msg);
  751 
  752    if((unicode = gtkui_utf8_validate((char *)msg)) == NULL)
  753             return;
  754 
  755    gtkui_infobar_show(GTK_MESSAGE_ERROR, msg);
  756 
  757    return;
  758 }
  759 
  760 
  761 /*
  762  * handle a fatal error and exit
  763  */
  764 static void gtkui_fatal_error(const char *msg)
  765 {
  766    /* if the gui is working at this point
  767       display the message in a dialog */
  768    if(window)
  769       gtkui_error(msg);
  770 
  771    /* also dump it to console in case ettercap was started in an xterm */
  772    fprintf(stderr, "FATAL ERROR: %s\n\n\n", msg);
  773 
  774    clean_exit(-1);
  775 }
  776 
  777 
  778 /*
  779  * get an input from the user
  780  */
  781 void gtkui_input(const char *title, char *input, size_t n, void (*callback)(void))
  782 {
  783    GtkWidget *dialog, *entry, *label, *hbox, *vbox, *image, *content_area;
  784 
  785    dialog = gtk_dialog_new_with_buttons(PROGRAM" Input", GTK_WINDOW (window),
  786                                         GTK_DIALOG_MODAL|GTK_DIALOG_USE_HEADER_BAR, 
  787                                         "_Cancel", GTK_RESPONSE_CANCEL, 
  788                                         "_OK",     GTK_RESPONSE_OK,
  789                                         NULL);
  790    gtk_container_set_border_width(GTK_CONTAINER (dialog), 5);
  791 
  792    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
  793 
  794    content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
  795    gtk_container_add(GTK_CONTAINER(content_area), hbox);
  796    
  797    image = gtk_image_new_from_icon_name("dialog-question", GTK_ICON_SIZE_DIALOG);
  798    gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
  799    
  800    label = gtk_label_new (title);
  801    gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
  802    gtk_label_set_selectable (GTK_LABEL (label), TRUE);
  803    gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
  804    
  805    entry = gtk_entry_new();
  806    gtk_entry_set_max_length(GTK_ENTRY(entry), n);
  807    g_object_set_data(G_OBJECT (entry), "dialog", dialog);
  808    g_signal_connect(G_OBJECT (entry), "activate", G_CALLBACK (gtkui_dialog_enter), NULL);
  809 
  810    
  811    if (input)
  812       gtk_entry_set_text(GTK_ENTRY (entry), input); 
  813 
  814    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
  815    gtk_box_pack_start(GTK_BOX(vbox), entry, TRUE, FALSE, 0);
  816    
  817    gtk_box_pack_start(GTK_BOX (hbox), vbox, FALSE, FALSE, 5);
  818    gtk_widget_show_all (hbox);
  819 
  820    if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK) {
  821 
  822       strncpy(input, gtk_entry_get_text(GTK_ENTRY (entry)), n);
  823 
  824       if (callback != NULL) {
  825          gtk_widget_destroy(dialog);
  826 
  827          callback();
  828          return;
  829       }
  830    }
  831    gtk_widget_destroy(dialog);
  832 }
  833 
  834 
  835 /* 
  836  * show or update the progress bar
  837  */
  838 static void gtkui_progress(char *title, int value, int max)
  839 {
  840    static GtkWidget *hbox, *header, *content;
  841 
  842    /* the first time, create the object */
  843    if (progress_bar == NULL) {
  844       header = gtk_header_bar_new();
  845       gtk_header_bar_set_title(GTK_HEADER_BAR(header), "Progress");
  846       gtk_header_bar_set_decoration_layout(GTK_HEADER_BAR(header), ":close");
  847       gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(header), TRUE);
  848 
  849       progress_dialog = gtk_dialog_new();
  850       gtk_window_set_title(GTK_WINDOW (progress_dialog), PROGRAM);
  851       gtk_window_set_titlebar(GTK_WINDOW(progress_dialog), header);
  852       gtk_window_set_modal(GTK_WINDOW (progress_dialog), TRUE);
  853       gtk_window_set_transient_for(GTK_WINDOW(progress_dialog), GTK_WINDOW(window));
  854       gtk_window_set_position(GTK_WINDOW(progress_dialog), GTK_WIN_POS_CENTER_ON_PARENT);
  855       gtk_container_set_border_width(GTK_CONTAINER (progress_dialog), 10);
  856       g_signal_connect(G_OBJECT(progress_dialog), "delete_event", G_CALLBACK(gtkui_progress_cancel), NULL);
  857 
  858       hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
  859       content = gtk_dialog_get_content_area(GTK_DIALOG(progress_dialog));
  860       gtk_container_add(GTK_CONTAINER(content), hbox);
  861     
  862       progress_bar = gtk_progress_bar_new();
  863       gtk_progress_bar_set_show_text(GTK_PROGRESS_BAR(progress_bar), TRUE);
  864       gtk_box_pack_start(GTK_BOX(hbox), progress_bar, TRUE, TRUE, 20);
  865 
  866    } 
  867    
  868    /* the subsequent calls have to only update the object */
  869    gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress_bar), title);
  870    gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), (gdouble)((gdouble)value / (gdouble)max));
  871 
  872    /* update dialog window */
  873    gtk_widget_show_all(progress_dialog);
  874 
  875    /* 
  876     * when 100%, destroy it
  877     */
  878    if (value == max) {
  879       if (progress_dialog)
  880          gtk_widget_destroy(progress_dialog);
  881       progress_dialog = NULL;
  882       progress_bar = NULL;
  883    }
  884 
  885 }
  886 
  887 static gboolean gtkui_progress_cancel(GtkWidget *window, gpointer data) 
  888 {
  889    /* variable not used */
  890    (void) window;
  891 
  892    progress_canceled = TRUE;
  893 
  894    /* the progress dialog must be manually destroyed if the cancel button is used */
  895    if (data != NULL && GTK_IS_WIDGET(data)) {
  896       gtk_widget_destroy(data);
  897       progress_dialog = NULL;
  898       progress_bar = NULL;
  899    }
  900    return(FALSE);
  901 }
  902 
  903 /*
  904  * print a message
  905  */
  906 void gtkui_message(const char *msg)
  907 {
  908    DEBUG_MSG("gtkui_message: %s", msg);
  909    gtkui_infobar_show(GTK_MESSAGE_INFO, msg);
  910 }
  911 
  912 
  913 /*
  914  * Create the main interface and enter the second loop
  915  */
  916 
  917 void gtkui_start(void)
  918 {
  919    guint idle_flush;
  920    gint online;
  921 
  922    DEBUG_MSG("gtkui_start");
  923 
  924    idle_flush = g_timeout_add(500, gtkui_flush_msg, NULL);
  925 
  926    /* which interface do we have to display ? */
  927    online = (EC_GBL_OPTIONS->read ? 0 : 1);
  928 
  929    /* create second instance of the UI application */
  930    etterapp = gtkui_setup(gtkui_create_menu, GINT_TO_POINTER(online));
  931 
  932    /* start plugins defined on CLI */
  933    g_idle_add(gtkui_plugins_autostart, NULL);
  934    
  935    /* the main gui loop, once this exits the gui will be destroyed */
  936    g_application_run(G_APPLICATION(etterapp), 0, NULL);
  937    g_object_unref(G_OBJECT(etterapp));
  938 
  939    g_source_remove(idle_flush);
  940 }
  941 
  942 static void toggle_unoffensive(GSimpleAction *action, GVariant *value, gpointer data)
  943 {
  944    (void) data;
  945 
  946    g_simple_action_set_state(action, value); 
  947 
  948    EC_GBL_OPTIONS->unoffensive ^= 1;
  949 }
  950 
  951 static void toggle_nopromisc(GSimpleAction *action, GVariant *value, gpointer data)
  952 {
  953    (void) data;
  954 
  955    g_simple_action_set_state(action, value); 
  956 
  957    EC_GBL_PCAP->promisc ^= 1;
  958 }
  959 
  960 /*
  961  * display the initial menu to setup global options
  962  * at startup.
  963  */
  964 GtkApplication* gtkui_setup(void * activate_func, gpointer data)
  965 {
  966    GtkApplication *app;
  967    DEBUG_MSG("gtkui_setup");
  968 
  969    app = gtk_application_new("org.gtk.Ettercap", 0);
  970    g_signal_connect(app, "activate", G_CALLBACK(activate_func), data);
  971 
  972    return app;
  973 }
  974 
  975 /*
  976  * activate callback for GtkApplication for building the widgets 
  977  * for the setup dialog
  978  */
  979 static void gtkui_build_widgets(GApplication* app, gpointer data)
  980 {
  981    GtkWidget *header, *menubutton, *logo, *switcher;
  982    GtkWidget *layout, *label, *combo1, *combo2, *setting_frame, *grid, *box;
  983    GtkBuilder *builder;
  984    GtkListStore *iface_list;
  985    GtkTreeIter iter;
  986    GtkCellRenderer *cell1, *cell2;
  987    gint width, height, left, top;
  988    gchar *title = NULL;
  989    char *path = NULL, *markup = NULL;
  990    guint i;
  991    pcap_if_t *dev;
  992 
  993    (void) data;
  994 
  995    /* accelerators */
  996    static gtkui_accel_map_t accels[] = {
  997       {"app.pcap_filter", {"<Primary>p", NULL}},
  998       {"app.set_netmask", {"<Primary>n", NULL}},
  999       {"app.open", {"<Primary>o", NULL}},
 1000       {"app.save", {"<Primary>s", NULL}},
 1001 #ifndef OS_WINDOWS
 1002       {"app.help", {"F1", NULL}},
 1003 #endif
 1004       {"app.quit", {"<Primary>q", "<Primary>x", NULL}}
 1005    };
 1006 
 1007 
 1008    /* actions */
 1009    static GActionEntry action_entries[] = {
 1010       {"set_promisc",      NULL,  NULL, ENABLED,  toggle_nopromisc, {}},
 1011       {"set_unoffensive",  NULL,  NULL, DISABLED, toggle_unoffensive, {}},
 1012       {"open",  gtkui_file_open, NULL, NULL, NULL, {}},
 1013       {"save",  gtkui_file_write, NULL, NULL, NULL, {}},
 1014       {"about", gtkui_about, NULL, NULL, NULL, {}},
 1015       {"shortcuts", gtkui_show_shortcuts, "s", NULL, NULL, {}},
 1016 #ifndef OS_WINDOWS
 1017       {"help",        gtkui_help, NULL, NULL, NULL, {}},
 1018 #endif
 1019       {"quit",  gtkui_exit, NULL, NULL, NULL, {}},
 1020       {"pcap_filter", gtkui_pcap_filter,   NULL, NULL, NULL, {}},
 1021       {"set_netmask", gtkui_set_netmask,   NULL, NULL, NULL, {}}
 1022    };
 1023 
 1024 
 1025 
 1026    DEBUG_MSG("gtkui_build_widgets (activate method)");
 1027 
 1028    /* honor CLI options */
 1029    if(!EC_GBL_PCAP->promisc)
 1030       /* setting the menu item active will toggle this setting */
 1031       /* it will be TRUE after the menu is updated */
 1032       action_entries[0].state = DISABLED;
 1033 
 1034    if(EC_GBL_OPTIONS->unoffensive)
 1035       action_entries[1].state = ENABLED;
 1036 
 1037 
 1038    /* add actions to the application */
 1039    g_action_map_add_action_entries(G_ACTION_MAP(app), action_entries, 
 1040          G_N_ELEMENTS(action_entries), app);
 1041 
 1042    /* map accelerators to actions */
 1043    for (i = 0; i < G_N_ELEMENTS(accels); i++) {
 1044       gtk_application_set_accels_for_action(GTK_APPLICATION(app), 
 1045             accels[i].action, accels[i].accel);
 1046    }
 1047 
 1048    /* menu structures */
 1049    builder = gtk_builder_new();
 1050    gtk_builder_add_from_string(builder,
 1051          "<interface>"
 1052          "  <menu id='app-menu'>"
 1053          "    <section>"
 1054          "      <item>"
 1055          "        <attribute name='label' translatable='yes'>_Open PCAP</attribute>"
 1056          "        <attribute name='action'>app.open</attribute>"
 1057          "        <attribute name='icon'>document-open</attribute>"
 1058          "      </item>"
 1059          "      <item>"
 1060          "        <attribute name='label' translatable='yes'>_Save PCAP</attribute>"
 1061          "        <attribute name='action'>app.save</attribute>"
 1062          "        <attribute name='icon'>document-save</attribute>"
 1063          "      </item>"
 1064          "    </section>"
 1065          "    <section>"
 1066 #ifndef OS_WINDOWS
 1067          "      <item>"
 1068          "        <attribute name='label' translatable='yes'>Help</attribute>"
 1069          "        <attribute name='action'>app.help</attribute>"
 1070          "        <attribute name='icon'>help-browser</attribute>"
 1071          "      </item>"
 1072 #endif
 1073          "      <item>"
 1074          "        <attribute name='label' translatable='yes'>Shortcuts</attribute>"
 1075          "        <attribute name='action'>app.shortcuts</attribute>"
 1076          "        <attribute name='target'>setup-shortcuts</attribute>"
 1077          "      </item>"
 1078          "      <item>"
 1079          "        <attribute name='label' translatable='yes'>_About Ettercap</attribute>"
 1080          "        <attribute name='action'>app.about</attribute>"
 1081          "        <attribute name='icon'>help-about</attribute>"
 1082          "      </item>"
 1083          "    </section>"
 1084          "    <section>"
 1085          "      <item>"
 1086          "        <attribute name='label' translatable='yes'>_Quit</attribute>"
 1087          "        <attribute name='action'>app.quit</attribute>"
 1088          "        <attribute name='icon'>application-exit</attribute>"
 1089          "      </item>"
 1090          "    </section>"
 1091          "  </menu>"
 1092          "  <menu id='options-menu'>"
 1093          "    <section>"
 1094          "    <attribute name='label' translatable='yes'>Options</attribute>"
 1095          "      <item>"
 1096          "        <attribute name='label' translatable='yes'>Unoffensive</attribute>"
 1097          "        <attribute name='action'>app.set_unoffensive</attribute>"
 1098          "      </item>"
 1099          "      <item>"
 1100          "        <attribute name='label' translatable='yes'>Promisc mode</attribute>"
 1101          "        <attribute name='action'>app.set_promisc</attribute>"
 1102          "      </item>"
 1103          "      <item>"
 1104          "        <attribute name='label' translatable='yes'>Set Netmask</attribute>"
 1105          "        <attribute name='action'>app.set_netmask</attribute>"
 1106          "      </item>"
 1107          "    </section>"
 1108          "  </menu>"
 1109          "</interface>", -1, NULL);
 1110    
 1111 
 1112    /* set app menu */
 1113    gtk_application_set_app_menu(GTK_APPLICATION(app), 
 1114          G_MENU_MODEL(gtk_builder_get_object(builder, "app-menu")));
 1115 
 1116    if (g_getenv("APP_MENU_FALLBACK"))
 1117       g_object_set(gtk_settings_get_default(), "gtk-shell-shows-app-menu", FALSE, NULL);
 1118 
 1119 
 1120    /* position main window */
 1121    width = gtkui_conf_get("window_width");
 1122    height = gtkui_conf_get("window_height");
 1123    left = gtkui_conf_get("window_left");
 1124    top = gtkui_conf_get("window_top");
 1125 
 1126    /* setup window needs minimal size */
 1127    width = width < 800 ? 800 : width;
 1128    height = height < 400 ? 400 : height;
 1129 
 1130    /* Adjust title formatting */
 1131    title = g_strdup(PROGRAM);
 1132    *title = g_ascii_toupper(*title);
 1133 
 1134    /* create main window */
 1135    window = gtk_application_window_new(GTK_APPLICATION(app));
 1136    gtk_application_window_set_show_menubar(GTK_APPLICATION_WINDOW(window), TRUE);
 1137    gtk_window_set_title(GTK_WINDOW(window), title);
 1138    gtk_window_set_default_size(GTK_WINDOW(window), width, height);
 1139 
 1140    /* set window icon */
 1141    path = ICON_DIR "/" ICON_FILE;
 1142    if (g_file_test(path, G_FILE_TEST_EXISTS)) {
 1143       gtk_window_set_icon(GTK_WINDOW(window), gdk_pixbuf_new_from_file(path, NULL));
 1144    }
 1145    else { /* if neither path is valid gtk will use a broken image icon */
 1146       gtk_window_set_icon(GTK_WINDOW(window), gdk_pixbuf_new_from_file("./share/" ICON_FILE, NULL));
 1147    }
 1148 
 1149    if(left > 0 || top > 0)
 1150       gtk_window_move(GTK_WINDOW(window), left, top);
 1151 
 1152    g_signal_connect(G_OBJECT (window), "delete_event", G_CALLBACK(gtkui_exit), NULL);
 1153 
 1154    
 1155    /* create header bar and menu buttons */
 1156    header = gtk_header_bar_new();
 1157    gtk_header_bar_set_title(GTK_HEADER_BAR(header), title);
 1158    gtk_header_bar_set_subtitle(GTK_HEADER_BAR(header), EC_VERSION);
 1159    gtk_header_bar_set_show_close_button(GTK_HEADER_BAR(header), TRUE);
 1160    gtk_window_set_titlebar(GTK_WINDOW(window), header);
 1161 
 1162    menubutton = gtk_menu_button_new();
 1163    gtk_widget_set_tooltip_text(menubutton, "Options");
 1164    gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(menubutton),
 1165          G_MENU_MODEL(gtk_builder_get_object(builder, "options-menu")));
 1166    gtk_button_set_image(GTK_BUTTON(menubutton),
 1167          gtk_image_new_from_icon_name("open-menu-symbolic", GTK_ICON_SIZE_MENU));
 1168    gtk_header_bar_pack_end(GTK_HEADER_BAR(header), menubutton);
 1169 
 1170 
 1171    /* main content area */
 1172    box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
 1173    gtk_container_add(GTK_CONTAINER(window), box);
 1174 
 1175    /* prepare infobar for later notifications */
 1176    infoframe = gtkui_infobar_new(NULL);
 1177    gtk_box_pack_start(GTK_BOX(box), infoframe, FALSE, FALSE, 0);
 1178 
 1179    /* the ettercap logo */
 1180    path = INSTALL_DATADIR "/" PROGRAM "/" LOGO_FILE;
 1181    if(g_file_test(path, G_FILE_TEST_EXISTS))
 1182       logo = gtk_image_new_from_file(path);
 1183    else /* if neither path is valid gtk will use a broken image icon */
 1184       logo = gtk_image_new_from_file("./share/" LOGO_FILE);
 1185 
 1186    /* create overlay to display the logo and overlay the settings widgets */
 1187    layout = gtk_layout_new(NULL, NULL);
 1188    gtk_box_pack_start(GTK_BOX(box), layout, TRUE, TRUE, 0);
 1189    gtk_layout_put(GTK_LAYOUT(layout), logo, 0, 0);
 1190 
 1191    setting_frame = gtk_frame_new(NULL);
 1192    gtk_frame_set_label(GTK_FRAME(setting_frame), "Setup");
 1193    gtk_frame_set_label_align(GTK_FRAME(setting_frame), 0.5, 0.0);
 1194    gtk_frame_set_shadow_type(GTK_FRAME(setting_frame), GTK_SHADOW_ETCHED_OUT);
 1195 
 1196    grid = gtk_grid_new();
 1197    gtk_grid_set_row_spacing(GTK_GRID(grid), 10);
 1198    gtk_grid_set_column_spacing(GTK_GRID(grid), 10);
 1199    g_object_set(grid, "margin", 10, NULL);
 1200    gtk_container_add(GTK_CONTAINER(setting_frame), grid);
 1201 
 1202    label = gtk_label_new(NULL);
 1203    markup = g_markup_printf_escaped(
 1204          "<span style='italic'>%s</span>", 
 1205          "Primary Interface");
 1206    gtk_label_set_markup(GTK_LABEL(label), markup);
 1207    gtk_widget_set_halign(label, GTK_ALIGN_START);
 1208    gtk_grid_attach(GTK_GRID(grid), label, 0, 1, 1, 1);
 1209    g_free(markup);
 1210 
 1211    /* make a list of network interfaces */
 1212    iface_list = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
 1213    for (dev = (pcap_if_t *)EC_GBL_PCAP->ifs; dev != NULL; dev = dev->next) {
 1214       gtk_list_store_append(iface_list, &iter);
 1215       gtk_list_store_set(iface_list, &iter, 
 1216             0, dev->name, 1, dev->description, -1); 
 1217    }
 1218 
 1219    /* make a drop down box for the primary interface and attach the list */
 1220    combo1 = gtk_combo_box_new();
 1221    gtk_combo_box_set_model(GTK_COMBO_BOX(combo1), GTK_TREE_MODEL(iface_list));
 1222    cell1 = gtk_cell_renderer_text_new();
 1223    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo1), cell1, TRUE);
 1224    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo1), cell1, 
 1225          "text", 1, NULL);
 1226    g_signal_connect(G_OBJECT(combo1), "changed", 
 1227          G_CALLBACK(gtkui_set_iface_unified), NULL);
 1228    gtk_combo_box_set_active(GTK_COMBO_BOX(combo1), 0);
 1229    gtk_grid_attach(GTK_GRID(grid), combo1, 1, 1, 1, 1);
 1230 
 1231 
 1232    label = gtk_label_new(NULL);
 1233    markup = g_markup_printf_escaped(
 1234          "<span style='italic'>%s</span>", 
 1235          "Sniffing at startup");
 1236    gtk_label_set_markup(GTK_LABEL(label), markup);
 1237    gtk_widget_set_halign(label, GTK_ALIGN_START);
 1238    gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1);
 1239    g_free(markup);
 1240 
 1241    switcher = gtk_switch_new();
 1242    box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
 1243    gtk_box_set_homogeneous(GTK_BOX(box), FALSE);
 1244    gtk_box_pack_start(GTK_BOX(box), switcher, FALSE, FALSE, 0);
 1245    gtk_grid_attach(GTK_GRID(grid), box, 1, 0, 1, 1);
 1246    if (EC_GBL_CONF->sniffing_at_startup)
 1247       gtk_switch_set_active(GTK_SWITCH(switcher), TRUE);
 1248    g_signal_connect(G_OBJECT(switcher), "state-set",
 1249          G_CALLBACK(gtkui_autostart_switch), NULL);
 1250 
 1251    label = gtk_label_new(NULL);
 1252    markup = g_markup_printf_escaped(
 1253          "<span style='italic'>%s</span>", 
 1254          "Bridged sniffing");
 1255    gtk_label_set_markup(GTK_LABEL(label), markup);
 1256    gtk_widget_set_halign(label, GTK_ALIGN_START);
 1257    gtk_grid_attach(GTK_GRID(grid), label, 0, 2, 1, 1);
 1258    g_free(markup);
 1259 
 1260    switcher = gtk_switch_new();
 1261    box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
 1262    gtk_box_set_homogeneous(GTK_BOX(box), FALSE);
 1263    gtk_box_pack_start(GTK_BOX(box), switcher, FALSE, FALSE, 0);
 1264    gtk_grid_attach(GTK_GRID(grid), box, 1, 2, 1, 1);
 1265 
 1266    label = gtk_label_new(NULL);
 1267    markup = g_markup_printf_escaped(
 1268          "<span style='italic'>%s</span>", 
 1269          "Bridged Interface");
 1270    gtk_label_set_markup(GTK_LABEL(label), markup);
 1271    gtk_widget_set_halign(label, GTK_ALIGN_START);
 1272    gtk_grid_attach(GTK_GRID(grid), label, 0, 3, 1, 1);
 1273 
 1274    /* make a drop down box for the bridge interface and assign the list to it */
 1275    combo2 = gtk_combo_box_new();
 1276    gtk_combo_box_set_model(GTK_COMBO_BOX(combo2), GTK_TREE_MODEL(iface_list));
 1277    cell2 = gtk_cell_renderer_text_new();
 1278    gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo2), cell2, TRUE);
 1279    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo2), cell2, 
 1280          "text", 1, NULL);
 1281    g_signal_connect(G_OBJECT(combo2), "changed", 
 1282          G_CALLBACK(gtkui_set_iface_bridge), NULL);
 1283    gtk_combo_box_set_active(GTK_COMBO_BOX(combo2), 1);
 1284    gtk_grid_attach(GTK_GRID(grid), combo2, 1, 3 , 1, 1);
 1285    gtk_widget_set_sensitive(combo2, FALSE);
 1286 
 1287    /* enable / disable bridged interface combo box */
 1288    g_signal_connect(G_OBJECT(switcher), "state-set", 
 1289          G_CALLBACK(gtkui_bridged_switch), combo2);
 1290 
 1291    gtk_layout_put(GTK_LAYOUT(layout), setting_frame, 450, 10);
 1292 
 1293    menubutton = gtk_button_new();
 1294    gtk_widget_set_tooltip_text(menubutton, "Accept");
 1295    gtk_button_set_image(GTK_BUTTON(menubutton),
 1296          gtk_image_new_from_icon_name("emblem-ok-symbolic", GTK_ICON_SIZE_BUTTON));
 1297    gtk_header_bar_pack_end(GTK_HEADER_BAR(header), menubutton);
 1298    g_signal_connect(G_OBJECT(menubutton), "clicked", G_CALLBACK(gtkui_sniff), switcher);
 1299    
 1300    gtk_widget_show_all(GTK_WIDGET(window));
 1301 
 1302    g_object_unref(iface_list);
 1303    g_object_unref(builder);
 1304    g_free(title);
 1305 
 1306 
 1307    DEBUG_MSG("gtk_setup: end");
 1308 }
 1309 
 1310 /*
 1311  * display the file open dialog
 1312  */
 1313 static void gtkui_file_open(GSimpleAction *action, GVariant *value, gpointer data)
 1314 {
 1315    GtkWidget *dialog, *chooser, *content;
 1316    gchar *filename;
 1317    int response = 0;
 1318 
 1319    (void) action;
 1320    (void) value;
 1321    (void) data;
 1322 
 1323    DEBUG_MSG("gtk_file_open");
 1324 
 1325 
 1326    dialog = gtk_dialog_new_with_buttons("Select a PCAP file for offline sniffing ...", 
 1327          GTK_WINDOW (window), 
 1328          GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_USE_HEADER_BAR,
 1329          "_Cancel", GTK_RESPONSE_CANCEL, 
 1330          "_OK",     GTK_RESPONSE_OK, 
 1331          NULL);
 1332    gtk_container_set_border_width(GTK_CONTAINER(dialog), 10);
 1333 
 1334    content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
 1335    chooser = gtk_file_chooser_widget_new(GTK_FILE_CHOOSER_ACTION_OPEN);
 1336    gtk_container_add(GTK_CONTAINER(content), chooser);
 1337    gtk_widget_show(chooser);
 1338 
 1339    /* This way the file chooser dialog doesn't start in the recent section */
 1340    gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser), "");
 1341 
 1342    response = gtk_dialog_run (GTK_DIALOG (dialog));
 1343 
 1344    if (response == GTK_RESPONSE_OK) {
 1345       gtk_widget_hide(dialog);
 1346       filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser));
 1347       /* destroy needs to come before read_pcapfile so gtk_main_quit
 1348          can reside inside read_pcapfile, which is why destroy is here
 1349          twice and not after the if() block */
 1350       gtk_widget_destroy (dialog);
 1351 
 1352       read_pcapfile (filename);
 1353       g_free(filename);
 1354    } else {
 1355       gtk_widget_destroy (dialog);
 1356    }
 1357 
 1358 }
 1359 
 1360 static void read_pcapfile(gchar *file)
 1361 {
 1362    char pcap_errbuf[PCAP_ERRBUF_SIZE];
 1363    
 1364    DEBUG_MSG("read_pcapfile %s", file);
 1365    
 1366    SAFE_CALLOC(EC_GBL_OPTIONS->pcapfile_in, strlen(file)+1, sizeof(char));
 1367 
 1368    snprintf(EC_GBL_OPTIONS->pcapfile_in, strlen(file)+1, "%s", file);
 1369 
 1370    /* check if the file is good */
 1371    if (is_pcap_file(EC_GBL_OPTIONS->pcapfile_in, pcap_errbuf) != E_SUCCESS) {
 1372       ui_error("%s", pcap_errbuf);
 1373       SAFE_FREE(EC_GBL_OPTIONS->pcapfile_in);
 1374       return;
 1375    }
 1376    
 1377    /* set the options for reading from file */
 1378    EC_GBL_OPTIONS->silent = 1;
 1379    EC_GBL_OPTIONS->unoffensive = 1;
 1380    EC_GBL_OPTIONS->write = 0;
 1381    EC_GBL_OPTIONS->read = 1;
 1382 
 1383    gtk_main_quit();
 1384 }
 1385 
 1386 /*
 1387  * display the write file menu
 1388  */
 1389 static void gtkui_file_write(GSimpleAction *action, GVariant *value, gpointer data)
 1390 {
 1391 #define FILE_LEN  40
 1392    
 1393    GtkWidget *dialog, *content, *chooser;
 1394    gchar *filename;
 1395    int response = 0;
 1396 
 1397    (void) action;
 1398    (void) value;
 1399    (void) data;
 1400 
 1401    DEBUG_MSG("gtk_file_write");
 1402    
 1403    dialog = gtk_dialog_new_with_buttons("Save traffic in a PCAP file ...", 
 1404          GTK_WINDOW (window), 
 1405          GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_USE_HEADER_BAR,
 1406          "_Cancel", GTK_RESPONSE_CANCEL, 
 1407          "_OK",     GTK_RESPONSE_OK, 
 1408          NULL);
 1409    gtk_container_set_border_width(GTK_CONTAINER(dialog), 10);
 1410 
 1411    content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
 1412    chooser = gtk_file_chooser_widget_new(GTK_FILE_CHOOSER_ACTION_SAVE);
 1413    gtk_container_add(GTK_CONTAINER(content), chooser);
 1414    gtk_widget_show(chooser);
 1415 
 1416    /* This way the file chooser dialog doesn't start in the recent section */
 1417    gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser), "");
 1418 
 1419    response = gtk_dialog_run (GTK_DIALOG (dialog));
 1420 
 1421    if (response == GTK_RESPONSE_OK) {
 1422       gtk_widget_hide(dialog);
 1423       filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser));
 1424       /* destroy needs to come before read_pcapfile so gtk_main_quit
 1425          can reside inside read_pcapfile, which is why destroy is here
 1426          twice and not after the if() block */
 1427       gtk_widget_destroy (dialog);
 1428 
 1429       EC_GBL_OPTIONS->pcapfile_out = filename;
 1430       write_pcapfile();
 1431    } else {
 1432       gtk_widget_destroy (dialog);
 1433    }
 1434 }
 1435 
 1436 static void write_pcapfile(void)
 1437 {
 1438    FILE *f;
 1439    
 1440    DEBUG_MSG("write_pcapfile");
 1441    
 1442    /* check if the file is writeable */
 1443    f = fopen(EC_GBL_OPTIONS->pcapfile_out, "w");
 1444    if (f == NULL) {
 1445       ui_error("Cannot write %s", EC_GBL_OPTIONS->pcapfile_out);
 1446       g_free(EC_GBL_OPTIONS->pcapfile_out);
 1447       return;
 1448    }
 1449  
 1450    /* if ok, delete it */
 1451    fclose(f);
 1452    unlink(EC_GBL_OPTIONS->pcapfile_out);
 1453 
 1454    /* set the options for writing to a file */
 1455    EC_GBL_OPTIONS->write = 1;
 1456    EC_GBL_OPTIONS->read = 0;
 1457 }
 1458 
 1459 
 1460 /*
 1461  * set unified interface when changed in UI
 1462  */
 1463 static void gtkui_set_iface_unified(GtkComboBox *combo, gpointer data)
 1464 {
 1465    GtkTreeIter iter;
 1466    gchar *iface;
 1467 
 1468    (void) data;
 1469 
 1470    gtk_combo_box_get_active_iter(combo, &iter);
 1471    gtk_tree_model_get(gtk_combo_box_get_model(combo), &iter, 0, &iface, -1);
 1472 
 1473    DEBUG_MSG("gtkui_set_iface_unified: set iface '%s'", iface);
 1474 
 1475    SAFE_FREE(EC_GBL_OPTIONS->iface);
 1476    SAFE_CALLOC(EC_GBL_OPTIONS->iface, IFACE_LEN, sizeof(char));
 1477    strncpy(EC_GBL_OPTIONS->iface, iface, IFACE_LEN);
 1478 }
 1479 
 1480 /*
 1481  * set bridged interface when changed 
 1482  */
 1483 static void gtkui_set_iface_bridge(GtkComboBox *combo, gpointer data)
 1484 {
 1485    GtkTreeIter iter;
 1486    gchar *iface;
 1487 
 1488    (void) data;
 1489 
 1490    gtk_combo_box_get_active_iter(combo, &iter);
 1491    gtk_tree_model_get(gtk_combo_box_get_model(combo), &iter, 0, &iface, -1);
 1492 
 1493    DEBUG_MSG("gtkui_set_iface_bridge: set iface '%s'", iface);
 1494 
 1495    SAFE_FREE(EC_GBL_OPTIONS->iface_bridge);
 1496    SAFE_CALLOC(EC_GBL_OPTIONS->iface_bridge, IFACE_LEN, sizeof(char));
 1497    strncpy(EC_GBL_OPTIONS->iface_bridge, iface, IFACE_LEN);
 1498 }
 1499 
 1500 
 1501 /*
 1502  * bridged sniffing switcher callback
 1503  */
 1504 static gboolean gtkui_bridged_switch(GtkSwitch *switcher, gboolean state, gpointer data)
 1505 {
 1506    (void) data;
 1507    //gtk_switch_set_active(switcher, state);
 1508    gtk_switch_set_state(switcher, state);
 1509    gtk_widget_set_sensitive(GTK_WIDGET(data), gtk_switch_get_active(switcher));
 1510 
 1511    return TRUE;
 1512 }
 1513 
 1514 
 1515 
 1516 /*
 1517  * sniffing at startup switcher callback
 1518  */
 1519 static gboolean gtkui_autostart_switch(GtkSwitch *switcher, gboolean state, gpointer data)
 1520 {
 1521    (void) data;
 1522    //gtk_switch_set_active(switcher, state);
 1523    gtk_switch_set_state(switcher, state);
 1524    EC_GBL_CONF->sniffing_at_startup = gtk_switch_get_active(switcher);
 1525 
 1526    return TRUE;
 1527 }
 1528 
 1529 /*
 1530  * check if unified or bridged sniffing, 
 1531  * then quit this application intance to continue 
 1532  * main functions further initializing ettercap
 1533  */
 1534 static void gtkui_sniff(GtkButton *button, gpointer data)
 1535 {
 1536    (void) button;
 1537 
 1538    /* set bridge sniffing if switcher has been set to "on" */
 1539    if (gtk_switch_get_active(GTK_SWITCH(data)))
 1540       set_bridge_sniff();
 1541 
 1542    /* quit first instance of GtkApplication */
 1543    g_application_quit(G_APPLICATION(etterapp));
 1544 
 1545 }
 1546 
 1547 /*
 1548  * display the pcap filter dialog
 1549  */
 1550 static void gtkui_pcap_filter(GSimpleAction *action, GVariant *value, gpointer data)
 1551 {
 1552    (void) action;
 1553    (void) value;
 1554    (void) data;
 1555 
 1556 #define PCAP_FILTER_LEN  50
 1557    
 1558    DEBUG_MSG("gtk_pcap_filter");
 1559    
 1560    if (EC_GBL_PCAP->filter == NULL)
 1561        SAFE_CALLOC(EC_GBL_PCAP->filter, PCAP_FILTER_LEN, sizeof(char));
 1562 
 1563    /* 
 1564     * no callback, the filter is set but we have to return to
 1565     * the interface for other user input
 1566     */
 1567    gtkui_input("Pcap filter :", EC_GBL_PCAP->filter, PCAP_FILTER_LEN, NULL);
 1568 }
 1569 
 1570 /*
 1571  * set a different netmask than the system one 
 1572  */
 1573 static void gtkui_set_netmask(GSimpleAction *action, GVariant *value, gpointer data)
 1574 {
 1575    struct ip_addr net;
 1576 
 1577    (void) action;
 1578    (void) value;
 1579    (void) data;
 1580    
 1581    DEBUG_MSG("gtkui_set_netmask");
 1582   
 1583    if (EC_GBL_OPTIONS->netmask == NULL)
 1584       SAFE_CALLOC(EC_GBL_OPTIONS->netmask, IP_ASCII_ADDR_LEN, sizeof(char));
 1585 
 1586    /* 
 1587     * no callback, the filter is set but we have to return to
 1588     * the interface for other user input
 1589     */
 1590    gtkui_input("Netmask :", EC_GBL_OPTIONS->netmask, IP_ASCII_ADDR_LEN, NULL);
 1591 
 1592    /* sanity check */
 1593    if (strcmp(EC_GBL_OPTIONS->netmask, "") && 
 1594          ip_addr_pton(EC_GBL_OPTIONS->netmask, &net) != E_SUCCESS)
 1595       ui_error("Invalid netmask %s", EC_GBL_OPTIONS->netmask);
 1596             
 1597    /* if no netmask was specified, free it */
 1598    if (!strcmp(EC_GBL_OPTIONS->netmask, ""))
 1599       SAFE_FREE(EC_GBL_OPTIONS->netmask);
 1600 }
 1601 
 1602 
 1603 /*
 1604  * Callback for g_timeout_add() to resolve a IP to name asyncronously
 1605  * if the name is not already in the cache, host_iptoa
 1606  * immediately returns but starts the resolution process
 1607  * in the background. 
 1608  * This function periodically recalls this host_iptoa until
 1609  * a result in available in the cache and updates the widget.
 1610  */
 1611 gboolean gtkui_iptoa_deferred(gpointer data)
 1612 {
 1613    struct resolv_object *ro;
 1614    char name[MAX_HOSTNAME_LEN];
 1615    ro = (struct resolv_object *)data;
 1616 
 1617    DEBUG_MSG("gtkui_iptoa_deferred");
 1618 
 1619    if (host_iptoa(ro->ip, name) == E_SUCCESS) {
 1620       /* 
 1621        * Name has now been resolved in the background
 1622        * Set the widget text and destroy the timer
 1623        */
 1624       if (ro->type == GTK_TYPE_LABEL)
 1625             gtk_label_set_text(GTK_LABEL(ro->widget), name);
 1626       else if (ro->type == GTK_TYPE_LIST_STORE)
 1627             gtk_list_store_set(GTK_LIST_STORE(ro->liststore), 
 1628                   &ro->treeiter, ro->column, name, -1);
 1629       
 1630       /* Free allocated memory */
 1631       SAFE_FREE(ro);
 1632 
 1633       /* destroy timer */
 1634       return FALSE;
 1635    }
 1636    else  {
 1637       /* Keep trying */
 1638       return TRUE;
 1639    }
 1640 }
 1641 
 1642 
 1643 /* hitting "Enter" keyy in a combo box does the same as clicking OK button */
 1644 gboolean gtkui_combo_enter(GtkWidget *widget, GdkEventKey *event, gpointer data)
 1645 {
 1646    GtkWidget *dialog;
 1647 
 1648    /* variable not used */
 1649    (void) data;
 1650 
 1651    if (event->keyval == GDK_KEY_Return) {
 1652       dialog = g_object_get_data(G_OBJECT(widget), "dialog");
 1653       gtk_dialog_response(GTK_DIALOG (dialog), GTK_RESPONSE_OK);
 1654 
 1655       return TRUE;
 1656    }
 1657 
 1658    return FALSE;
 1659 }
 1660 
 1661 /* hitting "Enter" key in dialog does same as clicking OK button */
 1662 void gtkui_dialog_enter(GtkWidget *widget, gpointer data) {
 1663    GtkWidget *dialog;
 1664 
 1665    /* variable not used */
 1666    (void) data;
 1667 
 1668    dialog = g_object_get_data(G_OBJECT(widget), "dialog");
 1669    gtk_dialog_response(GTK_DIALOG (dialog), GTK_RESPONSE_OK);
 1670 }
 1671 
 1672 /* create a new notebook (tab) page */
 1673 /* returns a parent widget to pack the contents of the page into */
 1674 GtkWidget *gtkui_page_new(char *title, void (*callback)(void), void (*detacher)(GtkWidget *)) {
 1675    GtkWidget *parent, *label;
 1676    GtkWidget *hbox, *button, *image;
 1677 
 1678    /* a container to hold the close button and tab label */
 1679    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
 1680    gtk_widget_show(hbox);
 1681 
 1682    /* the label for the tab title */
 1683    label = gtk_label_new(title);
 1684    gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
 1685    gtk_widget_show(label);
 1686 
 1687    /* the close button */
 1688    button = gtk_button_new();
 1689    gtk_button_set_relief(GTK_BUTTON (button), GTK_RELIEF_NONE);
 1690    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
 1691    gtk_widget_set_size_request(button, 20, 20);
 1692    gtk_widget_show(button);
 1693 
 1694    /* an image for the button */
 1695    image = gtk_image_new_from_icon_name("window-close", GTK_ICON_SIZE_MENU);
 1696    gtk_container_add(GTK_CONTAINER (button), image);
 1697    gtk_widget_show(image);
 1698 
 1699    /* a parent to pack the contents into */
 1700    parent = gtk_frame_new(NULL);
 1701    gtk_frame_set_shadow_type(GTK_FRAME(parent), GTK_SHADOW_NONE);
 1702    gtk_widget_show(parent);
 1703 
 1704    if(!notebook && notebook_frame) {
 1705       gtk_container_remove(GTK_CONTAINER (notebook_frame), gtk_bin_get_child(GTK_BIN (notebook_frame)));
 1706 
 1707       notebook = gtk_notebook_new();
 1708       gtk_notebook_set_tab_pos(GTK_NOTEBOOK (notebook), GTK_POS_TOP);
 1709       gtk_notebook_set_scrollable(GTK_NOTEBOOK (notebook), TRUE);
 1710       gtk_container_add(GTK_CONTAINER (notebook_frame), notebook);
 1711       gtk_widget_show(notebook);
 1712 
 1713       gtkui_create_tab_menu();
 1714    }
 1715 
 1716    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), parent, hbox);
 1717 
 1718    /* attach callback to destroy the tab/page */
 1719    g_signal_connect(G_OBJECT (button), "clicked", G_CALLBACK(gtkui_page_close), parent);
 1720 
 1721    /* attach callback to do specific clean-up */
 1722    if(callback)
 1723       g_object_set_data(G_OBJECT (parent), "destroy", callback);
 1724 
 1725    if(detacher)
 1726       g_object_set_data(G_OBJECT (parent), "detach", detacher);
 1727 
 1728    gtkui_page_present(parent);
 1729 
 1730    return(parent);
 1731 }
 1732 
 1733 /* show and focus the page containing child */
 1734 void gtkui_page_present(GtkWidget *child) {
 1735    int num = 0;
 1736 
 1737    num = gtk_notebook_page_num(GTK_NOTEBOOK (notebook), child);
 1738    gtk_notebook_set_current_page(GTK_NOTEBOOK (notebook), num);
 1739 
 1740 }
 1741 
 1742 /* close the page containing the child passed in "data" */
 1743 void gtkui_page_close(GtkWidget *widget, gpointer data) {
 1744    GtkWidget *child;
 1745    gint num = 0;
 1746    void (*callback)(void);
 1747 
 1748    /* variable not used */
 1749    (void) widget;
 1750    (void) data;
 1751 
 1752    DEBUG_MSG("gtkui_page_close");
 1753 
 1754    num = gtk_notebook_page_num(GTK_NOTEBOOK(notebook), GTK_WIDGET (data));
 1755    child = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), num);
 1756    g_object_ref(G_OBJECT(child));
 1757 
 1758    gtk_notebook_remove_page(GTK_NOTEBOOK(notebook), num);
 1759 
 1760    callback = g_object_get_data(G_OBJECT (child), "destroy");
 1761    if(callback)
 1762       callback();
 1763 }
 1764 
 1765 /* close the currently focused notebook page */
 1766 void gtkui_page_close_current(GSimpleAction *action, GVariant *value, gpointer data) {
 1767    GtkWidget *child;
 1768    gint num = 0;
 1769 
 1770    (void) action;
 1771    (void) value;
 1772    (void) data;
 1773 
 1774    num = gtk_notebook_get_current_page(GTK_NOTEBOOK (notebook));
 1775    child = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), num);
 1776 
 1777    gtkui_page_close(NULL, child);
 1778 }
 1779 
 1780 /* show the context menu when the notebook tabs receive a mouse right-click */
 1781 gboolean gtkui_context_menu(GtkWidget *widget, GdkEventButton *event, gpointer data) {
 1782    /* variable not used */
 1783    (void) widget;
 1784 
 1785     if(event->button == 3) {
 1786 #if GTK_CHECK_VERSION(3,22,0)
 1787        gtk_menu_popup_at_pointer(GTK_MENU(data), (GdkEvent*)event);
 1788 #else
 1789        gtk_menu_popup(GTK_MENU(data), NULL, NULL, NULL, NULL, 3, event->time);
 1790 #endif
 1791        /* 
 1792         * button press event handle must return TRUE to keep the selection
 1793         * active when pressing the mouse button 
 1794         */
 1795        return TRUE;
 1796     }
 1797 
 1798     return FALSE;
 1799 }
 1800 
 1801 /* detach the currently focused notebook page into a free window */
 1802 void gtkui_page_detach_current(GSimpleAction *action, GVariant *value, gpointer data) {
 1803    void (*detacher)(GtkWidget *);
 1804    GtkWidget *child;
 1805    gint num = 0;
 1806 
 1807    (void) action;
 1808    (void) value;
 1809    (void) data;
 1810 
 1811    num = gtk_notebook_get_current_page(GTK_NOTEBOOK (notebook));
 1812    if(num < 0)
 1813       return;
 1814    child = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), num);
 1815    g_object_ref(G_OBJECT(child));
 1816 
 1817    gtk_notebook_remove_page(GTK_NOTEBOOK(notebook), num);
 1818    
 1819    detacher = g_object_get_data(G_OBJECT (child), "detach");
 1820    if(detacher)
 1821       detacher(child);
 1822 }
 1823 
 1824 void gtkui_page_attach_shortcut(GtkWidget *win, void (*attacher)(void))
 1825 {
 1826    GtkAccelGroup *accel;
 1827    GClosure *closure = NULL;
 1828    GdkModifierType mods;
 1829    gint keyval;
 1830 
 1831    accel = gtk_accel_group_new ();
 1832    gtk_window_add_accel_group(GTK_WINDOW (win), accel);
 1833    closure = g_cclosure_new(G_CALLBACK(attacher), NULL, NULL);
 1834    gtk_accelerator_parse ("<control>D", &keyval, &mods);
 1835    gtk_accel_group_connect(accel, keyval, mods, 0, closure);
 1836 }
 1837 
 1838 /* change view and focus to the next notebook page */
 1839 void gtkui_page_right(GSimpleAction *action, GVariant *value, gpointer data) {
 1840    (void) action;
 1841    (void) value;
 1842    (void) data;
 1843 
 1844    gtk_notebook_next_page(GTK_NOTEBOOK (notebook));
 1845 }
 1846 
 1847 /* change view and focus to previous notebook page */
 1848 void gtkui_page_left(GSimpleAction *action, GVariant *value, gpointer data) {
 1849    (void) action;
 1850    (void) value;
 1851    (void) data;
 1852 
 1853    gtk_notebook_prev_page(GTK_NOTEBOOK (notebook));
 1854 }
 1855 
 1856 /* for connecting to browse buttons, pass entry widget as callback "data" */
 1857 void gtkui_filename_browse(GtkWidget *widget, gpointer data)
 1858 {  
 1859    GtkWidget *dialog = NULL;
 1860    gint response = 0;
 1861    const char *filename = NULL;
 1862    
 1863    /* variable not used */
 1864    (void) widget;
 1865 
 1866    dialog = gtk_file_chooser_dialog_new("Select a file...",
 1867          NULL, GTK_FILE_CHOOSER_ACTION_OPEN, 
 1868          "_Cancel", GTK_RESPONSE_CANCEL,
 1869          "_OK",     GTK_RESPONSE_OK, 
 1870          NULL);
 1871    
 1872    response = gtk_dialog_run (GTK_DIALOG (dialog));
 1873    
 1874    if (response == GTK_RESPONSE_OK) {
 1875       gtk_widget_hide(dialog); 
 1876       filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
 1877 
 1878       gtk_entry_set_text(GTK_ENTRY (data), filename);
 1879    }
 1880    gtk_widget_destroy(dialog);
 1881 }
 1882 
 1883 /* make sure data is valid UTF8 */
 1884 char *gtkui_utf8_validate(char *data) {
 1885    const gchar *end;
 1886    char *unicode = NULL;
 1887 
 1888    unicode = data;
 1889    if(!g_utf8_validate (data, -1, &end)) {
 1890       /* if "end" pointer is at beginning of string, we have no valid text to print */
 1891       if(end == unicode) return(NULL);
 1892 
 1893       /* cut off the invalid part so we don't lose the whole string */
 1894       /* this shouldn't happen often */
 1895       unicode = (char *)end;
 1896       *unicode = 0;
 1897       unicode = data;
 1898    }
 1899 
 1900    return(unicode);
 1901 }
 1902 
 1903 /* EOF */
 1904 
 1905 // vim:ts=3:expandtab
 1906