1859 lines
58 KiB
C
1859 lines
58 KiB
C
/* main.c
|
|
*
|
|
* $Id: main.c,v 1.128 2000/07/05 09:41:04 guy Exp $
|
|
*
|
|
* Ethereal - Network traffic analyzer
|
|
* By Gerald Combs <gerald@zing.org>
|
|
* Copyright 1998 Gerald Combs
|
|
*
|
|
* Richard Sharpe, 13-Feb-1999, added support for initializing structures
|
|
* needed by dissect routines
|
|
*
|
|
* 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
|
|
* - Check for end of packet in dissect_* routines.
|
|
* - 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 <gtk/gtk.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#ifdef HAVE_IO_H
|
|
#include <io.h> /* open/close on win32 */
|
|
#endif
|
|
|
|
#ifdef HAVE_DIRECT_H
|
|
#include <direct.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_NETINET_IN_H
|
|
#include <netinet/in.h>
|
|
#endif
|
|
|
|
#include <signal.h>
|
|
|
|
#ifdef NEED_SNPRINTF_H
|
|
# ifdef HAVE_STDARG_H
|
|
# include <stdarg.h>
|
|
# else
|
|
# include <varargs.h>
|
|
# endif
|
|
# include "snprintf.h"
|
|
#endif
|
|
|
|
#if defined(HAVE_UCD_SNMP_SNMP_H)
|
|
#ifdef HAVE_UCD_SNMP_VERSION_H
|
|
#include <ucd-snmp/version.h>
|
|
#endif /* HAVE_UCD_SNMP_VERSION_H */
|
|
#elif defined(HAVE_SNMP_SNMP_H)
|
|
#ifdef HAVE_SNMP_VERSION_H
|
|
#include <snmp/version.h>
|
|
#endif /* HAVE_SNMP_VERSION_H */
|
|
#endif /* SNMP */
|
|
|
|
#ifdef NEED_STRERROR_H
|
|
#include "strerror.h"
|
|
#endif
|
|
|
|
#ifdef NEED_GETOPT_H
|
|
#include "getopt.h"
|
|
#endif
|
|
|
|
#include "main.h"
|
|
#include "timestamp.h"
|
|
#include "packet.h"
|
|
#include "capture.h"
|
|
#include "summary.h"
|
|
#include "file.h"
|
|
#include "menu.h"
|
|
#include "../menu.h"
|
|
#include "filter_prefs.h"
|
|
#include "prefs_dlg.h"
|
|
#include "column.h"
|
|
#include "print.h"
|
|
#include "resolv.h"
|
|
#include "follow.h"
|
|
#include "util.h"
|
|
#include "simple_dialog.h"
|
|
#include "proto_draw.h"
|
|
#include "dfilter.h"
|
|
#include "keys.h"
|
|
#include "packet_win.h"
|
|
#include "gtkglobals.h"
|
|
#include "plugins.h"
|
|
|
|
FILE *data_out_file = NULL;
|
|
packet_info pi;
|
|
capture_file cfile;
|
|
GtkWidget *top_level, *packet_list, *tree_view, *byte_view,
|
|
*info_bar, *tv_scrollw, *pkt_scrollw;
|
|
static GtkWidget *bv_scrollw;
|
|
GdkFont *m_r_font, *m_b_font;
|
|
guint main_ctx, file_ctx;
|
|
gchar comp_info_str[256];
|
|
gchar *ethereal_path = NULL;
|
|
gchar *medium_font = MONO_MEDIUM_FONT;
|
|
gchar *bold_font = MONO_BOLD_FONT;
|
|
gchar *last_open_dir = NULL;
|
|
|
|
ts_type timestamp_type = RELATIVE;
|
|
|
|
GtkStyle *item_style;
|
|
|
|
/* Specifies the field currently selected in the GUI protocol tree */
|
|
field_info *finfo_selected = NULL;
|
|
|
|
static void follow_destroy_cb(GtkWidget *win, gpointer data);
|
|
static void follow_charset_toggle_cb(GtkWidget *w, gpointer parent_w);
|
|
static void follow_load_text(GtkWidget *text, char *filename, guint8 show_type);
|
|
static void follow_print_stream(GtkWidget *w, gpointer parent_w);
|
|
static char* hfinfo_numeric_format(header_field_info *hfinfo);
|
|
static void create_main_window(gint, gint, gint, e_prefs*);
|
|
|
|
/* About Ethereal window */
|
|
void
|
|
about_ethereal( GtkWidget *w, gpointer data ) {
|
|
simple_dialog(ESD_TYPE_INFO, NULL,
|
|
"Ethereal - Network Protocol Analyzer\n"
|
|
"Version " VERSION " (C) 1998-2000 Gerald Combs <gerald@zing.org>\n"
|
|
"Compiled with %s\n\n"
|
|
|
|
"Check the man page for complete documentation and\n"
|
|
"for the list of contributors.\n"
|
|
|
|
"\nSee http://ethereal.zing.org/ for more information.",
|
|
comp_info_str);
|
|
}
|
|
|
|
/* Follow the TCP stream, if any, to which the last packet that we called
|
|
a dissection routine on belongs (this might be the most recently
|
|
selected packet, or it might be the last packet in the file). */
|
|
void
|
|
follow_stream_cb( GtkWidget *w, gpointer data ) {
|
|
char filename1[128+1];
|
|
GtkWidget *streamwindow, *box, *txt_scrollw, *text, *filter_te;
|
|
GtkWidget *hbox, *close_bt, *print_bt;
|
|
GtkWidget *b_ascii, *b_ebcdic, *b_hexdump;
|
|
int tmp_fd;
|
|
gchar *follow_filter;
|
|
|
|
if( pi.ipproto == 6 ) {
|
|
/* we got tcp so we can follow */
|
|
/* Create a temporary file into which to dump the reassembled data
|
|
from the TCP stream, and set "data_out_file" to refer to it, so
|
|
that the TCP code will write to it.
|
|
|
|
XXX - it might be nicer to just have the TCP code directly
|
|
append stuff to the text widget for the TCP stream window,
|
|
if we can arrange that said window not pop up until we're
|
|
done. */
|
|
tmp_fd = create_tempfile( filename1, sizeof filename1, "follow");
|
|
if (tmp_fd == -1) {
|
|
simple_dialog(ESD_TYPE_WARN, NULL,
|
|
"Could not create temporary file %s: %s", filename1, strerror(errno));
|
|
return;
|
|
}
|
|
data_out_file = fdopen( tmp_fd, "wb" );
|
|
if( data_out_file == NULL ) {
|
|
simple_dialog(ESD_TYPE_WARN, NULL,
|
|
"Could not create temporary file %s: %s", filename1, strerror(errno));
|
|
close(tmp_fd);
|
|
unlink(filename1);
|
|
return;
|
|
}
|
|
|
|
/* Create a new filter that matches all packets in the TCP stream,
|
|
and set the display filter entry accordingly */
|
|
reset_tcp_reassembly();
|
|
follow_filter = build_follow_filter( &pi );
|
|
|
|
/* set the display filter entry accordingly */
|
|
filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
|
|
gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter);
|
|
|
|
/* Run the display filter so it goes in effect. */
|
|
filter_packets(&cfile, follow_filter);
|
|
|
|
/* the data_out_file should now be full of the streams information */
|
|
fclose( data_out_file );
|
|
|
|
/* the filename1 file now has all the text that was in the session */
|
|
streamwindow = gtk_window_new( GTK_WINDOW_TOPLEVEL);
|
|
gtk_widget_set_name( streamwindow, "TCP stream window" );
|
|
|
|
gtk_signal_connect( GTK_OBJECT(streamwindow), "destroy",
|
|
GTK_SIGNAL_FUNC(follow_destroy_cb), NULL);
|
|
|
|
if( incomplete_tcp_stream ) {
|
|
gtk_window_set_title( GTK_WINDOW(streamwindow),
|
|
"Contents of TCP stream (incomplete)" );
|
|
} else {
|
|
gtk_window_set_title( GTK_WINDOW(streamwindow),
|
|
"Contents of TCP stream" );
|
|
}
|
|
gtk_widget_set_usize( GTK_WIDGET(streamwindow), DEF_WIDTH, DEF_HEIGHT );
|
|
gtk_container_border_width( GTK_CONTAINER(streamwindow), 2 );
|
|
|
|
/* setup the container */
|
|
box = gtk_vbox_new( FALSE, 0 );
|
|
gtk_container_add( GTK_CONTAINER(streamwindow), box );
|
|
gtk_widget_show( box );
|
|
|
|
/* create a scrolled window for the text */
|
|
txt_scrollw = gtk_scrolled_window_new( NULL, NULL );
|
|
gtk_box_pack_start( GTK_BOX(box), txt_scrollw, TRUE, TRUE, 0 );
|
|
gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(txt_scrollw),
|
|
GTK_POLICY_NEVER,
|
|
GTK_POLICY_ALWAYS );
|
|
set_scrollbar_placement_scrollw(txt_scrollw, prefs.gui_scrollbar_on_right);
|
|
remember_scrolled_window(txt_scrollw);
|
|
gtk_widget_show( txt_scrollw );
|
|
|
|
/* create a text box */
|
|
text = gtk_text_new( NULL, NULL );
|
|
gtk_text_set_editable( GTK_TEXT(text), FALSE);
|
|
gtk_container_add( GTK_CONTAINER(txt_scrollw), text );
|
|
gtk_widget_show(text);
|
|
|
|
/* Create hbox */
|
|
hbox = gtk_hbox_new( FALSE, 1 );
|
|
gtk_box_pack_end( GTK_BOX(box), hbox, FALSE, FALSE, 0);
|
|
gtk_widget_show(hbox);
|
|
|
|
#define E_FOLLOW_ASCII_KEY "follow_ascii_key"
|
|
#define E_FOLLOW_EBCDIC_KEY "follow_ebcdic_key"
|
|
#define E_FOLLOW_HEXDUMP_KEY "follow_hexdump_key"
|
|
|
|
/* Create Radio Buttons */
|
|
b_ascii = gtk_radio_button_new_with_label(NULL, "ASCII");
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b_ascii), TRUE);
|
|
gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_ASCII_KEY, b_ascii);
|
|
gtk_box_pack_start(GTK_BOX(hbox), b_ascii, FALSE, FALSE, 0);
|
|
gtk_signal_connect(GTK_OBJECT(b_ascii), "toggled",
|
|
GTK_SIGNAL_FUNC(follow_charset_toggle_cb),
|
|
GTK_OBJECT(streamwindow));
|
|
gtk_widget_show(b_ascii);
|
|
|
|
b_ebcdic = gtk_radio_button_new_with_label(
|
|
gtk_radio_button_group(GTK_RADIO_BUTTON(b_ascii)),
|
|
"EBCDIC");
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b_ebcdic), FALSE);
|
|
gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_EBCDIC_KEY, b_ebcdic);
|
|
gtk_box_pack_start(GTK_BOX(hbox), b_ebcdic, FALSE, FALSE, 0);
|
|
gtk_signal_connect(GTK_OBJECT(b_ebcdic), "toggled",
|
|
GTK_SIGNAL_FUNC(follow_charset_toggle_cb),
|
|
GTK_OBJECT(streamwindow));
|
|
gtk_widget_show(b_ebcdic);
|
|
|
|
b_hexdump = gtk_radio_button_new_with_label(
|
|
gtk_radio_button_group(GTK_RADIO_BUTTON(b_ascii)),
|
|
"Hex. Dump");
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(b_hexdump), FALSE);
|
|
gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_HEXDUMP_KEY, b_hexdump);
|
|
gtk_box_pack_start(GTK_BOX(hbox), b_hexdump, FALSE, FALSE, 0);
|
|
gtk_signal_connect(GTK_OBJECT(b_hexdump), "toggled",
|
|
GTK_SIGNAL_FUNC(follow_charset_toggle_cb),
|
|
GTK_OBJECT(streamwindow));
|
|
gtk_widget_show(b_hexdump);
|
|
|
|
/* Create Close Button */
|
|
close_bt = gtk_button_new_with_label("Close");
|
|
gtk_signal_connect_object(GTK_OBJECT(close_bt), "clicked",
|
|
GTK_SIGNAL_FUNC(gtk_widget_destroy),
|
|
GTK_OBJECT(streamwindow));
|
|
gtk_box_pack_end( GTK_BOX(hbox), close_bt, FALSE, FALSE, 0);
|
|
gtk_widget_show( close_bt );
|
|
|
|
/* Create Print Button */
|
|
print_bt = gtk_button_new_with_label("Print");
|
|
gtk_signal_connect(GTK_OBJECT(print_bt), "clicked",
|
|
GTK_SIGNAL_FUNC(follow_print_stream),
|
|
GTK_OBJECT(streamwindow));
|
|
gtk_box_pack_end( GTK_BOX(hbox), print_bt, FALSE, FALSE, 0);
|
|
gtk_widget_show( print_bt );
|
|
|
|
/* Tuck away the filename and textbox into streamwindow */
|
|
#define E_FOLLOW_FILENAME_KEY "follow_filename_key"
|
|
#define E_FOLLOW_TEXT_KEY "follow_text_key"
|
|
|
|
gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_FILENAME_KEY,
|
|
g_strdup(filename1));
|
|
gtk_object_set_data(GTK_OBJECT(streamwindow), E_FOLLOW_TEXT_KEY, text);
|
|
|
|
follow_load_text(text, filename1, 0);
|
|
|
|
data_out_file = NULL;
|
|
|
|
/* Make sure this widget gets destroyed if we quit the main loop,
|
|
so that if we exit, we clean up any temporary files we have
|
|
for "Follow TCP Stream" windows. */
|
|
gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
|
|
gtk_widget_show( streamwindow );
|
|
} else {
|
|
simple_dialog(ESD_TYPE_WARN, NULL,
|
|
"Error following stream. Please make\n"
|
|
"sure you have a TCP packet selected.");
|
|
}
|
|
}
|
|
|
|
/* The destroy call back has the responsibility of
|
|
* unlinking the temporary file */
|
|
static void
|
|
follow_destroy_cb(GtkWidget *win, gpointer data)
|
|
{
|
|
char *filename;
|
|
|
|
filename = (char*) gtk_object_get_data(GTK_OBJECT(win),
|
|
E_FOLLOW_FILENAME_KEY);
|
|
g_assert(filename);
|
|
|
|
unlink(filename);
|
|
gtk_widget_destroy(win);
|
|
}
|
|
|
|
#define E_FOLLOW_ASCII_TYPE 0
|
|
#define E_FOLLOW_EBCDIC_TYPE 1
|
|
#define E_FOLLOW_HEXDUMP_TYPE 2
|
|
|
|
/* Handles the ASCII/EBCDIC toggling */
|
|
static void
|
|
follow_charset_toggle_cb(GtkWidget *w, gpointer parent_w)
|
|
{
|
|
guint8 show_type = E_FOLLOW_ASCII_TYPE;
|
|
GtkWidget *b_ascii, *b_ebcdic, *b_hexdump, *text;
|
|
char *filename;
|
|
|
|
b_ascii = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w),
|
|
E_FOLLOW_ASCII_KEY);
|
|
b_ebcdic = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w),
|
|
E_FOLLOW_EBCDIC_KEY);
|
|
b_hexdump = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w),
|
|
E_FOLLOW_HEXDUMP_KEY);
|
|
text = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w),
|
|
E_FOLLOW_TEXT_KEY);
|
|
filename = (char*) gtk_object_get_data(GTK_OBJECT(parent_w),
|
|
E_FOLLOW_FILENAME_KEY);
|
|
|
|
g_assert(b_ascii);
|
|
g_assert(b_ebcdic);
|
|
g_assert(b_hexdump);
|
|
g_assert(text);
|
|
g_assert(filename);
|
|
|
|
if (GTK_TOGGLE_BUTTON(b_ebcdic)->active)
|
|
show_type = E_FOLLOW_EBCDIC_TYPE;
|
|
else if (GTK_TOGGLE_BUTTON(b_hexdump)->active)
|
|
show_type = E_FOLLOW_HEXDUMP_TYPE;
|
|
|
|
follow_load_text(text, filename, show_type);
|
|
}
|
|
|
|
#define FLT_BUF_SIZE 1024
|
|
static void
|
|
follow_read_stream(char *filename, guint8 show_type,
|
|
void (*print_line)(char *, int, gboolean, void *), void *arg)
|
|
{
|
|
tcp_stream_chunk sc;
|
|
int bcount;
|
|
guint32 client_addr = 0;
|
|
guint16 client_port = 0;
|
|
gboolean is_server;
|
|
guint16 current_pos, global_client_pos = 0, global_server_pos = 0;
|
|
guint16 *global_pos;
|
|
|
|
data_out_file = fopen( filename, "rb" );
|
|
if( data_out_file ) {
|
|
char buffer[FLT_BUF_SIZE];
|
|
int nchars;
|
|
while(fread(&sc.src_addr, 1, sizeof(sc), data_out_file)) {
|
|
if (client_addr == 0) {
|
|
client_addr = sc.src_addr;
|
|
client_port = sc.src_port;
|
|
}
|
|
if (client_addr == sc.src_addr && client_port == sc.src_port) {
|
|
is_server = FALSE;
|
|
global_pos = &global_client_pos;
|
|
}
|
|
else {
|
|
is_server = TRUE;
|
|
global_pos = &global_server_pos;
|
|
}
|
|
|
|
while (sc.dlen > 0) {
|
|
bcount = (sc.dlen < FLT_BUF_SIZE) ? sc.dlen : FLT_BUF_SIZE;
|
|
nchars = fread( buffer, 1, bcount, data_out_file );
|
|
if (nchars == 0)
|
|
break;
|
|
sc.dlen -= bcount;
|
|
switch (show_type) {
|
|
case E_FOLLOW_EBCDIC_TYPE:
|
|
/* If our native arch is ASCII, call: */
|
|
EBCDIC_to_ASCII(buffer, nchars);
|
|
case E_FOLLOW_ASCII_TYPE:
|
|
/* If our native arch is EBCDIC, call:
|
|
* ASCII_TO_EBCDIC(buffer, nchars);
|
|
*/
|
|
(*print_line)( buffer, nchars, is_server, arg );
|
|
break;
|
|
case E_FOLLOW_HEXDUMP_TYPE:
|
|
current_pos = 0;
|
|
while (current_pos < nchars)
|
|
{
|
|
gchar hexbuf[256];
|
|
gchar hexchars[] = "0123456789abcdef";
|
|
int i, cur;
|
|
/* is_server indentation : put 63 spaces at the begenning
|
|
* of the string */
|
|
sprintf(hexbuf, is_server ?
|
|
" "
|
|
" %08X " :
|
|
"%08X ", *global_pos);
|
|
cur = strlen(hexbuf);
|
|
for (i=0; i < 16 && current_pos+i < nchars; i++) {
|
|
hexbuf[cur++] = hexchars[(buffer[current_pos+i] & 0xf0) >> 4];
|
|
hexbuf[cur++] = hexchars[buffer[current_pos+i] & 0x0f];
|
|
if (i == 7) {
|
|
hexbuf[cur++] = ' '; hexbuf[cur++] = ' ';
|
|
}
|
|
else if (i != 15)
|
|
hexbuf[cur++] = ' ';
|
|
}
|
|
current_pos += i;
|
|
(*global_pos) += i;
|
|
hexbuf[cur++] = '\n';
|
|
hexbuf[cur] = 0;
|
|
(*print_line)( hexbuf, strlen(hexbuf), is_server, arg );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if( ferror( data_out_file ) ) {
|
|
simple_dialog(ESD_TYPE_WARN, NULL,
|
|
"Error reading temporary file %s: %s", filename, strerror(errno));
|
|
}
|
|
fclose( data_out_file );
|
|
data_out_file = NULL;
|
|
} else {
|
|
simple_dialog(ESD_TYPE_WARN, NULL,
|
|
"Could not open temporary file %s: %s", filename, strerror(errno));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* XXX - for text printing, we probably want to wrap lines at 80 characters;
|
|
* for PostScript printing, we probably want to wrap them at the appropriate
|
|
* width, and perhaps put some kind of dingbat (to use the technical term)
|
|
* to indicate a wrapped line, along the lines of what's done when displaying
|
|
* this in a window, as per Warren Young's suggestion.
|
|
*
|
|
* For now, we support only text printing.
|
|
*/
|
|
static void
|
|
follow_print_text(char *buffer, int nchars, gboolean is_server, void *arg)
|
|
{
|
|
FILE *fh = arg;
|
|
|
|
fwrite(buffer, nchars, 1, fh);
|
|
}
|
|
|
|
static void
|
|
follow_print_stream(GtkWidget *w, gpointer parent_w)
|
|
{
|
|
FILE *fh;
|
|
gboolean to_file;
|
|
char* print_dest;
|
|
char* filename;
|
|
guint8 show_type = E_FOLLOW_ASCII_TYPE;
|
|
GtkWidget *button;
|
|
|
|
switch (prefs.pr_dest) {
|
|
case PR_DEST_CMD:
|
|
print_dest = prefs.pr_cmd;
|
|
to_file = FALSE;
|
|
break;
|
|
|
|
case PR_DEST_FILE:
|
|
print_dest = prefs.pr_file;
|
|
to_file = TRUE;
|
|
break;
|
|
default: /* "Can't happen" */
|
|
simple_dialog(ESD_TYPE_WARN, NULL,
|
|
"Couldn't figure out where to send the print "
|
|
"job. Check your preferences.");
|
|
return;
|
|
}
|
|
|
|
fh = open_print_dest(to_file, print_dest);
|
|
if (fh == NULL) {
|
|
switch (to_file) {
|
|
case FALSE:
|
|
simple_dialog(ESD_TYPE_WARN, NULL,
|
|
"Couldn't run print command %s.", prefs.pr_cmd);
|
|
break;
|
|
|
|
case TRUE:
|
|
simple_dialog(ESD_TYPE_WARN, NULL,
|
|
file_write_error_message(errno),
|
|
prefs.pr_file);
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
button = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w),
|
|
E_FOLLOW_EBCDIC_KEY);
|
|
if (GTK_TOGGLE_BUTTON(button)->active)
|
|
show_type = E_FOLLOW_EBCDIC_TYPE;
|
|
button = (GtkWidget*) gtk_object_get_data(GTK_OBJECT(parent_w),
|
|
E_FOLLOW_HEXDUMP_KEY);
|
|
if (GTK_TOGGLE_BUTTON(button)->active)
|
|
show_type = E_FOLLOW_HEXDUMP_TYPE;
|
|
|
|
filename = (char*) gtk_object_get_data(GTK_OBJECT(parent_w),
|
|
E_FOLLOW_FILENAME_KEY);
|
|
|
|
if (filename != NULL) {
|
|
print_preamble(fh, PR_FMT_TEXT);
|
|
follow_read_stream(filename, show_type, follow_print_text, fh);
|
|
print_finale(fh, PR_FMT_TEXT);
|
|
close_print_dest(to_file, fh);
|
|
}
|
|
else {
|
|
simple_dialog(ESD_TYPE_WARN, NULL, "Could not find data to print.");
|
|
}
|
|
}
|
|
|
|
static void
|
|
follow_add_to_gtk_text(char *buffer, int nchars, gboolean is_server, void *arg)
|
|
{
|
|
GtkWidget *text = arg;
|
|
|
|
if (is_server)
|
|
gtk_text_insert( GTK_TEXT(text), m_r_font, &prefs.st_server_fg,
|
|
&prefs.st_server_bg, buffer, nchars );
|
|
else
|
|
gtk_text_insert( GTK_TEXT(text), m_r_font, &prefs.st_client_fg,
|
|
&prefs.st_client_bg, buffer, nchars );
|
|
}
|
|
|
|
static void
|
|
follow_load_text(GtkWidget *text, char *filename, guint8 show_type)
|
|
{
|
|
int bytes_already;
|
|
|
|
/* Delete any info already in text box */
|
|
bytes_already = gtk_text_get_length(GTK_TEXT(text));
|
|
if (bytes_already > 0) {
|
|
gtk_text_set_point(GTK_TEXT(text), 0);
|
|
gtk_text_forward_delete(GTK_TEXT(text), bytes_already);
|
|
}
|
|
|
|
/* stop the updates while we fill the text box */
|
|
gtk_text_freeze( GTK_TEXT(text) );
|
|
follow_read_stream(filename, show_type, follow_add_to_gtk_text, text);
|
|
gtk_text_thaw( GTK_TEXT(text) );
|
|
}
|
|
|
|
/* Match selected byte pattern */
|
|
void
|
|
match_selected_cb(GtkWidget *w, gpointer data)
|
|
{
|
|
char *buf;
|
|
GtkWidget *filter_te;
|
|
char *ptr, *format, *stringified;
|
|
int i, dfilter_len, abbrev_len;
|
|
guint8 *c;
|
|
header_field_info *hfinfo;
|
|
|
|
filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
|
|
|
|
if (!finfo_selected) {
|
|
simple_dialog(ESD_TYPE_WARN, NULL,
|
|
"Error determining selected bytes. Please make\n"
|
|
"sure you have selected a field within the tree\n"
|
|
"view to be matched.");
|
|
return;
|
|
}
|
|
|
|
hfinfo = finfo_selected->hfinfo;
|
|
g_assert(hfinfo);
|
|
abbrev_len = strlen(hfinfo->abbrev);
|
|
|
|
switch(hfinfo->type) {
|
|
|
|
case FT_BOOLEAN:
|
|
dfilter_len = abbrev_len + 2;
|
|
buf = g_malloc0(dfilter_len);
|
|
snprintf(buf, dfilter_len, "%s%s", finfo_selected->value.numeric ? "" : "!",
|
|
hfinfo->abbrev);
|
|
break;
|
|
|
|
case FT_UINT8:
|
|
case FT_UINT16:
|
|
case FT_UINT24:
|
|
case FT_UINT32:
|
|
case FT_INT8:
|
|
case FT_INT16:
|
|
case FT_INT24:
|
|
case FT_INT32:
|
|
dfilter_len = abbrev_len + 20;
|
|
buf = g_malloc0(dfilter_len);
|
|
format = hfinfo_numeric_format(hfinfo);
|
|
snprintf(buf, dfilter_len, format, hfinfo->abbrev, finfo_selected->value.numeric);
|
|
break;
|
|
|
|
case FT_IPv4:
|
|
dfilter_len = abbrev_len + 4 + 15 + 1;
|
|
buf = g_malloc0(dfilter_len);
|
|
snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev,
|
|
ipv4_addr_str(&(finfo_selected->value.ipv4)));
|
|
break;
|
|
|
|
case FT_IPXNET:
|
|
dfilter_len = abbrev_len + 15;
|
|
buf = g_malloc0(dfilter_len);
|
|
snprintf(buf, dfilter_len, "%s == 0x%08x", hfinfo->abbrev,
|
|
finfo_selected->value.numeric);
|
|
break;
|
|
|
|
case FT_IPv6:
|
|
stringified = ip6_to_str((struct e_in6_addr*) &(finfo_selected->value.ipv6));
|
|
dfilter_len = abbrev_len + 4 + strlen(stringified) + 1;
|
|
buf = g_malloc0(dfilter_len);
|
|
snprintf(buf, dfilter_len, "%s == %s", hfinfo->abbrev,
|
|
stringified);
|
|
break;
|
|
|
|
case FT_DOUBLE:
|
|
dfilter_len = abbrev_len + 30;
|
|
buf = g_malloc0(dfilter_len);
|
|
snprintf(buf, dfilter_len, "%s == %f", hfinfo->abbrev,
|
|
finfo_selected->value.floating);
|
|
break;
|
|
|
|
case FT_ETHER:
|
|
dfilter_len = abbrev_len + 22;
|
|
buf = g_malloc0(dfilter_len);
|
|
snprintf(buf, dfilter_len, "%s == %s",
|
|
hfinfo->abbrev,
|
|
ether_to_str(finfo_selected->value.ether));
|
|
break;
|
|
#if 0
|
|
|
|
case FT_ABSOLUTE_TIME:
|
|
case FT_RELATIVE_TIME:
|
|
memcpy(&fi->value.time, va_arg(ap, struct timeval*),
|
|
sizeof(struct timeval));
|
|
break;
|
|
|
|
case FT_STRING:
|
|
/* This g_strdup'ed memory is freed in proto_tree_free_node() */
|
|
fi->value.string = g_strdup(va_arg(ap, char*));
|
|
break;
|
|
|
|
case FT_TEXT_ONLY:
|
|
; /* nothing */
|
|
break;
|
|
#endif
|
|
default:
|
|
c = cfile.pd + finfo_selected->start;
|
|
buf = g_malloc0(32 + finfo_selected->length * 3);
|
|
ptr = buf;
|
|
|
|
sprintf(ptr, "frame[%d] == ", finfo_selected->start);
|
|
ptr = buf+strlen(buf);
|
|
|
|
if (finfo_selected->length == 1) {
|
|
sprintf(ptr, "0x%02x", *c++);
|
|
}
|
|
else {
|
|
for (i=0;i<finfo_selected->length; i++) {
|
|
if (i == 0 ) {
|
|
sprintf(ptr, "%02x", *c++);
|
|
}
|
|
else {
|
|
sprintf(ptr, ":%02x", *c++);
|
|
}
|
|
ptr = buf+strlen(buf);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* create a new one and set the display filter entry accordingly */
|
|
gtk_entry_set_text(GTK_ENTRY(filter_te), buf);
|
|
|
|
/* Run the display filter so it goes in effect. */
|
|
filter_packets(&cfile, buf);
|
|
|
|
/* Don't g_free(buf) here. filter_packets() will do it the next time it's called */
|
|
}
|
|
|
|
static char*
|
|
hfinfo_numeric_format(header_field_info *hfinfo)
|
|
{
|
|
char *format = NULL;
|
|
|
|
/* Pick the proper format string */
|
|
switch(hfinfo->display) {
|
|
case BASE_DEC:
|
|
case BASE_NONE:
|
|
case BASE_OCT: /* I'm lazy */
|
|
case BASE_BIN: /* I'm lazy */
|
|
switch(hfinfo->type) {
|
|
case FT_UINT8:
|
|
case FT_UINT16:
|
|
case FT_UINT24:
|
|
case FT_UINT32:
|
|
format = "%s == %u";
|
|
break;
|
|
case FT_INT8:
|
|
case FT_INT16:
|
|
case FT_INT24:
|
|
case FT_INT32:
|
|
format = "%s == %d";
|
|
break;
|
|
default:
|
|
g_assert_not_reached();
|
|
;
|
|
}
|
|
break;
|
|
case BASE_HEX:
|
|
switch(hfinfo->type) {
|
|
case FT_UINT8:
|
|
format = "%s == 0x%02x";
|
|
break;
|
|
case FT_UINT16:
|
|
format = "%s == 0x%04x";
|
|
break;
|
|
case FT_UINT24:
|
|
format = "%s == 0x%06x";
|
|
break;
|
|
case FT_UINT32:
|
|
format = "%s == 0x%08x";
|
|
break;
|
|
default:
|
|
g_assert_not_reached();
|
|
;
|
|
}
|
|
break;
|
|
default:
|
|
g_assert_not_reached();
|
|
;
|
|
}
|
|
return format;
|
|
}
|
|
|
|
|
|
/* 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(w), E_DFILTER_FL_KEY);
|
|
GList *li, *nl = NULL;
|
|
gboolean add_filter = TRUE;
|
|
|
|
char *s = gtk_entry_get_text(GTK_ENTRY(w));
|
|
|
|
/* 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, g_strdup(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) {
|
|
filter_list = g_list_append(filter_list, g_strdup(s));
|
|
li = g_list_first(filter_list);
|
|
while (li) {
|
|
nl = g_list_append(nl, strdup(li->data));
|
|
li = li->next;
|
|
}
|
|
gtk_combo_set_popdown_strings(filter_cm, nl);
|
|
gtk_entry_set_text(GTK_ENTRY(filter_cm->entry), g_list_last(filter_list)->data);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* redisplay with no display filter */
|
|
static void
|
|
filter_reset_cb(GtkWidget *w, gpointer data)
|
|
{
|
|
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)
|
|
{
|
|
if (column == clist->sort_column) {
|
|
if (clist->sort_type == GTK_SORT_ASCENDING)
|
|
clist->sort_type = GTK_SORT_DESCENDING;
|
|
else
|
|
clist->sort_type = GTK_SORT_ASCENDING;
|
|
}
|
|
else {
|
|
clist->sort_type = GTK_SORT_ASCENDING;
|
|
gtk_clist_set_sort_column(clist, column);
|
|
}
|
|
|
|
gtk_clist_sort(clist);
|
|
}
|
|
|
|
/* What to do when a list item is selected/unselected */
|
|
static void
|
|
packet_list_select_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
|
|
|
|
blank_packetinfo();
|
|
select_packet(&cfile, row);
|
|
}
|
|
|
|
static void
|
|
packet_list_unselect_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
|
|
unselect_packet(&cfile);
|
|
}
|
|
|
|
static void
|
|
tree_view_select_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user_data)
|
|
{
|
|
field_info *finfo;
|
|
|
|
g_assert(node);
|
|
finfo = gtk_ctree_node_get_row_data( ctree, GTK_CTREE_NODE(node) );
|
|
if (!finfo) return;
|
|
|
|
finfo_selected = finfo;
|
|
|
|
packet_hex_print(GTK_TEXT(byte_view), cfile.pd, cfile.current_frame->cap_len,
|
|
finfo->start, finfo->length, cfile.current_frame->flags.encoding);
|
|
}
|
|
|
|
static void
|
|
tree_view_unselect_row_cb(GtkCTree *ctree, GList *node, gint column, gpointer user_data)
|
|
{
|
|
finfo_selected = NULL;
|
|
packet_hex_print(GTK_TEXT(byte_view), cfile.pd, cfile.current_frame->cap_len,
|
|
-1, -1, cfile.current_frame->flags.encoding);
|
|
}
|
|
|
|
void collapse_all_cb(GtkWidget *widget, gpointer data) {
|
|
if (cfile.protocol_tree)
|
|
collapse_all_tree(cfile.protocol_tree, tree_view);
|
|
}
|
|
|
|
void expand_all_cb(GtkWidget *widget, gpointer data) {
|
|
if (cfile.protocol_tree)
|
|
expand_all_tree(cfile.protocol_tree, tree_view);
|
|
}
|
|
|
|
void resolve_name_cb(GtkWidget *widget, gpointer data) {
|
|
if (cfile.protocol_tree) {
|
|
int tmp = g_resolving_actif;
|
|
g_resolving_actif = 1;
|
|
gtk_clist_clear ( GTK_CLIST(tree_view) );
|
|
proto_tree_draw(cfile.protocol_tree, tree_view);
|
|
g_resolving_actif = tmp;
|
|
}
|
|
}
|
|
|
|
/* Set the scrollbar placement of a scrolled window based upon pos value:
|
|
0 = left, 1 = right */
|
|
void
|
|
set_scrollbar_placement_scrollw(GtkWidget *scrollw, int pos) /* 0=left, 1=right */
|
|
{
|
|
if (pos) {
|
|
gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
|
|
GTK_CORNER_TOP_LEFT);
|
|
} else {
|
|
gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
|
|
GTK_CORNER_TOP_RIGHT);
|
|
}
|
|
}
|
|
|
|
/* List of all scrolled windows, so we can globally set the scrollbar
|
|
placement of them. */
|
|
static GList *scrolled_windows;
|
|
|
|
/* Add a scrolled window to the list of scrolled windows. */
|
|
void
|
|
remember_scrolled_window(GtkWidget *scrollw)
|
|
{
|
|
scrolled_windows = g_list_append(scrolled_windows, scrollw);
|
|
}
|
|
|
|
/* Remove a scrolled window from the list of scrolled windows. */
|
|
void
|
|
forget_scrolled_window(GtkWidget *scrollw)
|
|
{
|
|
scrolled_windows = g_list_remove(scrolled_windows, scrollw);
|
|
}
|
|
|
|
static void
|
|
set_scrollbar_placement_cb(gpointer data, gpointer user_data)
|
|
{
|
|
set_scrollbar_placement_scrollw((GtkWidget *)data,
|
|
*(int *)user_data);
|
|
}
|
|
|
|
/* Set the scrollbar placement of all scrolled windows based on pos value:
|
|
0 = left, 1 = right */
|
|
void
|
|
set_scrollbar_placement_all(int pos)
|
|
{
|
|
g_list_foreach(scrolled_windows, set_scrollbar_placement_cb, &pos);
|
|
}
|
|
|
|
/* Set the selection mode of the packet list window. */
|
|
void
|
|
set_plist_sel_browse(gboolean val)
|
|
{
|
|
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 selection mode of a given packet tree window. */
|
|
void
|
|
set_ptree_sel_browse(GtkWidget *tv, gboolean val)
|
|
{
|
|
/* 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(tv), GTK_SELECTION_SINGLE);
|
|
}
|
|
else {
|
|
gtk_clist_set_selection_mode(GTK_CLIST(tv), GTK_SELECTION_BROWSE);
|
|
}
|
|
}
|
|
|
|
/* Set the selection mode of all packet tree windows. */
|
|
void
|
|
set_ptree_sel_browse_all(gboolean val)
|
|
{
|
|
set_ptree_sel_browse(tree_view, val);
|
|
set_ptree_sel_browse_packet_wins(val);
|
|
}
|
|
|
|
/* Set the line style of a given packet tree window. */
|
|
void
|
|
set_ptree_line_style(GtkWidget *tv, gint style)
|
|
{
|
|
/* I'm using an assert here since the preferences code limits
|
|
* the user input, both in the GUI and when reading the preferences file.
|
|
* If the value is incorrect, it's a program error, not a user-initiated error.
|
|
*/
|
|
g_assert(style >= GTK_CTREE_LINES_NONE && style <= GTK_CTREE_LINES_TABBED);
|
|
gtk_ctree_set_line_style( GTK_CTREE(tv), style );
|
|
}
|
|
|
|
/* Set the line style of all packet tree window. */
|
|
void
|
|
set_ptree_line_style_all(gint style)
|
|
{
|
|
set_ptree_line_style(tree_view, style);
|
|
set_ptree_line_style_packet_wins(style);
|
|
}
|
|
|
|
/* Set the expander style of a given packet tree window. */
|
|
void
|
|
set_ptree_expander_style(GtkWidget *tv, gint style)
|
|
{
|
|
/* I'm using an assert here since the preferences code limits
|
|
* the user input, both in the GUI and when reading the preferences file.
|
|
* If the value is incorrect, it's a program error, not a user-initiated error.
|
|
*/
|
|
g_assert(style >= GTK_CTREE_EXPANDER_NONE && style <= GTK_CTREE_EXPANDER_CIRCULAR);
|
|
gtk_ctree_set_expander_style( GTK_CTREE(tv), style );
|
|
}
|
|
|
|
void
|
|
set_ptree_expander_style_all(gint style)
|
|
{
|
|
set_ptree_expander_style(tree_view, style);
|
|
set_ptree_expander_style_packet_wins(style);
|
|
}
|
|
|
|
static gboolean
|
|
main_window_delete_event_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
|
|
{
|
|
file_quit_cmd_cb(widget, data);
|
|
|
|
/* Say that the window should be deleted. */
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
file_quit_cmd_cb (GtkWidget *widget, gpointer data)
|
|
{
|
|
/* 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 that
|
|
"main_window_delete_event_cb()" should return its
|
|
return value. */
|
|
|
|
/* 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;
|
|
} 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, info_bar);
|
|
|
|
/* Exit by leaving the main loop, so that any quit functions
|
|
we registered get called. */
|
|
gtk_main_quit();
|
|
}
|
|
}
|
|
|
|
static void
|
|
print_usage(void) {
|
|
|
|
fprintf(stderr, "This is GNU " PACKAGE " " VERSION ", compiled with %s\n",
|
|
comp_info_str);
|
|
#ifdef HAVE_LIBPCAP
|
|
fprintf(stderr, "%s [ -vh ] [ -kQS ] [ -b <bold font> ] [ -B <byte view height> ]\n",
|
|
PACKAGE);
|
|
fprintf(stderr, "\t[ -c count ] [ -D ] [ -f <capture filter> ] [ -i interface ]\n");
|
|
fprintf(stderr, "\t[ -m <medium font> ] [ -n ] [ -o <preference setting> ] ...\n");
|
|
fprintf(stderr, "\t[ -P <packet list height> ] [ -r infile ] [ -R <read filter> ]\n");
|
|
fprintf(stderr, "\t[ -s snaplen ] [ -t <time stamp format> ] [ -T <tree view height> ]\n");
|
|
fprintf(stderr, "\t[ -w savefile ]\n");
|
|
#else
|
|
fprintf(stderr, "%s [ -vh ] [ -b <bold font> ] [ -B <byte view height> ]\n",
|
|
PACKAGE);
|
|
fprintf(stderr, "\t[ -m <medium font> ] [ -n ] [ -o <preference setting ] ...\n");
|
|
fprintf(stderr, "\t[ -P <packet list height> ] [ -r infile ] [ -R <read filter> ]\n");
|
|
fprintf(stderr, "\t[ -t <time stamp format> ] [ -T <tree view height> ]\n");
|
|
#endif
|
|
}
|
|
|
|
/* And now our feature presentation... [ fade to music ] */
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
#ifdef HAVE_LIBPCAP
|
|
char *command_name;
|
|
#endif
|
|
char *s;
|
|
int i;
|
|
int opt;
|
|
extern char *optarg;
|
|
gboolean arg_error = FALSE;
|
|
#ifdef HAVE_LIBPCAP
|
|
#ifdef WIN32
|
|
char pcap_version[] = "0.4a6";
|
|
#else
|
|
extern char pcap_version[];
|
|
#endif
|
|
#endif
|
|
char *gpf_path, *pf_path;
|
|
int gpf_open_errno, pf_open_errno;
|
|
int err;
|
|
#ifdef HAVE_LIBPCAP
|
|
gboolean start_capture = FALSE;
|
|
gchar *save_file = NULL;
|
|
GList *if_list;
|
|
gchar err_str[PCAP_ERRBUF_SIZE];
|
|
#else
|
|
gboolean capture_option_specified = FALSE;
|
|
#endif
|
|
gint pl_size = 280, tv_size = 95, bv_size = 75;
|
|
gchar *rc_file, *cf_name = NULL, *rfilter = NULL;
|
|
dfilter *rfcode = NULL;
|
|
gboolean rfilter_parse_failed = FALSE;
|
|
e_prefs *prefs;
|
|
|
|
ethereal_path = argv[0];
|
|
|
|
#ifdef HAVE_LIBPCAP
|
|
command_name = get_basename(ethereal_path);
|
|
/* Set "capture_child" to indicate whether this is going to be a child
|
|
process for a "-S" capture. */
|
|
capture_child = (strcmp(command_name, CHILD_NAME) == 0);
|
|
#endif
|
|
|
|
/* Register all dissectors; we must do this before checking for the
|
|
"-G" flag, as the "-G" flag dumps a list of fields registered
|
|
by the dissectors, and we must do it before we read the preferences,
|
|
in case any dissectors register preferences. */
|
|
dissect_init();
|
|
|
|
/* Now register the preferences for any non-dissector modules.
|
|
We must do that before we read the preferences as well. */
|
|
prefs_register_modules();
|
|
|
|
/* If invoked with the "-G" flag, we dump out a glossary of
|
|
display filter symbols.
|
|
|
|
We must do this before calling "gtk_init()", because "gtk_init()"
|
|
tries to open an X display, and we don't want to have to do any X
|
|
stuff just to do a build.
|
|
|
|
Given that we call "gtk_init()" before doing the regular argument
|
|
list processing, so that it can handle X and GTK+ arguments and
|
|
remove them from the list at which we look, this means we must do
|
|
this before doing the regular argument list processing, as well.
|
|
|
|
This means that:
|
|
|
|
you must give the "-G" flag as the first flag on the command line;
|
|
|
|
you must give it as "-G", nothing more, nothing less;
|
|
|
|
any arguments after the "-G" flag will not be used. */
|
|
if (argc >= 2 && strcmp(argv[1], "-G") == 0) {
|
|
proto_registrar_dump();
|
|
exit(0);
|
|
}
|
|
|
|
/* Set the current locale according to the program environment.
|
|
* We haven't localized anything, but some GTK widgets are localized
|
|
* (the file selection dialogue, for example).
|
|
* This also sets the C-language locale to the native environment. */
|
|
gtk_set_locale();
|
|
|
|
/* Let GTK get its args */
|
|
gtk_init (&argc, &argv);
|
|
|
|
prefs = read_prefs(&gpf_open_errno, &gpf_path, &pf_open_errno, &pf_path);
|
|
|
|
/* Initialize the capture file struct */
|
|
cfile.plist = NULL;
|
|
cfile.plist_end = NULL;
|
|
cfile.wth = NULL;
|
|
cfile.filename = NULL;
|
|
cfile.user_saved = FALSE;
|
|
cfile.is_tempfile = FALSE;
|
|
cfile.rfcode = NULL;
|
|
cfile.dfilter = NULL;
|
|
cfile.dfcode = NULL;
|
|
#ifdef HAVE_LIBPCAP
|
|
cfile.cfilter = g_strdup(EMPTY_FILTER);
|
|
#endif
|
|
cfile.iface = NULL;
|
|
cfile.save_file = NULL;
|
|
cfile.save_file_fd = -1;
|
|
cfile.snap = WTAP_MAX_PACKET_SIZE;
|
|
cfile.count = 0;
|
|
cfile.cinfo.num_cols = prefs->num_cols;
|
|
cfile.cinfo.col_fmt = (gint *) g_malloc(sizeof(gint) * cfile.cinfo.num_cols);
|
|
cfile.cinfo.fmt_matx = (gboolean **) g_malloc(sizeof(gboolean *) * cfile.cinfo.num_cols);
|
|
cfile.cinfo.col_width = (gint *) g_malloc(sizeof(gint) * cfile.cinfo.num_cols);
|
|
cfile.cinfo.col_title = (gchar **) g_malloc(sizeof(gchar *) * cfile.cinfo.num_cols);
|
|
cfile.cinfo.col_data = (gchar **) g_malloc(sizeof(gchar *) * cfile.cinfo.num_cols);
|
|
|
|
/* Assemble the compile-time options */
|
|
snprintf(comp_info_str, 256,
|
|
#ifdef GTK_MAJOR_VERSION
|
|
"GTK+ %d.%d.%d, %s%s, %s%s, %s%s", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
|
|
GTK_MICRO_VERSION,
|
|
#else
|
|
"GTK+ (version unknown), %s%s, %s%s, %s%s",
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBPCAP
|
|
"with libpcap ", pcap_version,
|
|
#else
|
|
"without libpcap", "",
|
|
#endif
|
|
|
|
#ifdef HAVE_LIBZ
|
|
#ifdef ZLIB_VERSION
|
|
"with libz ", ZLIB_VERSION,
|
|
#else /* ZLIB_VERSION */
|
|
"with libz ", "(version unknown)",
|
|
#endif /* ZLIB_VERSION */
|
|
#else /* HAVE_LIBZ */
|
|
"without libz", "",
|
|
#endif /* HAVE_LIBZ */
|
|
|
|
/* Oh, this is pretty */
|
|
#if defined(HAVE_UCD_SNMP_SNMP_H)
|
|
#ifdef HAVE_UCD_SNMP_VERSION_H
|
|
"with UCD SNMP ", VersionInfo
|
|
#else /* HAVE_UCD_SNMP_VERSION_H */
|
|
"with UCD SNMP ", "(version unknown)"
|
|
#endif /* HAVE_UCD_SNMP_VERSION_H */
|
|
#elif defined(HAVE_SNMP_SNMP_H)
|
|
#ifdef HAVE_SNMP_VERSION_H
|
|
"with CMU SNMP ", snmp_Version()
|
|
#else /* HAVE_SNMP_VERSION_H */
|
|
"with CMU SNMP ", "(version unknown)"
|
|
#endif /* HAVE_SNMP_VERSION_H */
|
|
#else /* no SNMP */
|
|
"without SNMP", ""
|
|
#endif
|
|
);
|
|
|
|
/* Now get our args */
|
|
while ((opt = getopt(argc, argv, "b:B:c:Df:hi:km:no:P:Qr:R:Ss:t:T:w:W:vZ:")) != EOF) {
|
|
switch (opt) {
|
|
case 'b': /* Bold font */
|
|
bold_font = g_strdup(optarg);
|
|
break;
|
|
case 'B': /* Byte view pane height */
|
|
bv_size = atoi(optarg);
|
|
break;
|
|
case 'c': /* Capture xxx packets */
|
|
#ifdef HAVE_LIBPCAP
|
|
cfile.count = atoi(optarg);
|
|
#else
|
|
capture_option_specified = TRUE;
|
|
arg_error = TRUE;
|
|
#endif
|
|
break;
|
|
case 'D': /* Turn off DSCP printing */
|
|
g_ip_dscp_actif = FALSE;
|
|
break;
|
|
case 'f':
|
|
#ifdef HAVE_LIBPCAP
|
|
if (cfile.cfilter)
|
|
g_free(cfile.cfilter);
|
|
cfile.cfilter = g_strdup(optarg);
|
|
#else
|
|
capture_option_specified = TRUE;
|
|
arg_error = TRUE;
|
|
#endif
|
|
break;
|
|
case 'h': /* Print help and exit */
|
|
print_usage();
|
|
exit(0);
|
|
break;
|
|
case 'i': /* Use interface xxx */
|
|
#ifdef HAVE_LIBPCAP
|
|
cfile.iface = g_strdup(optarg);
|
|
#else
|
|
capture_option_specified = TRUE;
|
|
arg_error = TRUE;
|
|
#endif
|
|
break;
|
|
case 'k': /* Start capture immediately */
|
|
#ifdef HAVE_LIBPCAP
|
|
start_capture = TRUE;
|
|
#else
|
|
capture_option_specified = TRUE;
|
|
arg_error = TRUE;
|
|
#endif
|
|
break;
|
|
case 'm': /* Medium font */
|
|
medium_font = g_strdup(optarg);
|
|
break;
|
|
case 'n': /* No name resolution */
|
|
g_resolving_actif = 0;
|
|
break;
|
|
case 'o': /* Override preference from command line */
|
|
switch (prefs_set_pref(optarg)) {
|
|
|
|
case PREFS_SET_SYNTAX_ERR:
|
|
fprintf(stderr, "ethereal: Invalid -o flag \"%s\"\n", optarg);
|
|
exit(1);
|
|
break;
|
|
|
|
case PREFS_SET_NO_SUCH_PREF:
|
|
fprintf(stderr, "ethereal: -o flag \"%s\" specifies unknown preference\n",
|
|
optarg);
|
|
exit(1);
|
|
break;
|
|
}
|
|
break;
|
|
case 'P': /* Packet list pane height */
|
|
pl_size = atoi(optarg);
|
|
break;
|
|
case 'Q': /* Quit after capture (just capture to file) */
|
|
#ifdef HAVE_LIBPCAP
|
|
quit_after_cap = 1;
|
|
start_capture = TRUE; /*** -Q implies -k !! ***/
|
|
#else
|
|
capture_option_specified = TRUE;
|
|
arg_error = TRUE;
|
|
#endif
|
|
break;
|
|
case 'r': /* Read capture file xxx */
|
|
/* We may set "last_open_dir" to "cf_name", and if we change
|
|
"last_open_dir" later, we free the old value, so we have to
|
|
set "cf_name" to something that's been allocated. */
|
|
cf_name = g_strdup(optarg);
|
|
break;
|
|
case 'R': /* Read file filter */
|
|
rfilter = optarg;
|
|
break;
|
|
case 's': /* Set the snapshot (capture) length */
|
|
#ifdef HAVE_LIBPCAP
|
|
cfile.snap = atoi(optarg);
|
|
#else
|
|
capture_option_specified = TRUE;
|
|
arg_error = TRUE;
|
|
#endif
|
|
break;
|
|
case 'S': /* "Sync" mode: used for following file ala tail -f */
|
|
#ifdef HAVE_LIBPCAP
|
|
sync_mode = TRUE;
|
|
#else
|
|
capture_option_specified = TRUE;
|
|
arg_error = TRUE;
|
|
#endif
|
|
break;
|
|
case 't': /* Time stamp type */
|
|
if (strcmp(optarg, "r") == 0)
|
|
timestamp_type = RELATIVE;
|
|
else if (strcmp(optarg, "a") == 0)
|
|
timestamp_type = ABSOLUTE;
|
|
else if (strcmp(optarg, "d") == 0)
|
|
timestamp_type = DELTA;
|
|
else {
|
|
fprintf(stderr, "ethereal: Invalid time stamp type \"%s\"\n",
|
|
optarg);
|
|
fprintf(stderr, "It must be \"r\" for relative, \"a\" for absolute,\n");
|
|
fprintf(stderr, "or \"d\" for delta.\n");
|
|
exit(1);
|
|
}
|
|
break;
|
|
case 'T': /* Tree view pane height */
|
|
tv_size = atoi(optarg);
|
|
break;
|
|
case 'v': /* Show version and exit */
|
|
printf("%s %s, with %s\n", PACKAGE, VERSION, comp_info_str);
|
|
exit(0);
|
|
break;
|
|
case 'w': /* Write to capture file xxx */
|
|
#ifdef HAVE_LIBPCAP
|
|
save_file = g_strdup(optarg);
|
|
#else
|
|
capture_option_specified = TRUE;
|
|
arg_error = TRUE;
|
|
#endif
|
|
break;
|
|
case 'W': /* Write to capture file FD xxx */
|
|
#ifdef HAVE_LIBPCAP
|
|
cfile.save_file_fd = atoi(optarg);
|
|
#else
|
|
capture_option_specified = TRUE;
|
|
arg_error = TRUE;
|
|
#endif
|
|
break;
|
|
|
|
#ifdef _WIN32
|
|
case 'Z': /* Write to pipe FD XXX */
|
|
#ifdef HAVE_LIBPCAP
|
|
/* associate stdout with pipe */
|
|
i = atoi(optarg);
|
|
if (dup2(i, 1) < 0) {
|
|
fprintf(stderr, "Unable to dup pipe handle\n");
|
|
exit(1);
|
|
}
|
|
#else
|
|
capture_option_specified = TRUE;
|
|
arg_error = TRUE;
|
|
#endif /* HAVE_LIBPCAP */
|
|
break;
|
|
#endif /* _WIN32 */
|
|
|
|
default:
|
|
case '?': /* Bad flag - print usage message */
|
|
arg_error = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifndef HAVE_LIBPCAP
|
|
if (capture_option_specified)
|
|
fprintf(stderr, "This version of Ethereal was not built with support for capturing packets.\n");
|
|
#endif
|
|
if (arg_error)
|
|
print_usage();
|
|
#ifdef HAVE_LIBPCAP
|
|
if (start_capture) {
|
|
/* We're supposed to do a live capture; did the user specify an interface
|
|
to use? */
|
|
if (cfile.iface == NULL) {
|
|
/* No - pick the first one from the list of interfaces. */
|
|
if_list = get_interface_list(&err, err_str);
|
|
if (if_list == NULL) {
|
|
switch (err) {
|
|
|
|
case CANT_GET_INTERFACE_LIST:
|
|
fprintf(stderr, "ethereal: Can't get list of interfaces: %s\n",
|
|
err_str);
|
|
break;
|
|
|
|
case NO_INTERFACES_FOUND:
|
|
fprintf(stderr, "ethereal: There are no interfaces on which a capture can be done\n");
|
|
break;
|
|
}
|
|
exit(2);
|
|
}
|
|
cfile.iface = g_strdup(if_list->data); /* first interface */
|
|
free_interface_list(if_list);
|
|
}
|
|
}
|
|
if (capture_child) {
|
|
if (cfile.save_file_fd == -1) {
|
|
/* XXX - send this to the standard output as something our parent
|
|
should put in an error message box? */
|
|
fprintf(stderr, "%s: \"-W\" flag not specified\n", CHILD_NAME);
|
|
exit(1);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Build the column format array */
|
|
for (i = 0; i < cfile.cinfo.num_cols; i++) {
|
|
cfile.cinfo.col_fmt[i] = get_column_format(i);
|
|
cfile.cinfo.col_title[i] = g_strdup(get_column_title(i));
|
|
cfile.cinfo.fmt_matx[i] = (gboolean *) g_malloc0(sizeof(gboolean) *
|
|
NUM_COL_FMTS);
|
|
get_column_format_matches(cfile.cinfo.fmt_matx[i], cfile.cinfo.col_fmt[i]);
|
|
if (cfile.cinfo.col_fmt[i] == COL_INFO)
|
|
cfile.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_INFO_LEN);
|
|
else
|
|
cfile.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
|
|
}
|
|
|
|
if (cfile.snap < 1)
|
|
cfile.snap = WTAP_MAX_PACKET_SIZE;
|
|
else if (cfile.snap < MIN_PACKET_SIZE)
|
|
cfile.snap = MIN_PACKET_SIZE;
|
|
|
|
rc_file = (gchar *) g_malloc(strlen(get_home_dir()) + strlen(RC_FILE) + 4);
|
|
sprintf(rc_file, "%s/%s", get_home_dir(), RC_FILE);
|
|
gtk_rc_parse(rc_file);
|
|
|
|
if ((m_r_font = gdk_font_load(medium_font)) == NULL) {
|
|
fprintf(stderr, "ethereal: Error font %s not found (use -m option)\n", medium_font);
|
|
exit(1);
|
|
}
|
|
|
|
if ((m_b_font = gdk_font_load(bold_font)) == NULL) {
|
|
fprintf(stderr, "ethereal: Error font %s not found (use -b option)\n", bold_font);
|
|
exit(1);
|
|
}
|
|
|
|
create_main_window(pl_size, tv_size, bv_size, prefs);
|
|
|
|
#ifdef HAVE_LIBPCAP
|
|
/* Is this a "child" ethereal, which is only supposed to pop up a
|
|
capture box to let us stop the capture, and run a capture
|
|
to a file that our parent will read? */
|
|
if (!capture_child) {
|
|
#endif
|
|
/* No. Pop up the main window, and read in a capture file if
|
|
we were told to. */
|
|
|
|
gtk_widget_show(top_level);
|
|
set_menus_for_capture_file(FALSE);
|
|
|
|
cfile.colors = colfilter_new();
|
|
|
|
/* If we were given the name of a capture file, read it in now;
|
|
we defer it until now, so that, if we can't open it, and pop
|
|
up an alert box, the alert box is more likely to come up on
|
|
top of the main window - but before the preference-file-error
|
|
alert box, so, if we get one of those, it's more likely to come
|
|
up on top of us. */
|
|
if (cf_name) {
|
|
if (rfilter != NULL) {
|
|
if (dfilter_compile(rfilter, &rfcode) != 0) {
|
|
simple_dialog(ESD_TYPE_WARN, NULL, dfilter_error_msg);
|
|
rfilter_parse_failed = TRUE;
|
|
}
|
|
}
|
|
if (!rfilter_parse_failed) {
|
|
if ((err = open_cap_file(cf_name, FALSE, &cfile)) == 0) {
|
|
/* "open_cap_file()" succeeded, so it closed the previous
|
|
capture file, and thus destroyed any previous read filter
|
|
attached to "cf". */
|
|
cfile.rfcode = rfcode;
|
|
switch (read_cap_file(&cfile, &err)) {
|
|
|
|
case READ_SUCCESS:
|
|
case READ_ERROR:
|
|
/* Just because we got an error, that doesn't mean we were unable
|
|
to read any of the file; we handle what we could get from the
|
|
file. */
|
|
break;
|
|
|
|
case READ_ABORTED:
|
|
/* Exit now. */
|
|
gtk_exit(0);
|
|
break;
|
|
}
|
|
/* Save the name of the containing directory specified in the
|
|
path name, if any; we can write over cf_name, which is a
|
|
good thing, given that "get_dirname()" does write over its
|
|
argument. */
|
|
s = get_dirname(cf_name);
|
|
if (s != NULL)
|
|
last_open_dir = s;
|
|
} else {
|
|
dfilter_destroy(rfcode);
|
|
cfile.rfcode = NULL;
|
|
}
|
|
}
|
|
}
|
|
#ifdef HAVE_LIBPCAP
|
|
}
|
|
#endif
|
|
|
|
/* If the global preferences file exists but we failed to open it,
|
|
pop up an alert box; we defer that until now, so that the alert
|
|
box is more likely to come up on top of the main window. */
|
|
if (gpf_path != NULL) {
|
|
simple_dialog(ESD_TYPE_WARN, NULL,
|
|
"Could not open global preferences file\n\"%s\": %s.", gpf_path,
|
|
strerror(gpf_open_errno));
|
|
}
|
|
|
|
/* If the user's preferences file exists but we failed to open it,
|
|
pop up an alert box; we defer that until now, so that the alert
|
|
box is more likely to come up on top of the main window. */
|
|
if (pf_path != NULL) {
|
|
simple_dialog(ESD_TYPE_WARN, NULL,
|
|
"Could not open your preferences file\n\"%s\": %s.", pf_path,
|
|
strerror(pf_open_errno));
|
|
}
|
|
|
|
#ifdef HAVE_LIBPCAP
|
|
if (capture_child) {
|
|
/* This is the child process for a sync mode or fork mode capture,
|
|
so just do the low-level work of a capture - don't create
|
|
a temporary file and fork off *another* child process (so don't
|
|
call "do_capture()"). */
|
|
|
|
capture();
|
|
|
|
/* The capture is done; there's nothing more for us to do. */
|
|
gtk_exit(0);
|
|
} else {
|
|
if (start_capture) {
|
|
/* "-k" was specified; start a capture. */
|
|
do_capture(save_file);
|
|
}
|
|
else {
|
|
set_menus_for_capture_in_progress(FALSE);
|
|
}
|
|
}
|
|
#else
|
|
set_menus_for_capture_in_progress(FALSE);
|
|
#endif
|
|
|
|
gtk_main();
|
|
|
|
dissect_cleanup();
|
|
g_free(rc_file);
|
|
|
|
gtk_exit(0);
|
|
|
|
/* This isn't reached, but we need it to keep GCC from complaining
|
|
that "main()" returns without returning a value - it knows that
|
|
"exit()" never returns, but it doesn't know that "gtk_exit()"
|
|
doesn't, as GTK+ doesn't declare it with the attribute
|
|
"noreturn". */
|
|
return 0; /* not reached */
|
|
}
|
|
|
|
#ifdef WIN32
|
|
|
|
/* We build this as a GUI subsystem application on Win32, so
|
|
"WinMain()", not "main()", gets called.
|
|
|
|
Hack shamelessly stolen from the Win32 port of the GIMP. */
|
|
#ifdef __GNUC__
|
|
#define _stdcall __attribute__((stdcall))
|
|
#endif
|
|
|
|
int _stdcall
|
|
WinMain (struct HINSTANCE__ *hInstance,
|
|
struct HINSTANCE__ *hPrevInstance,
|
|
char *lpszCmdLine,
|
|
int nCmdShow)
|
|
{
|
|
return main (__argc, __argv);
|
|
}
|
|
|
|
#endif
|
|
|
|
static void
|
|
create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs)
|
|
{
|
|
GtkWidget *main_vbox, *menubar, *u_pane, *l_pane,
|
|
*stat_hbox,
|
|
*filter_bt, *filter_cm, *filter_te,
|
|
*filter_reset;
|
|
GList *filter_list = NULL;
|
|
GtkStyle *pl_style;
|
|
GtkAccelGroup *accel;
|
|
int i;
|
|
|
|
/* Main window */
|
|
top_level = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
gtk_widget_set_name(top_level, "main window");
|
|
gtk_signal_connect(GTK_OBJECT(top_level), "delete_event",
|
|
GTK_SIGNAL_FUNC(main_window_delete_event_cb), NULL);
|
|
gtk_window_set_title(GTK_WINDOW(top_level), "The Ethereal Network Analyzer");
|
|
gtk_widget_set_usize(GTK_WIDGET(top_level), DEF_WIDTH, -1);
|
|
gtk_window_set_policy(GTK_WINDOW(top_level), TRUE, TRUE, FALSE);
|
|
|
|
/* Container for menu bar, paned windows and progress/info box */
|
|
main_vbox = gtk_vbox_new(FALSE, 1);
|
|
gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
|
|
gtk_container_add(GTK_CONTAINER(top_level), main_vbox);
|
|
gtk_widget_show(main_vbox);
|
|
|
|
/* Menu bar */
|
|
get_main_menu(&menubar, &accel);
|
|
gtk_window_add_accel_group(GTK_WINDOW(top_level), accel);
|
|
gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
|
|
gtk_widget_show(menubar);
|
|
|
|
/* Panes for the packet list, tree, and byte view */
|
|
u_pane = gtk_vpaned_new();
|
|
gtk_paned_gutter_size(GTK_PANED(u_pane), (GTK_PANED(u_pane))->handle_size);
|
|
l_pane = gtk_vpaned_new();
|
|
gtk_paned_gutter_size(GTK_PANED(l_pane), (GTK_PANED(l_pane))->handle_size);
|
|
gtk_container_add(GTK_CONTAINER(main_vbox), u_pane);
|
|
gtk_widget_show(l_pane);
|
|
gtk_paned_add2(GTK_PANED(u_pane), l_pane);
|
|
gtk_widget_show(u_pane);
|
|
|
|
/* Packet list */
|
|
pkt_scrollw = gtk_scrolled_window_new(NULL, NULL);
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(pkt_scrollw),
|
|
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
|
|
set_scrollbar_placement_scrollw(pkt_scrollw, prefs->gui_scrollbar_on_right);
|
|
remember_scrolled_window(pkt_scrollw);
|
|
gtk_widget_show(pkt_scrollw);
|
|
gtk_paned_add1(GTK_PANED(u_pane), pkt_scrollw);
|
|
|
|
packet_list = gtk_clist_new_with_titles(cfile.cinfo.num_cols, cfile.cinfo.col_title);
|
|
gtk_container_add(GTK_CONTAINER(pkt_scrollw), packet_list);
|
|
|
|
set_plist_sel_browse(prefs->gui_plist_sel_browse);
|
|
pl_style = gtk_style_new();
|
|
gdk_font_unref(pl_style->font);
|
|
pl_style->font = m_r_font;
|
|
gtk_widget_set_style(packet_list, pl_style);
|
|
gtk_widget_set_name(packet_list, "packet list");
|
|
gtk_signal_connect (GTK_OBJECT (packet_list), "click_column",
|
|
GTK_SIGNAL_FUNC(packet_list_click_column_cb), NULL);
|
|
gtk_signal_connect(GTK_OBJECT(packet_list), "select_row",
|
|
GTK_SIGNAL_FUNC(packet_list_select_cb), NULL);
|
|
gtk_signal_connect(GTK_OBJECT(packet_list), "unselect_row",
|
|
GTK_SIGNAL_FUNC(packet_list_unselect_cb), NULL);
|
|
for (i = 0; i < cfile.cinfo.num_cols; i++) {
|
|
if (get_column_resize_type(cfile.cinfo.col_fmt[i]) != RESIZE_MANUAL)
|
|
gtk_clist_set_column_auto_resize(GTK_CLIST(packet_list), i, TRUE);
|
|
|
|
/* Right-justify the packet number column. */
|
|
if (cfile.cinfo.col_fmt[i] == COL_NUMBER)
|
|
gtk_clist_set_column_justification(GTK_CLIST(packet_list), i,
|
|
GTK_JUSTIFY_RIGHT);
|
|
|
|
/* Save static column sizes to use during a "-S" capture, so that
|
|
the columns don't resize during a live capture. */
|
|
cfile.cinfo.col_width[i] = gdk_string_width(pl_style->font,
|
|
get_column_longest_string(get_column_format(i)));
|
|
}
|
|
gtk_widget_set_usize(packet_list, -1, pl_size);
|
|
gtk_signal_connect_object(GTK_OBJECT(packet_list), "button_press_event",
|
|
GTK_SIGNAL_FUNC(popup_menu_handler), gtk_object_get_data(GTK_OBJECT(popup_menu_object), PM_PACKET_LIST_KEY));
|
|
gtk_clist_set_compare_func(GTK_CLIST(packet_list), packet_list_compare);
|
|
gtk_widget_show(packet_list);
|
|
|
|
/* Tree view */
|
|
create_tree_view(tv_size, prefs, l_pane, &tv_scrollw, &tree_view,
|
|
prefs->gui_scrollbar_on_right);
|
|
gtk_signal_connect(GTK_OBJECT(tree_view), "tree-select-row",
|
|
GTK_SIGNAL_FUNC(tree_view_select_row_cb), NULL);
|
|
gtk_signal_connect(GTK_OBJECT(tree_view), "tree-unselect-row",
|
|
GTK_SIGNAL_FUNC(tree_view_unselect_row_cb), NULL);
|
|
gtk_signal_connect_object(GTK_OBJECT(tree_view), "button_press_event",
|
|
GTK_SIGNAL_FUNC(popup_menu_handler), gtk_object_get_data(GTK_OBJECT(popup_menu_object), PM_TREE_VIEW_KEY));
|
|
gtk_widget_show(tree_view);
|
|
|
|
item_style = gtk_style_new();
|
|
gdk_font_unref(item_style->font);
|
|
item_style->font = m_r_font;
|
|
|
|
/* Byte view. */
|
|
create_byte_view(bv_size, l_pane, &byte_view, &bv_scrollw,
|
|
prefs->gui_scrollbar_on_right);
|
|
|
|
/* Filter/info box */
|
|
stat_hbox = gtk_hbox_new(FALSE, 1);
|
|
gtk_container_border_width(GTK_CONTAINER(stat_hbox), 0);
|
|
gtk_box_pack_start(GTK_BOX(main_vbox), stat_hbox, FALSE, TRUE, 0);
|
|
gtk_widget_show(stat_hbox);
|
|
|
|
filter_bt = gtk_button_new_with_label("Filter:");
|
|
gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
|
|
GTK_SIGNAL_FUNC(filter_browse_cb), NULL);
|
|
gtk_box_pack_start(GTK_BOX(stat_hbox), filter_bt, FALSE, TRUE, 0);
|
|
gtk_widget_show(filter_bt);
|
|
|
|
filter_cm = gtk_combo_new();
|
|
filter_list = g_list_append (filter_list, "");
|
|
gtk_combo_set_popdown_strings(GTK_COMBO(filter_cm), filter_list);
|
|
gtk_combo_disable_activate(GTK_COMBO(filter_cm));
|
|
filter_te = GTK_COMBO(filter_cm)->entry;
|
|
gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
|
|
gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_CM_KEY, filter_cm);
|
|
gtk_object_set_data(GTK_OBJECT(filter_te), E_DFILTER_FL_KEY, filter_list);
|
|
gtk_box_pack_start(GTK_BOX(stat_hbox), filter_cm, TRUE, TRUE, 3);
|
|
gtk_signal_connect(GTK_OBJECT(filter_te), "activate",
|
|
GTK_SIGNAL_FUNC(filter_activate_cb), (gpointer) NULL);
|
|
gtk_widget_show(filter_cm);
|
|
|
|
filter_reset = gtk_button_new_with_label("Reset");
|
|
gtk_object_set_data(GTK_OBJECT(filter_reset), E_DFILTER_TE_KEY, filter_te);
|
|
gtk_signal_connect(GTK_OBJECT(filter_reset), "clicked",
|
|
GTK_SIGNAL_FUNC(filter_reset_cb), (gpointer) NULL);
|
|
gtk_box_pack_start(GTK_BOX(stat_hbox), filter_reset, FALSE, TRUE, 1);
|
|
gtk_widget_show(filter_reset);
|
|
|
|
/* Sets the text entry widget pointer as the E_DILTER_TE_KEY data
|
|
* of any widget that ends up calling a callback which needs
|
|
* that text entry pointer */
|
|
set_menu_object_data("/File/Open...", E_DFILTER_TE_KEY, filter_te);
|
|
set_menu_object_data("/File/Reload", E_DFILTER_TE_KEY, filter_te);
|
|
set_menu_object_data("/Edit/Filters...", E_FILT_TE_PTR_KEY, filter_te);
|
|
set_menu_object_data("/Display/Match Selected", E_DFILTER_TE_KEY, filter_te);
|
|
set_menu_object_data("/Tools/Follow TCP Stream", E_DFILTER_TE_KEY, filter_te);
|
|
|
|
info_bar = gtk_statusbar_new();
|
|
main_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "main");
|
|
file_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "file");
|
|
gtk_statusbar_push(GTK_STATUSBAR(info_bar), main_ctx, DEF_READY_MESSAGE);
|
|
gtk_box_pack_start(GTK_BOX(stat_hbox), info_bar, TRUE, TRUE, 0);
|
|
gtk_widget_show(info_bar);
|
|
}
|