/* main.c * * $Id$ * * Ethereal - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * Richard Sharpe, 13-Feb-1999, added support for initializing structures * needed by dissect routines * Jeff Foster, 2001/03/12, added support tabbed hex display windowss * * * This program 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 2 * of the License, or (at your option) any later version. * * This program 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. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_IO_H #include /* open/close on win32 */ #endif #ifdef NEED_STRERROR_H #include "strerror.h" #endif #ifdef NEED_GETOPT_H #include "getopt.h" #endif #ifdef _WIN32 /* Needed for console I/O */ #include #include #endif #include #include #include #include #include #include #include #include #include /* general (not GTK specific) */ #include "svnversion.h" #include "file.h" #include "summary.h" #include "filters.h" #include "disabled_protos.h" #include #include "filter_dlg.h" #include "layout_prefs.h" #include "color.h" #include "color_filters.h" #include "print.h" #include "simple_dialog.h" #include "register.h" #include #include "ringbuffer.h" #include "../ui_util.h" /* beware: ui_util.h exists twice! */ #include #include "util.h" #include "clopts_common.h" #include "version_info.h" #include "merge.h" #ifdef HAVE_LIBPCAP #include #include "pcap-util.h" #include "capture.h" #endif #ifdef _WIN32 #include "capture-wpcap.h" #include "capture_wpcap_packet.h" #endif #if GTK_MAJOR_VERSION < 2 && GTK_MINOR_VERSION < 3 #include "ethclist.h" #endif /* GTK related */ #include "statusbar.h" #include "alert_box.h" #include "dlg_utils.h" #include "gtkglobals.h" #include "colors.h" #include "ui_util.h" /* beware: ui_util.h exists twice! */ #include "compat_macros.h" #include "main.h" #include "menu.h" #include "../main_window.h" #include "../menu.h" #include "file_dlg.h" #include #include "proto_draw.h" #include "keys.h" #include "packet_win.h" #include "toolbar.h" #include "find_dlg.h" #include "packet_list.h" #include "recent.h" #include "follow_dlg.h" #include "font_utils.h" #include "about_dlg.h" #include "help_dlg.h" #include "decode_as_dlg.h" #include "webbrowser.h" #include "capture_dlg.h" #if 0 #include "../image/eicon3d64.xpm" #endif #include "capture_ui_utils.h" #include "log.h" /* * File under personal preferences directory in which GTK settings for * Ethereal are stored. */ #define RC_FILE "gtkrc" #ifdef HAVE_LIBPCAP #define DEF_READY_MESSAGE " Ready to load or capture" #else #define DEF_READY_MESSAGE " Ready to load file" #endif capture_file cfile; GtkWidget *main_display_filter_widget=NULL; GtkWidget *top_level = NULL, *tree_view, *byte_nb_ptr, *tv_scrollw; static GtkWidget *main_pane_v1, *main_pane_v2, *main_pane_h1, *main_pane_h2; static GtkWidget *main_first_pane, *main_second_pane; static GtkWidget *status_pane; static GtkWidget *menubar, *main_vbox, *main_tb, *pkt_scrollw, *stat_hbox, *filter_tb; static GtkWidget *info_bar; static GtkWidget *packets_bar = NULL; static GtkWidget *welcome_pane; static guint main_ctx, file_ctx, help_ctx; static guint packets_ctx; static gchar *packets_str = NULL; GString *comp_info_str, *runtime_info_str; gchar *ethereal_path = NULL; gboolean have_capture_file = FALSE; /* XXX - is there an aquivalent in cfile? */ #ifdef _WIN32 static gboolean has_console; /* TRUE if app has console */ /*static void create_console(void);*/ static void destroy_console(void); #endif static void console_log_handler(const char *log_domain, GLogLevelFlags log_level, const char *message, gpointer user_data); #ifdef HAVE_LIBPCAP static gboolean list_link_layer_types; capture_options global_capture_opts; capture_options *capture_opts = &global_capture_opts; #endif static void create_main_window(gint, gint, gint, e_prefs*); static void show_main_window(gboolean); static void file_quit_answered_cb(gpointer dialog, gint btn, gpointer data); static void main_save_window_geometry(GtkWidget *widget); #define E_DFILTER_CM_KEY "display_filter_combo" #define E_DFILTER_FL_KEY "display_filter_list" /* Match selected byte pattern */ static void match_selected_cb_do(gpointer data, int action, gchar *text) { GtkWidget *filter_te; char *cur_filter, *new_filter; if (!text) return; g_assert(data); filter_te = OBJECT_GET_DATA(data, E_DFILTER_TE_KEY); g_assert(filter_te); cur_filter = gtk_editable_get_chars(GTK_EDITABLE(filter_te), 0, -1); switch (action&MATCH_SELECTED_MASK) { case MATCH_SELECTED_REPLACE: new_filter = g_strdup(text); break; case MATCH_SELECTED_AND: if ((!cur_filter) || (0 == strlen(cur_filter))) new_filter = g_strdup(text); else new_filter = g_strconcat("(", cur_filter, ") && (", text, ")", NULL); break; case MATCH_SELECTED_OR: if ((!cur_filter) || (0 == strlen(cur_filter))) new_filter = g_strdup(text); else new_filter = g_strconcat("(", cur_filter, ") || (", text, ")", NULL); break; case MATCH_SELECTED_NOT: new_filter = g_strconcat("!(", text, ")", NULL); break; case MATCH_SELECTED_AND_NOT: if ((!cur_filter) || (0 == strlen(cur_filter))) new_filter = g_strconcat("!(", text, ")", NULL); else new_filter = g_strconcat("(", cur_filter, ") && !(", text, ")", NULL); break; case MATCH_SELECTED_OR_NOT: if ((!cur_filter) || (0 == strlen(cur_filter))) new_filter = g_strconcat("!(", text, ")", NULL); else new_filter = g_strconcat("(", cur_filter, ") || !(", text, ")", NULL); break; default: g_assert_not_reached(); new_filter = NULL; break; } /* Free up the copy we got of the old filter text. */ g_free(cur_filter); /* create a new one and set the display filter entry accordingly */ gtk_entry_set_text(GTK_ENTRY(filter_te), new_filter); /* Run the display filter so it goes in effect. */ if (action&MATCH_SELECTED_APPLY_NOW) main_filter_packets(&cfile, new_filter, FALSE); /* Free up the new filter text. */ g_free(new_filter); /* Free up the generated text we were handed. */ g_free(text); } void match_selected_ptree_cb(GtkWidget *w, gpointer data, MATCH_SELECTED_E action) { if (cfile.finfo_selected) match_selected_cb_do((data ? data : w), action, proto_construct_dfilter_string(cfile.finfo_selected, cfile.edt)); } static void selected_ptree_info_answered_cb(gpointer dialog _U_, gint btn, gpointer data) { gchar *selected_proto_url; gchar *proto_abbrev = data; switch(btn) { case(ESD_BTN_OK): if (cfile.finfo_selected) { /* open wiki page using the protocol abbreviation */ selected_proto_url = g_strdup_printf("http://wiki.ethereal.com/Protocols/%s", proto_abbrev); browser_open_url(selected_proto_url); g_free(selected_proto_url); } break; case(ESD_BTN_CANCEL): break; default: g_assert_not_reached(); } } void selected_ptree_info_cb(GtkWidget *widget _U_, gpointer data _U_) { int field_id; gchar *proto_abbrev; gpointer dialog; if (cfile.finfo_selected) { /* convert selected field to protocol abbreviation */ /* XXX - could this conversion be simplified? */ field_id = cfile.finfo_selected->hfinfo->id; /* if the selected field isn't a protocol, get it's parent */ if(!proto_registrar_is_protocol(field_id)) { field_id = proto_registrar_get_parent(cfile.finfo_selected->hfinfo->id); } proto_abbrev = proto_registrar_get_abbrev(field_id); /* ask the user if the wiki page really should be opened */ dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_OK_CANCEL, PRIMARY_TEXT_START "Open Ethereal Wiki page of protocol \"%s\"?" PRIMARY_TEXT_END "\n" "\n" "This will open the \"%s\" related Ethereal Wiki page in your Web browser.\n" "\n" "The Ethereal Wiki is a collaborative approach to provide information\n" "about Ethereal in several ways (not limited to protocol specifics).\n" "\n" "This Wiki is new, so the page of the selected protocol\n" "may not exist and/or may not contain valuable information.\n" "\n" "As everyone can edit the Wiki and add new content (or extend existing),\n" "you are encouraged to add information if you can.\n" "\n" "Hint 1: If you are new to wiki editing, try out editing the Sandbox first!\n" "\n" "Hint 2: If you want to add a new protocol page, you should use the ProtocolTemplate,\n" "which will save you a lot of editing and will give a consistent look over the pages.", proto_abbrev, proto_abbrev); simple_dialog_set_cb(dialog, selected_ptree_info_answered_cb, proto_abbrev); } } void selected_ptree_ref_cb(GtkWidget *widget _U_, gpointer data _U_) { int field_id; gchar *proto_abbrev; gchar *selected_proto_url; if (cfile.finfo_selected) { /* convert selected field to protocol abbreviation */ /* XXX - could this conversion be simplified? */ field_id = cfile.finfo_selected->hfinfo->id; /* if the selected field isn't a protocol, get it's parent */ if(!proto_registrar_is_protocol(field_id)) { field_id = proto_registrar_get_parent(cfile.finfo_selected->hfinfo->id); } proto_abbrev = proto_registrar_get_abbrev(field_id); /* open reference page using the protocol abbreviation */ selected_proto_url = g_strdup_printf("http://www.ethereal.com/docs/dfref/%c/%s", proto_abbrev[0], proto_abbrev); browser_open_url(selected_proto_url); g_free(selected_proto_url); } } static gchar * get_text_from_packet_list(gpointer data) { gint row = GPOINTER_TO_INT(OBJECT_GET_DATA(data, E_MPACKET_LIST_ROW_KEY)); gint column = GPOINTER_TO_INT(OBJECT_GET_DATA(data, E_MPACKET_LIST_COL_KEY)); frame_data *fdata = (frame_data *)packet_list_get_row_data(row); epan_dissect_t *edt; gchar *buf=NULL; int len; int err; gchar *err_info; if (fdata != NULL) { if (!wtap_seek_read(cfile.wth, fdata->file_off, &cfile.pseudo_header, cfile.pd, fdata->cap_len, &err, &err_info)) { simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, cf_read_error_message(err, err_info), cfile.filename); return NULL; } edt = epan_dissect_new(FALSE, FALSE); epan_dissect_run(edt, &cfile.pseudo_header, cfile.pd, fdata, &cfile.cinfo); epan_dissect_fill_in_columns(edt); if (strlen(cfile.cinfo.col_expr[column]) != 0 && strlen(cfile.cinfo.col_expr_val[column]) != 0) { len = strlen(cfile.cinfo.col_expr[column]) + strlen(cfile.cinfo.col_expr_val[column]) + 5; buf = g_malloc0(len); g_snprintf(buf, len, "%s == %s", cfile.cinfo.col_expr[column], cfile.cinfo.col_expr_val[column]); } epan_dissect_free(edt); } return buf; } void match_selected_plist_cb(GtkWidget *w _U_, gpointer data, MATCH_SELECTED_E action) { match_selected_cb_do(data, action, get_text_from_packet_list(data)); } /* XXX: use a preference for this setting! */ static guint dfilter_combo_max_recent = 10; /* add a display filter to the combo box */ /* Note: a new filter string will replace an old identical one */ static gboolean dfilter_combo_add(GtkWidget *filter_cm, char *s) { GList *li; GList *dfilter_list = OBJECT_GET_DATA(filter_cm, E_DFILTER_FL_KEY); /* GtkCombos don't let us get at their list contents easily, so we maintain our own filter list, and feed it to gtk_combo_set_popdown_strings when a new filter is added. */ li = g_list_first(dfilter_list); while (li) { /* If the filter is already in the list, remove the old one and * append the new one at the latest position (at g_list_append() below) */ if (li->data && strcmp(s, li->data) == 0) { dfilter_list = g_list_remove(dfilter_list, li->data); break; } li = li->next; } dfilter_list = g_list_append(dfilter_list, s); OBJECT_SET_DATA(filter_cm, E_DFILTER_FL_KEY, dfilter_list); gtk_combo_set_popdown_strings(GTK_COMBO(filter_cm), dfilter_list); gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(filter_cm)->entry), g_list_last(dfilter_list)->data); return TRUE; } /* write all non empty display filters (until maximum count) * of the combo box GList to the user's recent file */ void dfilter_recent_combo_write_all(FILE *rf) { GtkWidget *filter_cm = OBJECT_GET_DATA(top_level, E_DFILTER_CM_KEY); GList *dfilter_list = OBJECT_GET_DATA(filter_cm, E_DFILTER_FL_KEY); GList *li; guint max_count = 0; /* write all non empty display filter strings to the recent file (until max count) */ li = g_list_first(dfilter_list); while ( li && (max_count++ <= dfilter_combo_max_recent) ) { if (strlen(li->data)) { fprintf (rf, RECENT_KEY_DISPLAY_FILTER ": %s\n", (char *)li->data); } li = li->next; } } /* empty the combobox entry field */ void dfilter_combo_add_empty(void) { GtkWidget *filter_cm = OBJECT_GET_DATA(top_level, E_DFILTER_CM_KEY); gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(filter_cm)->entry), ""); } /* add a display filter coming from the user's recent file to the dfilter combo box */ gboolean dfilter_combo_add_recent(gchar *s) { GtkWidget *filter_cm = OBJECT_GET_DATA(top_level, E_DFILTER_CM_KEY); char *dup; dup = g_strdup(s); if (!dfilter_combo_add(filter_cm, dup)) { g_free(dup); return FALSE; } return TRUE; } /* call cf_filter_packets() and add this filter string to the recent filter list */ gboolean main_filter_packets(capture_file *cf, const gchar *dftext, gboolean force) { GtkCombo *filter_cm = OBJECT_GET_DATA(top_level, E_DFILTER_CM_KEY); GList *dfilter_list = OBJECT_GET_DATA(filter_cm, E_DFILTER_FL_KEY); GList *li; gboolean add_filter = TRUE; gboolean free_filter = TRUE; char *s; cf_status_t cf_status; /* we'll crash later on if dftext is NULL */ g_assert(dftext != NULL); s = g_strdup(dftext); /* GtkCombos don't let us get at their list contents easily, so we maintain our own filter list, and feed it to gtk_combo_set_popdown_strings when a new filter is added. */ cf_status = cf_filter_packets(cf, s, force); if (cf_status == CF_OK) { li = g_list_first(dfilter_list); while (li) { if (li->data && strcmp(s, li->data) == 0) add_filter = FALSE; li = li->next; } if (add_filter) { /* trim list size first */ while (g_list_length(dfilter_list) >= dfilter_combo_max_recent) { dfilter_list = g_list_remove(dfilter_list, g_list_first(dfilter_list)->data); } free_filter = FALSE; dfilter_list = g_list_append(dfilter_list, s); OBJECT_SET_DATA(filter_cm, E_DFILTER_FL_KEY, dfilter_list); gtk_combo_set_popdown_strings(filter_cm, dfilter_list); gtk_entry_set_text(GTK_ENTRY(filter_cm->entry), g_list_last(dfilter_list)->data); } } if (free_filter) g_free(s); return (cf_status == CF_OK); } /* Run the current display filter on the current packet set, and redisplay. */ static void filter_activate_cb(GtkWidget *w _U_, gpointer data) { const char *s; s = gtk_entry_get_text(GTK_ENTRY(data)); main_filter_packets(&cfile, s, FALSE); } /* redisplay with no display filter */ static void filter_reset_cb(GtkWidget *w, gpointer data _U_) { GtkWidget *filter_te = NULL; if ((filter_te = OBJECT_GET_DATA(w, E_DFILTER_TE_KEY))) { gtk_entry_set_text(GTK_ENTRY(filter_te), ""); } main_filter_packets(&cfile, "", FALSE); } /* mark as reference time frame */ static void set_frame_reftime(gboolean set, frame_data *frame, gint row) { if (row == -1) return; if (set) { frame->flags.ref_time=1; } else { frame->flags.ref_time=0; } cf_reftime_packets(&cfile); } void reftime_frame_cb(GtkWidget *w _U_, gpointer data _U_, REFTIME_ACTION_E action) { switch(action){ case REFTIME_TOGGLE: if (cfile.current_frame) { /* XXX hum, should better have a "cfile->current_row" here ... */ set_frame_reftime(!cfile.current_frame->flags.ref_time, cfile.current_frame, packet_list_find_row_from_data(cfile.current_frame)); } break; case REFTIME_FIND_NEXT: find_previous_next_frame_with_filter("frame.ref_time", FALSE); break; case REFTIME_FIND_PREV: find_previous_next_frame_with_filter("frame.ref_time", TRUE); break; } } #if GTK_MAJOR_VERSION < 2 static void tree_view_select_row_cb(GtkCTree *ctree, GList *node, gint column _U_, gpointer user_data _U_) #else static void tree_view_selection_changed_cb(GtkTreeSelection *sel, gpointer user_data _U_) #endif { field_info *finfo; gchar *help_str = NULL; gchar len_str[2+10+1+5+1]; /* ", {N} bytes\0", N < 4294967296 */ gboolean has_blurb = FALSE; guint length = 0, byte_len; GtkWidget *byte_view; const guint8 *byte_data; #if GTK_MAJOR_VERSION >= 2 GtkTreeModel *model; GtkTreeIter iter; #endif #if GTK_MAJOR_VERSION >= 2 /* if nothing is selected */ if (!gtk_tree_selection_get_selected(sel, &model, &iter)) { /* * Which byte view is displaying the current protocol tree * row's data? */ byte_view = get_notebook_bv_ptr(byte_nb_ptr); if (byte_view == NULL) return; /* none */ byte_data = get_byte_view_data_and_length(byte_view, &byte_len); if (byte_data == NULL) return; /* none */ cf_unselect_field(&cfile); packet_hex_print(GTK_TEXT_VIEW(byte_view), byte_data, cfile.current_frame, NULL, byte_len); return; } gtk_tree_model_get(model, &iter, 1, &finfo, -1); #else g_assert(node); finfo = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(node) ); #endif if (!finfo) return; set_notebook_page(byte_nb_ptr, finfo->ds_tvb); byte_view = get_notebook_bv_ptr(byte_nb_ptr); byte_data = get_byte_view_data_and_length(byte_view, &byte_len); g_assert(byte_data != NULL); cfile.finfo_selected = finfo; set_menus_for_selected_tree_row(&cfile); if (finfo->hfinfo) { if (finfo->hfinfo->blurb != NULL && finfo->hfinfo->blurb[0] != '\0') { has_blurb = TRUE; length = strlen(finfo->hfinfo->blurb); } else { length = strlen(finfo->hfinfo->name); } if (finfo->length == 0) { len_str[0] = '\0'; } else if (finfo->length == 1) { strcpy (len_str, ", 1 byte"); } else { g_snprintf (len_str, sizeof len_str, ", %d bytes", finfo->length); } statusbar_pop_field_msg(); /* get rid of current help msg */ if (length) { help_str = g_strdup_printf("%s (%s)%s", (has_blurb) ? finfo->hfinfo->blurb : finfo->hfinfo->name, finfo->hfinfo->abbrev, len_str); statusbar_push_field_msg(help_str); g_free(help_str); } else { /* * Don't show anything if the field name is zero-length; * the pseudo-field for "proto_tree_add_text()" is such * a field, and we don't want "Text (text)" showing up * on the status line if you've selected such a field. * * XXX - there are zero-length fields for which we *do* * want to show the field name. * * XXX - perhaps the name and abbrev field should be null * pointers rather than null strings for that pseudo-field, * but we'd have to add checks for null pointers in some * places if we did that. * * Or perhaps protocol tree items added with * "proto_tree_add_text()" should have -1 as the field index, * with no pseudo-field being used, but that might also * require special checks for -1 to be added. */ statusbar_push_field_msg(""); } } #if GTK_MAJOR_VERSION < 2 packet_hex_print(GTK_TEXT(byte_view), byte_data, cfile.current_frame, finfo, byte_len); #else packet_hex_print(GTK_TEXT_VIEW(byte_view), byte_data, cfile.current_frame, finfo, byte_len); #endif } #if GTK_MAJOR_VERSION < 2 static void tree_view_unselect_row_cb(GtkCTree *ctree _U_, GList *node _U_, gint column _U_, gpointer user_data _U_) { GtkWidget *byte_view; const guint8 *data; guint len; /* * Which byte view is displaying the current protocol tree * row's data? */ byte_view = get_notebook_bv_ptr(byte_nb_ptr); if (byte_view == NULL) return; /* none */ data = get_byte_view_data_and_length(byte_view, &len); if (data == NULL) return; /* none */ cf_unselect_field(&cfile); packet_hex_print(GTK_TEXT(byte_view), data, cfile.current_frame, NULL, len); } #endif void collapse_all_cb(GtkWidget *widget _U_, gpointer data _U_) { if (cfile.edt->tree) collapse_all_tree(cfile.edt->tree, tree_view); } void expand_all_cb(GtkWidget *widget _U_, gpointer data _U_) { if (cfile.edt->tree) expand_all_tree(cfile.edt->tree, tree_view); } void expand_tree_cb(GtkWidget *widget _U_, gpointer data _U_) { #if GTK_MAJOR_VERSION < 2 GtkCTreeNode *node; #else GtkTreePath *path; #endif #if GTK_MAJOR_VERSION < 2 node = gtk_ctree_find_by_row_data(GTK_CTREE(tree_view), NULL, cfile.finfo_selected); if(node) { /* the mouse position is at an entry, expand that one */ gtk_ctree_expand_recursive(GTK_CTREE(tree_view), node); } #else path = tree_find_by_field_info(GTK_TREE_VIEW(tree_view), cfile.finfo_selected); if(path) { /* the mouse position is at an entry, expand that one */ gtk_tree_view_expand_row(GTK_TREE_VIEW(tree_view), path, TRUE); gtk_tree_path_free(path); } #endif } void resolve_name_cb(GtkWidget *widget _U_, gpointer data _U_) { if (cfile.edt->tree) { guint32 tmp = g_resolv_flags; g_resolv_flags = RESOLV_ALL; proto_tree_draw(cfile.edt->tree, tree_view); g_resolv_flags = tmp; } } /* * Push a message referring to file access onto the statusbar. */ void statusbar_push_file_msg(gchar *msg) { /*g_warning("statusbar_push: %s", msg);*/ gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, msg); } /* * Pop a message referring to file access off the statusbar. */ void statusbar_pop_file_msg(void) { /*g_warning("statusbar_pop");*/ gtk_statusbar_pop(GTK_STATUSBAR(info_bar), file_ctx); } /* * XXX - do we need multiple statusbar contexts? */ /* * Push a message referring to the currently-selected field onto the statusbar. */ void statusbar_push_field_msg(gchar *msg) { gtk_statusbar_push(GTK_STATUSBAR(info_bar), help_ctx, msg); } /* * Pop a message referring to the currently-selected field off the statusbar. */ void statusbar_pop_field_msg(void) { gtk_statusbar_pop(GTK_STATUSBAR(info_bar), help_ctx); } /* * update the packets statusbar to the current values */ void packets_bar_update(void) { if(packets_bar) { /* remove old status */ if(packets_str) { g_free(packets_str); gtk_statusbar_pop(GTK_STATUSBAR(packets_bar), packets_ctx); } /* do we have any packets? */ if(cfile.count) { if(cfile.drops_known) { packets_str = g_strdup_printf(" P: %u D: %u M: %u Drops: %u", cfile.count, cfile.displayed_count, cfile.marked_count, cfile.drops); } else { packets_str = g_strdup_printf(" P: %u D: %u M: %u", cfile.count, cfile.displayed_count, cfile.marked_count); } } else { packets_str = g_strdup(" No Packets"); } gtk_statusbar_push(GTK_STATUSBAR(packets_bar), packets_ctx, packets_str); } } void main_set_for_capture_file(gboolean have_capture_file_in) { have_capture_file = have_capture_file_in; main_widgets_show_or_hide(); } gboolean main_do_quit(void) { /* get the current geometry, before writing it to disk */ main_save_window_geometry(top_level); /* write user's recent file to disk * It is no problem to write this file, even if we do not quit */ write_recent(); /* XXX - should we check whether the capture file is an unsaved temporary file for a live capture and, if so, pop up a "do you want to exit without saving the capture file?" dialog, and then just return, leaving said dialog box to forcibly quit if the user clicks "OK"? If so, note that this should be done in a subroutine that returns TRUE if we do so, and FALSE otherwise, and if it returns TRUE we should return TRUE without nuking anything. Note that, if we do that, we might also want to check if an "Update list of packets in real time" capture is in progress and, if so, ask whether they want to terminate the capture and discard it, and return TRUE, before nuking any child capture, if they say they don't want to do so. */ #ifdef HAVE_LIBPCAP /* Nuke any child capture in progress. */ capture_kill_child(capture_opts); #endif /* Are we in the middle of reading a capture? */ if (cfile.state == FILE_READ_IN_PROGRESS) { /* Yes, so we can't just close the file and quit, as that may yank the rug out from under the read in progress; instead, just set the state to "FILE_READ_ABORTED" and return - the code doing the read will check for that and, if it sees that, will clean up and quit. */ cfile.state = FILE_READ_ABORTED; /* Say that the window should *not* be deleted; that'll be done by the code that cleans up. */ return TRUE; } else { /* Close any capture file we have open; on some OSes, you can't unlink a temporary capture file if you have it open. "cf_close()" will unlink it after closing it if it's a temporary file. We do this here, rather than after the main loop returns, as, after the main loop returns, the main window may have been destroyed (if this is called due to a "destroy" even on the main window rather than due to the user selecting a menu item), and there may be a crash or other problem when "cf_close()" tries to clean up stuff in the main window. XXX - is there a better place to put this? Or should we have a routine that *just* closes the capture file, and doesn't do anything with the UI, which we'd call here, and another routine that calls that routine and also cleans up the UI, which we'd call elsewhere? */ cf_close(&cfile); /* Exit by leaving the main loop, so that any quit functions we registered get called. */ gtk_main_quit(); /* Say that the window should be deleted. */ return FALSE; } } static gboolean main_window_delete_event_cb(GtkWidget *widget _U_, GdkEvent *event _U_, gpointer data _U_) { gpointer dialog; if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) { #if GTK_MAJOR_VERSION >= 2 gtk_window_present(GTK_WINDOW(top_level)); #endif /* user didn't saved his current file, ask him */ dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE_CANCEL, PRIMARY_TEXT_START "Save capture file before program quit?" PRIMARY_TEXT_END "\n\n" "If you quit the program without saving, your capture data will be discarded."); simple_dialog_set_cb(dialog, file_quit_answered_cb, NULL); return TRUE; } else { /* unchanged file, just exit */ /* "main_do_quit()" indicates whether the main window should be deleted. */ return main_do_quit(); } } static void main_load_window_geometry(GtkWidget *widget) { window_geometry_t geom; geom.set_pos = prefs.gui_geometry_save_position; geom.x = recent.gui_geometry_main_x; geom.y = recent.gui_geometry_main_y; geom.set_size = prefs.gui_geometry_save_size; if (recent.gui_geometry_main_width > 0 && recent.gui_geometry_main_height > 0) { geom.width = recent.gui_geometry_main_width; geom.height = recent.gui_geometry_main_height; geom.set_maximized = prefs.gui_geometry_save_maximized; } else { /* We assume this means the width and height weren't set in the "recent" file (or that there is no "recent" file), and weren't set to a default value, so we don't set the size. (The "recent" file code rejects non-positive width and height values.) */ geom.set_size = FALSE; } geom.maximized = recent.gui_geometry_main_maximized; window_set_geometry(widget, &geom); if (recent.has_gui_geometry_main_upper_pane && recent.gui_geometry_main_upper_pane) gtk_paned_set_position(GTK_PANED(main_first_pane), recent.gui_geometry_main_upper_pane); if (recent.has_gui_geometry_main_lower_pane && recent.gui_geometry_main_lower_pane) gtk_paned_set_position(GTK_PANED(main_second_pane), recent.gui_geometry_main_lower_pane); if (recent.has_gui_geometry_main_lower_pane && recent.gui_geometry_status_pane) gtk_paned_set_position(GTK_PANED(status_pane), recent.gui_geometry_status_pane); } static void main_save_window_geometry(GtkWidget *widget) { window_geometry_t geom; window_get_geometry(widget, &geom); if (prefs.gui_geometry_save_position) { recent.gui_geometry_main_x = geom.x; recent.gui_geometry_main_y = geom.y; } if (prefs.gui_geometry_save_size) { recent.gui_geometry_main_width = geom.width, recent.gui_geometry_main_height = geom.height; } #if GTK_MAJOR_VERSION >= 2 if(prefs.gui_geometry_save_maximized) { recent.gui_geometry_main_maximized = geom.maximized; } recent.gui_geometry_main_upper_pane = gtk_paned_get_position(GTK_PANED(main_first_pane)); recent.gui_geometry_main_lower_pane = gtk_paned_get_position(GTK_PANED(main_second_pane)); recent.gui_geometry_status_pane = gtk_paned_get_position(GTK_PANED(status_pane)); #endif } static void file_quit_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_) { switch(btn) { case(ESD_BTN_SAVE): /* save file first */ file_save_as_cmd(after_save_exit, NULL); break; case(ESD_BTN_DONT_SAVE): main_do_quit(); break; case(ESD_BTN_CANCEL): break; default: g_assert_not_reached(); } } void file_quit_cmd_cb(GtkWidget *widget _U_, gpointer data _U_) { gpointer dialog; if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) { /* user didn't saved his current file, ask him */ dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE_CANCEL, PRIMARY_TEXT_START "Save capture file before program quit?" PRIMARY_TEXT_END "\n\n" "If you quit the program without saving, your capture data will be discarded."); simple_dialog_set_cb(dialog, file_quit_answered_cb, NULL); } else { /* unchanged file, just exit */ main_do_quit(); } } static void print_usage(gboolean print_ver) { FILE *output; if (print_ver) { output = stdout; fprintf(output, "This is GNU " PACKAGE " " VERSION #ifdef SVNVERSION " (" SVNVERSION ")" #endif "\n%s\n\n%s\n", comp_info_str->str, runtime_info_str->str); } else { output = stderr; } #ifdef HAVE_LIBPCAP fprintf(output, "\n%s [ -vh ] [ -klLnpQS ] [ -a ] ...\n", PACKAGE); fprintf(output, "\t[ -b ] ...]\n"); fprintf(output, "\t[ -B ] [ -c ] [ -f ]\n"); fprintf(output, "\t[ -i ] [ -m ] [ -N ]\n"); fprintf(output, "\t[ -o ] ... [ -P ]\n"); fprintf(output, "\t[ -r ] [ -R ] [ -s ] \n"); fprintf(output, "\t[ -t