/* main.c * * $Id: main.c,v 1.374 2004/01/25 18:51:25 ulfl 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 #ifdef HAVE_LIBPCAP #include #endif #include #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_IO_H #include /* open/close on win32 */ #endif #ifdef NEED_SNPRINTF_H # include "snprintf.h" #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 "cvsversion.h" #include "main.h" #include #include #include "capture.h" #include "summary.h" #include "file.h" #include "filters.h" #include "disabled_protos.h" #include "prefs.h" #include "menu.h" #include "../menu.h" #include "color.h" #include "color_filters.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 "dlg_utils.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 #include "ringbuffer.h" #include "../ui_util.h" #include "ui_util.h" #include "toolbar.h" #include "../tap.h" #include "../util.h" #include "../version_info.h" #include "compat_macros.h" #include "find_dlg.h" #include "packet_list.h" #include "recent.h" #include "follow_dlg.h" #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 *main_display_filter_widget=NULL; GtkWidget *top_level = NULL, *tree_view, *byte_nb_ptr, *tv_scrollw; GtkWidget *upper_pane, *lower_pane; GtkWidget *menubar, *main_vbox, *main_tb, *pkt_scrollw, *stat_hbox, *filter_tb; static GtkWidget *info_bar; #if GTK_MAJOR_VERSION < 2 GdkFont *m_r_font, *m_b_font; guint m_font_height, m_font_width; #else PangoFontDescription *m_r_font, *m_b_font; #endif static guint main_ctx, file_ctx, help_ctx; static GString *comp_info_str, *runtime_info_str; gchar *ethereal_path = NULL; gchar *last_open_dir = NULL; static gboolean updated_last_open_dir = FALSE; static gint root_x = G_MAXINT, root_y = G_MAXINT, top_width, top_height; static gboolean updated_geometry = FALSE; /* init with an invalid value, so that "recent" can detect this and */ /* distinguish it from a command line value */ ts_type timestamp_type = TS_NOT_SET; #if GTK_MAJOR_VERSION < 2 GtkStyle *item_style; #endif #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 #ifdef HAVE_LIBPCAP static gboolean list_link_layer_types; #endif static void create_main_window(gint, gint, gint, e_prefs*); #ifdef WIN32 #if GTK_MAJOR_VERSION >= 2 static void try_to_get_windows_font_gtk2 (void); #endif #endif #define E_DFILTER_CM_KEY "display_filter_combo" #define E_DFILTER_FL_KEY "display_filter_list" /* About Ethereal window */ #define MAX_ABOUT_MSG_LEN 2048 void about_ethereal( GtkWidget *w _U_, gpointer data _U_ ) { GtkWidget *win, *main_vb, *top_hb, *msg_label, *bbox, *ok_btn; gchar message[MAX_ABOUT_MSG_LEN]; /* * XXX - use GtkDialog? The GNOME 2.x GnomeAbout widget does. * Should we use GtkDialog for simple_dialog() as well? Or * is the GTK+ 2.x GtkDialog appropriate but the 1.2[.x] one * not? (The GNOME 1.x GnomeAbout widget uses GnomeDialog.) */ win = dlg_window_new("About Ethereal"); gtk_container_border_width(GTK_CONTAINER(win), 7); /* Container for our rows */ main_vb = gtk_vbox_new(FALSE, 5); gtk_container_border_width(GTK_CONTAINER(main_vb), 5); gtk_container_add(GTK_CONTAINER(win), main_vb); gtk_widget_show(main_vb); /* Top row: Message text */ top_hb = gtk_hbox_new(FALSE, 10); gtk_container_add(GTK_CONTAINER(main_vb), top_hb); gtk_widget_show(top_hb); /* Construct the message string */ snprintf(message, MAX_ABOUT_MSG_LEN, "Ethereal - Network Protocol Analyzer\n\n" "Version " VERSION #ifdef CVSVERSION " (cvs " CVSVERSION ")" #endif " (C) 1998-2004 Gerald Combs \n\n" "%s\n" "%s\n\n" "Ethereal is Open Source software released under the GNU General Public License.\n\n" "Check the man page for complete documentation and\n" "for the list of contributors.\n\n" "See http://www.ethereal.com for more information.", comp_info_str->str, runtime_info_str->str); msg_label = gtk_label_new(message); gtk_label_set_justify(GTK_LABEL(msg_label), GTK_JUSTIFY_FILL); gtk_container_add(GTK_CONTAINER(top_hb), msg_label); gtk_widget_show(msg_label); /* Button row */ bbox = dlg_button_row_new(GTK_STOCK_OK, NULL); gtk_container_add(GTK_CONTAINER(main_vb), bbox); gtk_widget_show(bbox); ok_btn = OBJECT_GET_DATA(bbox, GTK_STOCK_OK); SIGNAL_CONNECT_OBJECT(ok_btn, "clicked", gtk_widget_destroy, win); gtk_widget_grab_default(ok_btn); /* Catch the "key_press_event" signal in the window, so that we can catch the ESC key being pressed and act as if the "Cancel" button had been selected. */ dlg_set_cancel(win, ok_btn); gtk_widget_show(win); } #if GTK_MAJOR_VERSION < 2 void set_fonts(GdkFont *regular, GdkFont *bold) #else void set_fonts(PangoFontDescription *regular, PangoFontDescription *bold) #endif { /* 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; #if GTK_MAJOR_VERSION < 2 m_font_height = m_r_font->ascent + m_r_font->descent; m_font_width = gdk_string_width(m_r_font, "0"); #endif } /* * Go to frame specified by currently selected protocol tree item. */ void goto_framenum_cb(GtkWidget *w _U_, gpointer data _U_) { if (cfile.finfo_selected) { header_field_info *hfinfo; guint32 framenum; hfinfo = cfile.finfo_selected->hfinfo; g_assert(hfinfo); if (hfinfo->type == FT_FRAMENUM) { framenum = fvalue_get_integer(&cfile.finfo_selected->value); if (framenum != 0) goto_frame(&cfile, framenum); } } } void goto_top_frame_cb(GtkWidget *w _U_, gpointer d _U_) { goto_top_frame(&cfile); } void goto_bottom_frame_cb(GtkWidget *w _U_, gpointer d _U_) { goto_bottom_frame(&cfile); } void view_zoom_in_cb(GtkWidget *w _U_, gpointer d _U_) { gint save_gui_zoom_level; save_gui_zoom_level = recent.gui_zoom_level; recent.gui_zoom_level++; switch (font_apply()) { case FA_SUCCESS: break; case FA_FONT_NOT_RESIZEABLE: /* "font_apply()" popped up an alert box. */ recent.gui_zoom_level = save_gui_zoom_level; /* undo zoom */ break; case FA_FONT_NOT_AVAILABLE: /* We assume this means that the specified size isn't available. */ simple_dialog(ESD_TYPE_CRIT, NULL, "Your current font isn't available in the next larger size.\n"); recent.gui_zoom_level = save_gui_zoom_level; /* undo zoom */ break; } } void view_zoom_out_cb(GtkWidget *w _U_, gpointer d _U_) { gint save_gui_zoom_level; save_gui_zoom_level = recent.gui_zoom_level; recent.gui_zoom_level--; switch (font_apply()) { case FA_SUCCESS: break; case FA_FONT_NOT_RESIZEABLE: /* "font_apply()" popped up an alert box. */ recent.gui_zoom_level = save_gui_zoom_level; /* undo zoom */ break; case FA_FONT_NOT_AVAILABLE: /* We assume this means that the specified size isn't available. */ simple_dialog(ESD_TYPE_CRIT, NULL, "Your current font isn't available in the next smaller size.\n"); recent.gui_zoom_level = save_gui_zoom_level; /* undo zoom */ break; } } void view_zoom_100_cb(GtkWidget *w _U_, gpointer d _U_) { gint save_gui_zoom_level; save_gui_zoom_level = recent.gui_zoom_level; recent.gui_zoom_level = 0; switch (font_apply()) { case FA_SUCCESS: break; case FA_FONT_NOT_RESIZEABLE: /* "font_apply()" popped up an alert box. */ recent.gui_zoom_level = save_gui_zoom_level; /* undo zoom */ break; case FA_FONT_NOT_AVAILABLE: /* We assume this means that the specified size isn't available. XXX - this "shouldn't happen". */ simple_dialog(ESD_TYPE_CRIT, NULL, "Your current font couldn't be reloaded at the size you selected.\n"); recent.gui_zoom_level = save_gui_zoom_level; /* undo zoom */ break; } } /* 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); /* 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 (cfile.finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_REPLACE|MATCH_SELECTED_APPLY_NOW, proto_construct_dfilter_string(cfile.finfo_selected, cfile.edt)); } void match_selected_cb_and_ptree(GtkWidget *w, gpointer data) { if (cfile.finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_AND|MATCH_SELECTED_APPLY_NOW, proto_construct_dfilter_string(cfile.finfo_selected, cfile.edt)); } void match_selected_cb_or_ptree(GtkWidget *w, gpointer data) { if (cfile.finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_OR|MATCH_SELECTED_APPLY_NOW, proto_construct_dfilter_string(cfile.finfo_selected, cfile.edt)); } void match_selected_cb_not_ptree(GtkWidget *w, gpointer data) { if (cfile.finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_NOT|MATCH_SELECTED_APPLY_NOW, proto_construct_dfilter_string(cfile.finfo_selected, cfile.edt)); } void match_selected_cb_and_ptree_not(GtkWidget *w, gpointer data) { if (cfile.finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_AND_NOT|MATCH_SELECTED_APPLY_NOW, proto_construct_dfilter_string(cfile.finfo_selected, cfile.edt)); } void match_selected_cb_or_ptree_not(GtkWidget *w, gpointer data) { if (cfile.finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_OR_NOT, proto_construct_dfilter_string(cfile.finfo_selected, cfile.edt)); } void prepare_selected_cb_replace_ptree(GtkWidget *w, gpointer data) { if (cfile.finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_REPLACE, proto_construct_dfilter_string(cfile.finfo_selected, cfile.edt)); } void prepare_selected_cb_and_ptree(GtkWidget *w, gpointer data) { if (cfile.finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_AND, proto_construct_dfilter_string(cfile.finfo_selected, cfile.edt)); } void prepare_selected_cb_or_ptree(GtkWidget *w, gpointer data) { if (cfile.finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_OR, proto_construct_dfilter_string(cfile.finfo_selected, cfile.edt)); } void prepare_selected_cb_not_ptree(GtkWidget *w, gpointer data) { if (cfile.finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_NOT, proto_construct_dfilter_string(cfile.finfo_selected, cfile.edt)); } void prepare_selected_cb_and_ptree_not(GtkWidget *w, gpointer data) { if (cfile.finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_AND_NOT, proto_construct_dfilter_string(cfile.finfo_selected, cfile.edt)); } void prepare_selected_cb_or_ptree_not(GtkWidget *w, gpointer data) { if (cfile.finfo_selected) match_selected_cb_do((data ? data : w), MATCH_SELECTED_OR_NOT, proto_construct_dfilter_string(cfile.finfo_selected, cfile.edt)); } static gchar * get_text_from_packet_list(gpointer data) { gint row = (gint)OBJECT_GET_DATA(data, E_MPACKET_LIST_ROW_KEY); gint column = (gint)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; if (fdata != NULL) { if (!wtap_seek_read(cfile.wth, fdata->file_off, &cfile.pseudo_header, cfile.pd, fdata->cap_len, &err)) { simple_dialog(ESD_TYPE_CRIT, NULL, file_read_error_message(err), 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); 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)); } /* 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 *filter_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(filter_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) { filter_list = g_list_remove(filter_list, li->data); break; } li = li->next; } filter_list = g_list_append(filter_list, s); OBJECT_SET_DATA(filter_cm, E_DFILTER_FL_KEY, filter_list); gtk_combo_set_popdown_strings(GTK_COMBO(filter_cm), filter_list); gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(filter_cm)->entry), g_list_last(filter_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 *filter_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(filter_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 filter_packets() and add this filter string to the recent filter list */ int main_filter_packets(capture_file *cf, const gchar *dftext) { GtkCombo *filter_cm = OBJECT_GET_DATA(top_level, E_DFILTER_CM_KEY); GList *filter_list = OBJECT_GET_DATA(filter_cm, E_DFILTER_FL_KEY); GList *li; gboolean add_filter = TRUE; gboolean free_filter = TRUE; char *s; int filter_packets_ret; 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. */ if ((filter_packets_ret = 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) { /* trim list size first */ while (g_list_length(filter_list) >= dfilter_combo_max_recent) { filter_list = g_list_remove(filter_list, g_list_first(filter_list)->data); } free_filter = FALSE; filter_list = g_list_append(filter_list, s); OBJECT_SET_DATA(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); return filter_packets_ret; } /* Run the current display filter on the current packet set, and redisplay. */ static void filter_activate_cb(GtkWidget *w, gpointer data) { const char *s; s = gtk_entry_get_text(GTK_ENTRY(data)); main_filter_packets(&cfile, s); } /* 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), ""); } filter_packets(&cfile, NULL); } /* 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; } reftime_packets(&cfile); } /* 0: toggle ref time status for the selected frame * 1: find next ref time frame * 2: find previous reftime frame */ void reftime_frame_cb(GtkWidget *w _U_, gpointer data _U_, guint action) { switch(action){ case 0: /* toggle ref frame */ 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 1: /* find next ref frame */ find_previous_next_frame_with_filter("frame.ref_time", FALSE); break; case 2: /* find previous ref frame */ 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 */ 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 { 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(""); } } #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 */ 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 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) { 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) { gchar *rec_path; /* write user's recent file to disk * It is no problem to write this file, even if we do not quit */ write_recent(&rec_path); /* 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. "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_) { /* "do_quit()" indicates whether the main window should be deleted. */ return do_quit(); } static gboolean main_window_configure_event_cb(GtkWidget *widget, GdkEvent *event _U_, gpointer data _U_) { gint desk_x, desk_y; /* Try to grab our geometry. GTK+ provides two routines to get a window's position relative to the X root window. If I understand the documentation correctly, gdk_window_get_deskrelative_origin applies mainly to Enlightenment and gdk_window_get_root_origin applies for all other WMs. The code below tries both routines, and picks the one that returns the upper-left-most coordinates. More info at: http://mail.gnome.org/archives/gtk-devel-list/2001-March/msg00289.html http://www.gtk.org/faq/#AEN606 XXX - should we get this from the event itself? */ gdk_window_get_root_origin(widget->window, &root_x, &root_y); if (gdk_window_get_deskrelative_origin(widget->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(widget->window, &top_width, &top_height); updated_geometry = TRUE; return FALSE; } void file_quit_cmd_cb (GtkWidget *widget _U_, gpointer data _U_) { do_quit(); } static void print_usage(gboolean print_ver) { FILE *output; if (print_ver) { output = stdout; fprintf(output, "This is GNU " PACKAGE " " VERSION #ifdef CVSVERSION " (cvs " CVSVERSION ")" #endif "\n%s\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