/* main.c * * $Id: main.c,v 1.258 2002/08/24 01:17:08 guy Exp $ * * 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. * * * To do: * - Graphs * - Playback window * - Multiple window support * - Add cut/copy/paste * - Create header parsing routines * - Make byte view selections more fancy? */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_IO_H #include /* open/close on win32 */ #endif #ifdef HAVE_DIRECT_H #include #endif #include #ifdef HAVE_LIBPCAP #include #endif #ifdef HAVE_LIBZ #include /* to get the libz version number */ #endif #ifdef NEED_SNPRINTF_H # include "snprintf.h" #endif #ifdef HAVE_UCD_SNMP_VERSION_H #include #endif /* HAVE_UCD_SNMP_VERSION_H */ #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 "main.h" #include #include #include "capture.h" #include "summary.h" #include "file.h" #include "filters.h" #include "prefs.h" #include "menu.h" #include "../menu.h" #include "color.h" #include "color_utils.h" #include "filter_prefs.h" #include "file_dlg.h" #include "column.h" #include "print.h" #include #ifdef HAVE_LIBPCAP #include "pcap-util.h" #endif #include "statusbar.h" #include "simple_dialog.h" #include "proto_draw.h" #include #include "keys.h" #include "packet_win.h" #include "gtkglobals.h" #include #include "colors.h" #include #include "register.h" #include "ringbuffer.h" #include "ui_util.h" #include "image/clist_ascend.xpm" #include "image/clist_descend.xpm" #ifdef WIN32 #include "capture-wpcap.h" #endif typedef struct column_arrows { GtkWidget *table; GtkWidget *ascend_pm; GtkWidget *descend_pm; } column_arrows; capture_file cfile; GtkWidget *top_level, *packet_list, *tree_view, *byte_nb_ptr, *tv_scrollw, *pkt_scrollw; static GtkWidget *info_bar; GdkFont *m_r_font, *m_b_font; guint m_font_height, m_font_width; static guint main_ctx, file_ctx, help_ctx; static GString *comp_info_str; gchar *ethereal_path = NULL; gchar *last_open_dir = NULL; gint root_x = G_MAXINT, root_y = G_MAXINT, top_width, top_height; ts_type timestamp_type = RELATIVE; GtkStyle *item_style; /* Specifies the field currently selected in the GUI protocol tree */ field_info *finfo_selected = NULL; #ifdef WIN32 static gboolean has_no_console; /* TRUE if app has no console */ static gboolean console_was_created; /* TRUE if console was created */ static void create_console(void); static void destroy_console(void); static void console_log_handler(const char *log_domain, GLogLevelFlags log_level, const char *message, gpointer user_data); #endif static void create_main_window(gint, gint, gint, e_prefs*); #define E_DFILTER_CM_KEY "display_filter_combo" #define E_DFILTER_FL_KEY "display_filter_list" /* About Ethereal window */ void about_ethereal( GtkWidget *w _U_, gpointer data _U_ ) { simple_dialog(ESD_TYPE_INFO, NULL, "Ethereal - Network Protocol Analyzer\n" "Version " VERSION " (C) 1998-2000 Gerald Combs \n" "Compiled %s\n\n" "Check the man page for complete documentation and\n" "for the list of contributors.\n" "\nSee http://www.ethereal.com/ for more information.", comp_info_str->str); } void set_fonts(GdkFont *regular, GdkFont *bold) { /* Yes, assert. The code that loads the font should check * for NULL and provide its own error message. */ g_assert(m_r_font && m_b_font); m_r_font = regular; m_b_font = bold; m_font_height = m_r_font->ascent + m_r_font->descent; m_font_width = gdk_string_width(m_r_font, "0"); } /* 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 = gtk_object_get_data(GTK_OBJECT(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) filter_packets(&cfile, new_filter); /* Free up the new filter text. */ g_free(new_filter); /* Free up the generated text we were handed. */ g_free(text); } void match_selected_cb_replace_ptree(GtkWidget *w, gpointer data) { if (finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_REPLACE|MATCH_SELECTED_APPLY_NOW, proto_alloc_dfilter_string(finfo_selected, cfile.pd)); } void match_selected_cb_and_ptree(GtkWidget *w, gpointer data) { if (finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_AND|MATCH_SELECTED_APPLY_NOW, proto_alloc_dfilter_string(finfo_selected, cfile.pd)); } void match_selected_cb_or_ptree(GtkWidget *w, gpointer data) { if (finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_OR|MATCH_SELECTED_APPLY_NOW, proto_alloc_dfilter_string(finfo_selected, cfile.pd)); } void match_selected_cb_not_ptree(GtkWidget *w, gpointer data) { if (finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_NOT|MATCH_SELECTED_APPLY_NOW, proto_alloc_dfilter_string(finfo_selected, cfile.pd)); } void match_selected_cb_and_ptree_not(GtkWidget *w, gpointer data) { if (finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_AND_NOT|MATCH_SELECTED_APPLY_NOW, proto_alloc_dfilter_string(finfo_selected, cfile.pd)); } void match_selected_cb_or_ptree_not(GtkWidget *w, gpointer data) { if (finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_OR_NOT, proto_alloc_dfilter_string(finfo_selected, cfile.pd)); } void prepare_selected_cb_replace_ptree(GtkWidget *w, gpointer data) { if (finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_REPLACE, proto_alloc_dfilter_string(finfo_selected, cfile.pd)); } void prepare_selected_cb_and_ptree(GtkWidget *w, gpointer data) { if (finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_AND, proto_alloc_dfilter_string(finfo_selected, cfile.pd)); } void prepare_selected_cb_or_ptree(GtkWidget *w, gpointer data) { if (finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_OR, proto_alloc_dfilter_string(finfo_selected, cfile.pd)); } void prepare_selected_cb_not_ptree(GtkWidget *w, gpointer data) { if (finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_NOT, proto_alloc_dfilter_string(finfo_selected, cfile.pd)); } void prepare_selected_cb_and_ptree_not(GtkWidget *w, gpointer data) { if (finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_AND_NOT, proto_alloc_dfilter_string(finfo_selected, cfile.pd)); } void prepare_selected_cb_or_ptree_not(GtkWidget *w, gpointer data) { if (finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_OR_NOT, proto_alloc_dfilter_string(finfo_selected, cfile.pd)); } static gchar * get_text_from_packet_list(gpointer data) { gint row = (gint)gtk_object_get_data(GTK_OBJECT(data), E_MPACKET_LIST_ROW_KEY); gint column = (gint)gtk_object_get_data(GTK_OBJECT(data), E_MPACKET_LIST_COL_KEY); frame_data *fdata = (frame_data *)gtk_clist_get_row_data(GTK_CLIST(packet_list), row); epan_dissect_t *edt; gchar *buf=NULL; int len; int err; if (fdata != NULL) { /* XXX - do something with "err" */ wtap_seek_read(cfile.wth, fdata->file_off, &cfile.pseudo_header, cfile.pd, fdata->cap_len, &err); 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); 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_cb_replace_plist(GtkWidget *w _U_, gpointer data) { match_selected_cb_do(data, MATCH_SELECTED_REPLACE|MATCH_SELECTED_APPLY_NOW, get_text_from_packet_list(data)); } void match_selected_cb_and_plist(GtkWidget *w _U_, gpointer data) { match_selected_cb_do(data, MATCH_SELECTED_AND|MATCH_SELECTED_APPLY_NOW, get_text_from_packet_list(data)); } void match_selected_cb_or_plist(GtkWidget *w _U_, gpointer data) { match_selected_cb_do(data, MATCH_SELECTED_OR|MATCH_SELECTED_APPLY_NOW, get_text_from_packet_list(data)); } void match_selected_cb_not_plist(GtkWidget *w _U_, gpointer data) { match_selected_cb_do(data, MATCH_SELECTED_NOT|MATCH_SELECTED_APPLY_NOW, get_text_from_packet_list(data)); } void match_selected_cb_and_plist_not(GtkWidget *w _U_, gpointer data) { match_selected_cb_do(data, MATCH_SELECTED_AND_NOT|MATCH_SELECTED_APPLY_NOW, get_text_from_packet_list(data)); } void match_selected_cb_or_plist_not(GtkWidget *w _U_, gpointer data) { match_selected_cb_do(data, MATCH_SELECTED_OR_NOT|MATCH_SELECTED_APPLY_NOW, get_text_from_packet_list(data)); } void prepare_selected_cb_replace_plist(GtkWidget *w _U_, gpointer data) { match_selected_cb_do(data, MATCH_SELECTED_REPLACE, get_text_from_packet_list(data)); } void prepare_selected_cb_and_plist(GtkWidget *w _U_, gpointer data) { match_selected_cb_do(data, MATCH_SELECTED_AND, get_text_from_packet_list(data)); } void prepare_selected_cb_or_plist(GtkWidget *w _U_, gpointer data) { match_selected_cb_do(data, MATCH_SELECTED_OR, get_text_from_packet_list(data)); } void prepare_selected_cb_not_plist(GtkWidget *w _U_, gpointer data) { match_selected_cb_do(data, MATCH_SELECTED_NOT, get_text_from_packet_list(data)); } void prepare_selected_cb_and_plist_not(GtkWidget *w _U_, gpointer data) { match_selected_cb_do(data, MATCH_SELECTED_AND_NOT, get_text_from_packet_list(data)); } void prepare_selected_cb_or_plist_not(GtkWidget *w _U_, gpointer data) { match_selected_cb_do(data, MATCH_SELECTED_OR_NOT, get_text_from_packet_list(data)); } /* Run the current display filter on the current packet set, and redisplay. */ static void filter_activate_cb(GtkWidget *w, gpointer data) { GtkCombo *filter_cm = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_CM_KEY); GList *filter_list = gtk_object_get_data(GTK_OBJECT(filter_cm), E_DFILTER_FL_KEY); GList *li; gboolean add_filter = TRUE; gboolean free_filter = TRUE; char *s; g_assert(data); s = g_strdup(gtk_entry_get_text(GTK_ENTRY(data))); /* 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. */ if (filter_packets(&cfile, s)) { li = g_list_first(filter_list); while (li) { if (li->data && strcmp(s, li->data) == 0) add_filter = FALSE; li = li->next; } if (add_filter) { free_filter = FALSE; filter_list = g_list_append(filter_list, s); gtk_object_set_data(GTK_OBJECT(filter_cm), E_DFILTER_FL_KEY, filter_list); gtk_combo_set_popdown_strings(filter_cm, filter_list); gtk_entry_set_text(GTK_ENTRY(filter_cm->entry), g_list_last(filter_list)->data); } } if (free_filter) g_free(s); } /* redisplay with no display filter */ static void filter_reset_cb(GtkWidget *w, gpointer data _U_) { GtkWidget *filter_te = NULL; if ((filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY))) { gtk_entry_set_text(GTK_ENTRY(filter_te), ""); } filter_packets(&cfile, NULL); } /* GTKClist compare routine, overrides default to allow numeric comparison */ static gint packet_list_compare(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2) { /* Get row text strings */ char *text1 = GTK_CELL_TEXT (((GtkCListRow *)ptr1)->cell[clist->sort_column])->text; char *text2 = GTK_CELL_TEXT (((GtkCListRow *)ptr2)->cell[clist->sort_column])->text; /* Attempt to convert to numbers */ double num1 = atof(text1); double num2 = atof(text2); gint col_fmt = cfile.cinfo.col_fmt[clist->sort_column]; if ((col_fmt == COL_NUMBER) || (col_fmt == COL_REL_TIME) || (col_fmt == COL_DELTA_TIME) || ((col_fmt == COL_CLS_TIME) && (timestamp_type == RELATIVE)) || ((col_fmt == COL_CLS_TIME) && (timestamp_type == DELTA)) || (col_fmt == COL_UNRES_SRC_PORT) || (col_fmt == COL_UNRES_DST_PORT) || ((num1 != 0) && (num2 != 0) && ((col_fmt == COL_DEF_SRC_PORT) || (col_fmt == COL_RES_SRC_PORT) || (col_fmt == COL_DEF_DST_PORT) || (col_fmt == COL_RES_DST_PORT))) || (col_fmt == COL_PACKET_LENGTH)) { /* Compare numeric column */ if (num1 < num2) return -1; else if (num1 > num2) return 1; else return 0; } else { /* Compare text column */ if (!text2) return (text1 != NULL); if (!text1) return -1; return strcmp(text1, text2); } } /* What to do when a column is clicked */ static void packet_list_click_column_cb(GtkCList *clist, gint column, gpointer data) { column_arrows *col_arrows = (column_arrows *) data; int i; gtk_clist_freeze(clist); for (i = 0; i < cfile.cinfo.num_cols; i++) { gtk_widget_hide(col_arrows[i].ascend_pm); gtk_widget_hide(col_arrows[i].descend_pm); } if (column == clist->sort_column) { if (clist->sort_type == GTK_SORT_ASCENDING) { clist->sort_type = GTK_SORT_DESCENDING; gtk_widget_show(col_arrows[column].descend_pm); } else { clist->sort_type = GTK_SORT_ASCENDING; gtk_widget_show(col_arrows[column].ascend_pm); } } else { clist->sort_type = GTK_SORT_ASCENDING; gtk_widget_show(col_arrows[column].ascend_pm); gtk_clist_set_sort_column(clist, column); } gtk_clist_thaw(clist); gtk_clist_sort(clist); } /* mark packets */ static void set_frame_mark(gboolean set, frame_data *frame, gint row) { GdkColor fg, bg; if (row == -1) return; if (set) { mark_frame(&cfile, frame); color_t_to_gdkcolor(&fg, &prefs.gui_marked_fg); color_t_to_gdkcolor(&bg, &prefs.gui_marked_bg); } else { unmark_frame(&cfile, frame); fg = BLACK; bg = WHITE; } file_set_save_marked_sensitive(); gtk_clist_set_background(GTK_CLIST(packet_list), row, &bg); gtk_clist_set_foreground(GTK_CLIST(packet_list), row, &fg); } static void packet_list_button_pressed_cb(GtkWidget *w, GdkEvent *event, gpointer data _U_) { GdkEventButton *event_button = (GdkEventButton *)event; gint row, column; if (w == NULL || event == NULL) return; if (event->type == GDK_BUTTON_PRESS && event_button->button == 2 && gtk_clist_get_selection_info(GTK_CLIST(w), event_button->x, event_button->y, &row, &column)) { frame_data *fdata = (frame_data *) gtk_clist_get_row_data(GTK_CLIST(w), row); set_frame_mark(!fdata->flags.marked, fdata, row); } } void mark_frame_cb(GtkWidget *w _U_, gpointer data _U_) { if (cfile.current_frame) { /* XXX hum, should better have a "cfile->current_row" here ... */ set_frame_mark(!cfile.current_frame->flags.marked, cfile.current_frame, gtk_clist_find_row_from_data(GTK_CLIST(packet_list), cfile.current_frame)); } } static void mark_all_frames(gboolean set) { frame_data *fdata; for (fdata = cfile.plist; fdata != NULL; fdata = fdata->next) { set_frame_mark(set, fdata, gtk_clist_find_row_from_data(GTK_CLIST(packet_list), fdata)); } } void update_marked_frames(void) { frame_data *fdata; if (cfile.plist == NULL) return; for (fdata = cfile.plist; fdata != NULL; fdata = fdata->next) { if (fdata->flags.marked) set_frame_mark(TRUE, fdata, gtk_clist_find_row_from_data(GTK_CLIST(packet_list), fdata)); } } void mark_all_frames_cb(GtkWidget *w _U_, gpointer data _U_) { mark_all_frames(TRUE); } void unmark_all_frames_cb(GtkWidget *w _U_, gpointer data _U_) { mark_all_frames(FALSE); } /* What to do when a list item is selected/unselected */ static void packet_list_select_cb(GtkWidget *w _U_, gint row, gint col _U_, gpointer evt _U_) { /* Remove the hex display tabbed pages */ while( (gtk_notebook_get_nth_page( GTK_NOTEBOOK(byte_nb_ptr), 0))) gtk_notebook_remove_page( GTK_NOTEBOOK(byte_nb_ptr), 0); select_packet(&cfile, row); } static void packet_list_unselect_cb(GtkWidget *w _U_, gint row _U_, gint col _U_, gpointer evt _U_) { unselect_packet(&cfile); } static void tree_view_select_row_cb(GtkCTree *ctree, GList *node, gint column _U_, gpointer user_data _U_) { 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; g_assert(node); finfo = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(node) ); 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); finfo_selected = finfo; set_menus_for_selected_tree_row(TRUE); 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 { snprintf (len_str, sizeof len_str, ", %d bytes", finfo->length); } statusbar_pop_field_msg(); /* get rid of current help msg */ if (length) { length += strlen(finfo->hfinfo->abbrev) + strlen(len_str) + 10; help_str = g_malloc(sizeof(gchar) * length); sprintf(help_str, "%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(""); } } packet_hex_print(GTK_TEXT(byte_view), byte_data, cfile.current_frame, finfo, byte_len); } 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 */ unselect_field(); packet_hex_print(GTK_TEXT(byte_view), data, cfile.current_frame, NULL, len); } 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 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; } } /* Set the selection mode of the packet list window. */ void set_plist_sel_browse(gboolean val) { gboolean old_val; old_val = (GTK_CLIST(packet_list)->selection_mode == GTK_SELECTION_SINGLE); if (val == old_val) { /* * The mode isn't changing, so don't do anything. * In particular, don't gratuitiously unselect the * current packet. * * XXX - why do we have to unselect the current packet * ourselves? The documentation for the GtkCList at * * http://developer.gnome.org/doc/API/gtk/gtkclist.html * * says "Note that setting the widget's selection mode to * one of GTK_SELECTION_BROWSE or GTK_SELECTION_SINGLE will * cause all the items in the GtkCList to become deselected." */ return; } if (finfo_selected) unselect_packet(&cfile); /* Yeah, GTK uses "browse" in the case where we do not, but oh well. I think * "browse" in Ethereal makes more sense than "SINGLE" in GTK+ */ if (val) { gtk_clist_set_selection_mode(GTK_CLIST(packet_list), GTK_SELECTION_SINGLE); } else { gtk_clist_set_selection_mode(GTK_CLIST(packet_list), GTK_SELECTION_BROWSE); } } /* Set the font of the packet list window. */ void set_plist_font(GdkFont *font) { GtkStyle *style; int i; style = gtk_style_new(); gdk_font_unref(style->font); style->font = font; gdk_font_ref(font); gtk_widget_set_style(packet_list, style); /* Compute static column sizes to use during a "-S" capture, so that the columns don't resize during a live capture. */ for (i = 0; i < cfile.cinfo.num_cols; i++) { cfile.cinfo.col_width[i] = gdk_string_width(font, get_column_longest_string(get_column_format(i))); } } /* * Push a message referring to file access onto the statusbar. */ void statusbar_push_file_msg(gchar *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) { 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); } static gboolean do_quit(void) { /* 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. */ kill_capture_child(); #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. "close_cap_file()" 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 "close_cap_file()" 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? */ close_cap_file(&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_) { gint desk_x, desk_y; /* Try to grab our geometry */ gdk_window_get_root_origin(top_level->window, &root_x, &root_y); if (gdk_window_get_deskrelative_origin(top_level->window, &desk_x, &desk_y)) { if (desk_x <= root_x && desk_y <= root_y) { root_x = desk_x; root_y = desk_y; } } /* XXX - Is this the "approved" method? */ gdk_window_get_size(top_level->window, &top_width, &top_height); /* "do_quit()" indicates whether the main window should be deleted. */ return do_quit(); } void file_quit_cmd_cb (GtkWidget *widget _U_, gpointer data _U_) { do_quit(); } static void print_usage(gboolean print_ver) { if (print_ver) { fprintf(stderr, "This is GNU " PACKAGE " " VERSION ", compiled %s\n", comp_info_str->str); } #ifdef HAVE_LIBPCAP fprintf(stderr, "\n%s [ -vh ] [ -klpQS ] [ -a ] ...\n", PACKAGE); fprintf(stderr, "\t[ -b ] [ -B ]\n"); fprintf(stderr, "\t[ -c ] [ -f ] [ -i ]\n"); fprintf(stderr, "\t[ -m ] [ -n ] [ -N ]\n"); fprintf(stderr, "\t[ -o ] ... [ -P ]\n"); fprintf(stderr, "\t[ -r ] [ -R ] [ -s ] \n"); fprintf(stderr, "\t[ -t