Created a new protocol tree implementation and a new display filter
[obnox/wireshark/wip.git] / ethereal.c
1 /* ethereal.c
2  *
3  * $Id: ethereal.c,v 1.48 1999/07/07 22:51:37 gram Exp $
4  *
5  * Ethereal - Network traffic analyzer
6  * By Gerald Combs <gerald@zing.org>
7  * Copyright 1998 Gerald Combs
8  *
9  * Richard Sharpe, 13-Feb-1999, added support for initializing structures
10  *                              needed by dissect routines
11  * 
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  * 
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  *
26  *
27  * To do:
28  * - Live browser/capture display
29  * - Graphs
30  * - Get AIX to work
31  * - Check for end of packet in dissect_* routines.
32  * - Playback window
33  * - Multiple window support
34  * - Add cut/copy/paste
35  * - Fix progress/status bar glitches?  (GTK+ bug?)
36  * - Create header parsing routines
37  * - Check fopens, freads, fwrites
38  * - Make byte view scrollbars automatic?
39  * - Make byte view selections more fancy?
40  *
41  */
42
43 #ifdef HAVE_CONFIG_H
44 # include "config.h"
45 #endif
46
47 #include <gtk/gtk.h>
48 #include <pcap.h> /* needed for capture.h */
49
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include <errno.h>
55 #include <sys/types.h>
56 #include <sys/stat.h>
57 #include <fcntl.h>
58 #include <netinet/in.h>
59 #include <signal.h>
60
61 #ifdef NEED_SNPRINTF_H
62 # ifdef HAVE_STDARG_H
63 #  include <stdarg.h>
64 # else
65 #  include <varargs.h>
66 # endif
67 # include "snprintf.h"
68 #endif
69
70 #ifdef NEED_STRERROR_H
71 #include "strerror.h"
72 #endif
73
74 #include "ethereal.h"
75 #include "timestamp.h"
76 #include "packet.h"
77 #include "capture.h"
78 #include "summary.h"
79 #include "file.h"
80 #include "menu.h"
81 #include "etypes.h"
82 #include "prefs.h"
83 #include "column.h"
84 #include "print.h"
85 #include "resolv.h"
86 #include "follow.h"
87 #include "util.h"
88 #include "gtkpacket.h"
89 #include "dfilter.h"
90
91 static void file_save_ok_cb(GtkWidget *w, GtkFileSelection *fs);
92 static void file_save_as_ok_cb(GtkWidget *w, GtkFileSelection *fs);
93
94 FILE        *data_out_file = NULL;
95 packet_info  pi;
96 capture_file cf;
97 proto_tree      *protocol_tree = NULL;
98 GtkWidget   *file_sel, *packet_list, *tree_view, *byte_view, *prog_bar,
99             *info_bar;
100 GdkFont     *m_r_font, *m_b_font;
101 GtkStyle    *pl_style;
102 guint        main_ctx, file_ctx;
103 frame_data  *fd;
104 gint         start_capture = 0;
105 gchar        comp_info_str[256];
106 gchar       *ethereal_path = NULL;
107 gchar       *medium_font = MONO_MEDIUM_FONT;
108 gchar       *bold_font = MONO_BOLD_FONT;
109
110 ts_type timestamp_type = RELATIVE;
111
112 GtkStyle *item_style;
113
114 int sync_mode;  /* allow sync */
115 int sync_pipe[2]; /* used to sync father */
116 int fork_mode;  /* fork a child to do the capture */
117 int sigusr2_received = 0;
118 int quit_after_cap; /* Makes a "capture only mode". Implies -k */
119
120 /* Specifies byte offsets for object selected in tree */
121 static gint tree_selected_start=-1, tree_selected_len=-1; 
122
123 #define E_DFILTER_TE_KEY "display_filter_te"
124
125 /* About Ethereal window */
126 void
127 about_ethereal( GtkWidget *w, gpointer data ) {
128   simple_dialog(ESD_TYPE_INFO, NULL,
129                 "GNU Ethereal - network protocol analyzer\n"
130                 "Version %s (C) 1998 Gerald Combs <gerald@zing.org>\n"
131                 "Compiled with %s\n\n"
132                 "Contributors:\n"
133
134                 "Gilbert Ramirez          <gramirez@tivoli.com>\n"
135                 "Hannes R. Boehm          <hannes@boehm.org>\n"
136                 "Mike Hall                <mlh@io.com>\n"
137                 "Bobo Rajec               <bobo@bsp-consulting.sk>\n"
138                 "Laurent Deniel           <deniel@worldnet.fr>\n"
139                 "Don Lafontaine           <lafont02@cn.ca>\n"
140                 "Guy Harris               <guy@netapp.com>\n"
141                 "Simon Wilkinson          <sxw@dcs.ed.ac.uk>\n"
142                 "Joerg Mayer              <jmayer@telemation.de>\n"
143                 "Martin Maciaszek         <fastjack@i-s-o.net>\n"
144                 "Didier Jorand            <Didier.Jorand@alcatel.fr>\n"
145                 "Jun-ichiro itojun Hagino <itojun@iijlab.net>\n"
146                 "Richard Sharpe           <sharpe@ns.aus.com>\n"
147                 "John McDermott           <jjm@jkintl.com>\n"
148                 "Jeff Jahr                <jjahr@shastanets.com>\n"
149                 "Brad Robel-Forrest       <bradr@watchguard.com>\n"
150                 "Ashok Narayanan          <ashokn@cisco.com>\n"
151                 "Aaron Hillegass          <aaron@classmax.com>\n"
152                 "Jason Lango              <jal@netapp.com>\n"
153
154                 "\nSee http://ethereal.zing.org for more information",
155                 VERSION, comp_info_str);
156 }
157
158 /* Things to do when the OK button is pressed */
159 void
160 file_sel_ok_cb(GtkWidget *w, GtkFileSelection *fs) {
161   gchar     *cf_name;
162   int        err;
163
164   cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
165   gtk_widget_hide(GTK_WIDGET (fs));
166   gtk_widget_destroy(GTK_WIDGET (fs));
167
168   /* this depends upon load_cap_file removing the filename from
169    * cf_name, leaving only the path to the directory. */
170   if ((err = load_cap_file(cf_name, &cf)) == 0)
171     chdir(cf_name);
172   else {
173     simple_dialog(ESD_TYPE_WARN, NULL, file_open_error_message(err, FALSE),
174                 cf_name);
175   }
176   g_free(cf_name);
177 #ifdef USE_ITEM
178     set_menu_sensitivity("/File/Save", FALSE);
179     set_menu_sensitivity("/File/Save As...", TRUE);
180     set_menu_sensitivity("/Tools/Summary", TRUE);
181 #else
182     set_menu_sensitivity("<Main>/File/Save", FALSE);
183     set_menu_sensitivity("<Main>/File/Save As...", TRUE);
184     set_menu_sensitivity("<Main>/Tools/Summary", TRUE);
185 #endif
186 }
187
188 /* Update the progress bar */
189 gint
190 file_progress_cb(gpointer p) {
191   gtk_progress_bar_update(GTK_PROGRESS_BAR(prog_bar),
192     (gfloat) ftell(cf.fh) / (gfloat) cf.f_len);
193   return TRUE;
194 }
195
196 /* Follow a TCP stream */
197 void
198 follow_stream_cb( GtkWidget *w, gpointer data ) {
199   char filename1[128];
200   GtkWidget *streamwindow, *box, *text, *vscrollbar, *table;
201   GtkWidget *filter_te = NULL;
202   int err;
203
204   if (w)
205         filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
206
207   if( pi.ipproto == 6 ) {
208     /* we got tcp so we can follow */
209     /* check to see if we are using a filter */
210     if( cf.dfilter != NULL ) {
211       /* get rid of this one */
212       g_free( cf.dfilter );
213       cf.dfilter = NULL;
214     }
215     /* create a new one and set the display filter entry accordingly */
216     cf.dfilter = build_follow_filter( &pi );
217     if (filter_te)
218             gtk_entry_set_text(GTK_ENTRY(filter_te), cf.dfilter);
219     /* reload so it goes in effect. Also we set data_out_file which 
220        tells the tcp code to output the data */
221     close_cap_file( &cf, info_bar, file_ctx);
222     strcpy( filename1, tmpnam(NULL) );
223     data_out_file = fopen( filename1, "a" );
224     if( data_out_file == NULL ) {
225       fprintf( stderr, "Could not open tmp file %s\n", filename1 );
226     }
227     reset_tcp_reassembly();
228     err = load_cap_file( cf.filename, &cf );
229     if (err != 0) {
230       simple_dialog(ESD_TYPE_WARN, NULL, file_open_error_message(err, FALSE),
231                 cf.filename);
232     }
233     /* the data_out_file should now be full of the streams information */
234     fclose( data_out_file );
235     /* the filename1 file now has all the text that was in the session */
236     streamwindow = gtk_window_new( GTK_WINDOW_TOPLEVEL);
237     gtk_widget_set_name( streamwindow, "TCP stream window" );
238     gtk_signal_connect( GTK_OBJECT(streamwindow), "delete_event",
239                         NULL, "WM destroy" );
240     gtk_signal_connect( GTK_OBJECT(streamwindow), "destroy",
241                         NULL, "WM destroy" );
242     if( incomplete_tcp_stream ) {
243       gtk_window_set_title( GTK_WINDOW(streamwindow), 
244                             "Contents of TCP stream (incomplete)" );
245     } else {
246       gtk_window_set_title( GTK_WINDOW(streamwindow),
247                             "Contents of TCP stream" );
248     }
249     gtk_widget_set_usize( GTK_WIDGET(streamwindow), DEF_WIDTH, DEF_HEIGHT );
250     gtk_container_border_width( GTK_CONTAINER(streamwindow), 2 );
251     /* setup the container */
252     box = gtk_vbox_new( FALSE, 0 );
253     gtk_container_add( GTK_CONTAINER(streamwindow), box );
254     gtk_widget_show( box );
255     /* set up the table we attach to */
256     table = gtk_table_new( 1, 2, FALSE );
257     gtk_table_set_col_spacing( GTK_TABLE(table), 0, 2);
258     gtk_box_pack_start( GTK_BOX(box), table, TRUE, TRUE, 0 );
259     gtk_widget_show( table );
260     /* create a text box */
261     text = gtk_text_new( NULL, NULL );
262     gtk_text_set_editable( GTK_TEXT(text), FALSE);
263     gtk_table_attach( GTK_TABLE(table), text, 0, 1, 0, 1,
264                       GTK_EXPAND | GTK_SHRINK | GTK_FILL,
265                       GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0 );
266     gtk_widget_show(text);
267     /* create the scrollbar */
268     vscrollbar = gtk_vscrollbar_new( GTK_TEXT(text)->vadj );
269     gtk_table_attach( GTK_TABLE(table), vscrollbar, 1, 2, 0, 1,
270                       GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0 );
271     gtk_widget_show( vscrollbar );
272     gtk_widget_realize( text );
273     /* stop the updates while we fill the text box */
274     gtk_text_freeze( GTK_TEXT(text) );
275     data_out_file = NULL;
276     data_out_file = fopen( filename1, "r" );
277     if( data_out_file ) {
278       char buffer[1024];
279       int nchars;
280       while( 1 ) {
281         nchars = fread( buffer, 1, 1024, data_out_file );
282         gtk_text_insert( GTK_TEXT(text), m_r_font, NULL, NULL, buffer, nchars );
283         if( nchars < 1024 ) {
284           break;
285         }
286       }
287       fclose( data_out_file );
288       unlink( filename1 );
289     }
290     gtk_text_thaw( GTK_TEXT(text) );
291     data_out_file = NULL;
292     gtk_widget_show( streamwindow );
293     if( cf.dfilter != NULL ) {
294       g_free( cf.dfilter );
295       cf.dfilter = NULL;
296     }
297   } else {
298     simple_dialog(ESD_TYPE_WARN, NULL,
299       "Error following stream.  Please make\n"
300       "sure you have a TCP packet selected.");
301   }
302 }
303
304 /* Match selected byte pattern */
305 void
306 match_selected_cb(GtkWidget *w, gpointer data)
307 {
308     char *buf = malloc(1024);
309     GtkWidget *filter_te = NULL;
310
311     if (w)
312         filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
313
314     if (tree_selected_start<0) {
315         simple_dialog(ESD_TYPE_WARN, NULL,
316                       "Error determining selected bytes.  Please make\n"
317                       "sure you have selected a field within the tree\n"
318                       "view to be matched.");
319         return;
320     }
321 #if 0
322     switch (cf.lnk_t) {
323     case DLT_EN10MB :
324         c="ether";
325         break;
326     case DLT_FDDI :
327         c="fddi";
328         break;
329     default :
330 #endif
331         simple_dialog(ESD_TYPE_WARN, NULL,
332                       "Unsupported frame type format. Only Ethernet and FDDI\n"
333                       "frame formats are supported.");
334         return;
335 #if 0
336     }
337
338     sprintf(buf, "("); ptr = buf+strlen(buf);
339     for (i=0, c=cf.pd+tree_selected_start; i+4<tree_selected_len; i+=4, c+=4) {
340         sprintf(ptr, "(ether[%d : 4]=0x%02X%02X%02X%02X) and ", 
341                tree_selected_start+i, 
342                *c,
343                *(c+1),
344                *(c+2),
345                *(c+3));
346         ptr = buf+strlen(buf);
347     }
348
349     sprintf(ptr, "(ether[%d : %d]=0x", 
350            tree_selected_start+i, 
351            tree_selected_len - i);
352     ptr = buf+strlen(buf);
353     for (;i<tree_selected_len; i++) {
354         sprintf(ptr, "%02X", *c++);
355         ptr = buf+strlen(buf);
356     }
357
358     sprintf(ptr, "))");
359
360     if( cf.dfilter != NULL ) {
361       /* get rid of this one */
362       g_free( cf.dfilter );
363       cf.dfilter = NULL;
364     }
365     /* create a new one and set the display filter entry accordingly */
366     cf.dfilter = buf;
367     if (filter_te)
368         gtk_entry_set_text(GTK_ENTRY(filter_te), cf.dfilter);
369     /* reload so it goes in effect. */
370     close_cap_file( &cf, info_bar, file_ctx);
371     load_cap_file( cf.filename, &cf );
372     if( cf.dfilter != NULL ) {
373       g_free( cf.dfilter );
374       cf.dfilter = NULL;
375     }
376 #endif
377 }
378
379 /* Open a file */
380 void
381 file_open_cmd_cb(GtkWidget *w, gpointer data) {
382   file_sel = gtk_file_selection_new ("Ethereal: Open Capture File");
383   
384   /* Connect the ok_button to file_ok_sel_cb function and pass along the
385      pointer to the filter entry */
386   gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (file_sel)->ok_button),
387     "clicked", (GtkSignalFunc) file_sel_ok_cb, file_sel );
388
389   /* Gilbert --- I added this if statement. Is this right? */
390   if (w)
391   gtk_object_set_data(GTK_OBJECT(GTK_FILE_SELECTION(file_sel)->ok_button),
392     E_DFILTER_TE_KEY, gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY));
393
394   /* Connect the cancel_button to destroy the widget */
395   gtk_signal_connect_object(GTK_OBJECT (GTK_FILE_SELECTION
396     (file_sel)->cancel_button), "clicked", (GtkSignalFunc)
397     gtk_widget_destroy, GTK_OBJECT (file_sel));
398
399   if( fork_mode && (cf.save_file != NULL) )
400     gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_sel), cf.save_file);
401   else
402     gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_sel), "");
403
404   gtk_widget_show(file_sel);
405 }
406
407 /* Close a file */
408 void
409 file_close_cmd_cb(GtkWidget *widget, gpointer data) {
410   close_cap_file(&cf, info_bar, file_ctx);
411 #ifdef USE_ITEM
412   set_menu_sensitivity("/File/Close", FALSE);
413   set_menu_sensitivity("/File/Reload", FALSE);
414   set_menu_sensitivity("/Tools/Summary", FALSE);
415 #else
416   set_menu_sensitivity("<Main>/File/Close", FALSE);
417   set_menu_sensitivity("<Main>/File/Reload", FALSE);
418   set_menu_sensitivity("<Main>/Tools/Summary", FALSE);
419 #endif
420 }
421
422 void
423 file_save_cmd_cb(GtkWidget *w, gpointer data) {
424   file_sel = gtk_file_selection_new ("Ethereal: Save Capture File");
425   
426   /* Connect the ok_button to file_ok_sel_cb function and pass along the
427      pointer to the filter entry */
428   gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (file_sel)->ok_button),
429     "clicked", (GtkSignalFunc) file_save_ok_cb, file_sel );
430
431   /* Connect the cancel_button to destroy the widget */
432   gtk_signal_connect_object(GTK_OBJECT (GTK_FILE_SELECTION
433     (file_sel)->cancel_button), "clicked", (GtkSignalFunc)
434     gtk_widget_destroy, GTK_OBJECT (file_sel));
435
436   gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_sel), "");
437
438   gtk_widget_show(file_sel);
439 }
440
441 void
442 file_save_as_cmd_cb(GtkWidget *w, gpointer data) {
443   file_sel = gtk_file_selection_new ("Ethereal: Save Capture File as");
444
445   /* Connect the ok_button to file_ok_sel_cb function and pass along the
446      pointer to the filter entry */
447   gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (file_sel)->ok_button),
448     "clicked", (GtkSignalFunc) file_save_as_ok_cb, file_sel );
449
450   /* Connect the cancel_button to destroy the widget */
451   gtk_signal_connect_object(GTK_OBJECT (GTK_FILE_SELECTION
452     (file_sel)->cancel_button), "clicked", (GtkSignalFunc)
453     gtk_widget_destroy, GTK_OBJECT (file_sel));
454
455   gtk_file_selection_set_filename(GTK_FILE_SELECTION(file_sel), "");
456   gtk_widget_show(file_sel);
457 }
458
459 static void
460 file_save_ok_cb(GtkWidget *w, GtkFileSelection *fs) {
461         gchar   *cf_name;
462         int     err;
463
464         cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
465         gtk_widget_hide(GTK_WIDGET (fs));
466         gtk_widget_destroy(GTK_WIDGET (fs));
467
468         if (!file_mv(cf.save_file, cf_name))
469                 return;
470         g_free(cf.save_file);
471         cf.save_file = g_strdup(cf_name);
472         cf.user_saved = 1;
473         err = load_cap_file(cf_name, &cf);
474         if (err != 0) {
475                 simple_dialog(ESD_TYPE_WARN, NULL,
476                     file_open_error_message(err, FALSE), cf_name);
477         }
478
479 #ifdef USE_ITEM
480         set_menu_sensitivity("/File/Save", FALSE);
481         set_menu_sensitivity("/File/Save As...", TRUE);
482 #else
483         set_menu_sensitivity("<Main>/File/Save", FALSE);
484         set_menu_sensitivity("<Main>/File/Save As...", TRUE);
485 #endif
486 }
487
488 static void
489 file_save_as_ok_cb(GtkWidget *w, GtkFileSelection *fs) {
490         gchar   *cf_name;
491         int     err;
492
493         cf_name = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs)));
494         gtk_widget_hide(GTK_WIDGET (fs));
495         gtk_widget_destroy(GTK_WIDGET (fs));
496
497         if (!file_cp(cf.save_file, cf_name))
498                 return;
499         g_free(cf.save_file);
500         cf.save_file = g_strdup(cf_name);
501         cf.user_saved = 1;
502         err = load_cap_file(cf_name, &cf);
503         if (err != 0) {
504                 simple_dialog(ESD_TYPE_WARN, NULL,
505                     file_open_error_message(err, FALSE), cf_name);
506         }
507
508 #ifdef USE_ITEM
509         set_menu_sensitivity("/File/Save", FALSE);
510         set_menu_sensitivity("/File/Save As...", TRUE);
511 #else
512         set_menu_sensitivity("<Main>/File/Save", FALSE);
513         set_menu_sensitivity("<Main>/File/Save As...", TRUE);
514 #endif
515 }
516
517 /* Reload a file using the current display filter */
518 void
519 file_reload_cmd_cb(GtkWidget *w, gpointer data) {
520   /*GtkWidget *filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);*/
521   GtkWidget *filter_te;
522   int err;
523
524   filter_te = gtk_object_get_data(GTK_OBJECT(w), E_DFILTER_TE_KEY);
525
526   if (cf.dfilter) g_free(cf.dfilter);
527   cf.dfilter = g_strdup(gtk_entry_get_text(GTK_ENTRY(filter_te)));
528   err = load_cap_file(cf.filename, &cf);
529   if (err != 0) {
530     simple_dialog(ESD_TYPE_WARN, NULL, file_open_error_message(err, FALSE),
531                 cf.filename);
532   }
533 }
534
535 /* Print a packet */
536 void
537 file_print_cmd_cb(GtkWidget *widget, gpointer data) {
538     print_tree(cf.pd, fd, GTK_TREE(tree_view));
539 }
540
541 /* What to do when a list item is selected/unselected */
542 void
543 packet_list_select_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
544
545   if (!sync_mode) {
546   if (cf.wth) return; 
547   }
548   blank_packetinfo();
549   gtk_text_freeze(GTK_TEXT(byte_view));
550   gtk_text_set_point(GTK_TEXT(byte_view), 0);
551   gtk_text_forward_delete(GTK_TEXT(byte_view),
552   gtk_text_get_length(GTK_TEXT(byte_view)));
553
554   /* get the frame data struct pointer for this frame */
555   fd = (frame_data *) gtk_clist_get_row_data(GTK_CLIST(w), row);
556   fseek(cf.fh, fd->file_off, SEEK_SET);
557   fread(cf.pd, sizeof(guint8), fd->cap_len, cf.fh);
558
559   /* create the logical protocol tree */
560   if (protocol_tree)
561       proto_tree_free(protocol_tree);
562   protocol_tree = proto_tree_create_root();
563   dissect_packet(cf.pd, fd, protocol_tree);
564
565   /* display the GUI protocol tree and hex dump */
566   proto_tree_draw(protocol_tree, tree_view);
567   packet_hex_print(GTK_TEXT(byte_view), cf.pd, fd->cap_len, -1, -1);
568   gtk_text_thaw(GTK_TEXT(byte_view));
569 }
570
571 void
572 packet_list_unselect_cb(GtkWidget *w, gint row, gint col, gpointer evt) {
573   gtk_text_freeze(GTK_TEXT(byte_view));
574   gtk_text_set_point(GTK_TEXT(byte_view), 0);
575   gtk_text_forward_delete(GTK_TEXT(byte_view),
576     gtk_text_get_length(GTK_TEXT(byte_view)));
577   gtk_text_thaw(GTK_TEXT(byte_view));
578   gtk_tree_clear_items(GTK_TREE(tree_view), 0,
579     g_list_length(GTK_TREE(tree_view)->children));
580 }
581
582 void
583 tree_view_cb(GtkWidget *w) {
584
585   tree_selected_start = -1;
586   tree_selected_len = -1;
587
588   if (GTK_TREE(w)->selection) {
589     tree_selected_start = 
590         (gint) gtk_object_get_data(GTK_OBJECT(GTK_TREE(w)->selection->data),
591                                    E_TREEINFO_START_KEY);
592     tree_selected_len   = 
593         (gint) gtk_object_get_data(GTK_OBJECT(GTK_TREE(w)->selection->data),
594                                    E_TREEINFO_LEN_KEY);
595   }
596
597   gtk_text_freeze(GTK_TEXT(byte_view));
598   gtk_text_set_point(GTK_TEXT(byte_view), 0);
599   gtk_text_forward_delete(GTK_TEXT(byte_view),
600     gtk_text_get_length(GTK_TEXT(byte_view)));
601   packet_hex_print(GTK_TEXT(byte_view), cf.pd, fd->cap_len, 
602                    tree_selected_start, 
603                    tree_selected_len);
604   
605   gtk_text_thaw(GTK_TEXT(byte_view));
606 }
607
608 void
609 file_quit_cmd_cb (GtkWidget *widget, gpointer data) {
610   if (cf.save_file && !cf.user_saved) {
611         unlink(cf.save_file);
612   }
613   gtk_exit(0);
614 }
615
616 void blank_packetinfo() {
617   pi.srcip    = 0;
618   pi.destip   = 0;
619   pi.ipproto  = 0;
620   pi.srcport  = 0;
621   pi.destport = 0;
622 }
623
624 /* Things to do when the main window is realized */
625 void
626 main_realize_cb(GtkWidget *w, gpointer data) {
627   if (start_capture) {
628     capture();
629     start_capture = 0;
630   }
631 }
632
633 static void 
634 sigusr2_handler(int sig) {
635   sigusr2_received = 1;
636   signal(SIGUSR2, sigusr2_handler);
637 }
638
639 /* call initialization routines at program startup time */
640 static void
641 ethereal_proto_init(void) {
642   proto_init();
643   init_dissect_udp();
644   dfilter_init();
645 }
646
647 static void 
648 print_usage(void) {
649
650   fprintf(stderr, "This is GNU %s %s, compiled with %s\n", PACKAGE,
651           VERSION, comp_info_str);
652   fprintf(stderr, "%s [-vh] [-FkQS] [-b bold font] [-B byte view height] [-c count]\n",
653           PACKAGE);
654   fprintf(stderr, "         [-f \"filter expression\"] [-i interface] [-m medium font] [-n]\n");
655   fprintf(stderr, "         [-P packet list height] [-r infile] [-s snaplen]\n");
656   fprintf(stderr, "         [-t <time stamp format>] [-T tree view height] [-w savefile] \n");
657 }
658
659 /* And now our feature presentation... [ fade to music ] */
660 int
661 main(int argc, char *argv[])
662 {
663   int                  opt, i;
664   extern char         *optarg;
665   char                *pf_path;
666   int                 pf_open_errno = 0;
667   int                 err;
668   GtkWidget           *window, *main_vbox, *menubar, *u_pane, *l_pane,
669                       *bv_table, *bv_hscroll, *bv_vscroll, *stat_hbox, 
670                       *tv_scrollw, *filter_bt, *filter_te;
671 #ifdef GTK_HAVE_FEATURES_1_1_0
672   GtkAccelGroup *accel;
673 #else
674   GtkAcceleratorTable *accel;
675 #endif
676
677 #ifdef GTK_HAVE_FEATURES_1_1_4
678   GtkWidget     *packet_sw;
679 #endif
680   gint                 pl_size = 280, tv_size = 95, bv_size = 75;
681   gchar               *rc_file, *cf_name = NULL;
682   e_prefs             *prefs;
683   gint                *col_fmt;
684
685   ethereal_path = argv[0];
686
687   /* Let GTK get its args */
688   gtk_init (&argc, &argv);
689   
690
691   prefs = read_prefs(&pf_path);
692   if (pf_path != NULL) {
693     /* The preferences file exists, but couldn't be opened; "pf_path" is
694        its pathname.  Remember "errno", as that says why the attempt
695        failed. */
696     pf_open_errno = errno;
697   }
698     
699   /* Initialize the capture file struct */
700   cf.plist              = NULL;
701   cf.wth                = NULL;
702   cf.fh                 = NULL;
703   cf.dfilter            = NULL;
704   cf.cfilter            = NULL;
705   cf.dfcode             = NULL;
706   cf.iface              = NULL;
707   cf.save_file          = NULL;
708   cf.user_saved         = 0;
709   cf.snap               = MAX_PACKET_SIZE;
710   cf.count              = 0;
711   cf.cinfo.num_cols     = prefs->num_cols;
712   cf.cinfo.col_title    = (gchar **) g_malloc(sizeof(gchar *) * cf.cinfo.num_cols);
713   cf.cinfo.fmt_matx     = (gboolean **) g_malloc(sizeof(gboolean *) * cf.cinfo.num_cols);
714   cf.cinfo.col_data     = (gchar **) g_malloc(sizeof(gchar *) * cf.cinfo.num_cols);
715   cf.cinfo.col_width    = (gint *) g_malloc(sizeof(gint) * cf.cinfo.num_cols);
716
717   /* Assemble the compile-time options */
718   snprintf(comp_info_str, 256,
719 #ifdef GTK_MAJOR_VERSION
720     "GTK+ %d.%d.%d", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
721     GTK_MICRO_VERSION
722 #else
723     "GTK+ (version unknown)"
724 #endif
725    );
726
727   /* Now get our args */
728   while ((opt = getopt(argc, argv, "b:B:c:f:Fhi:km:nP:Qr:Ss:t:T:w:v")) != EOF) {
729     switch (opt) {
730       case 'b':        /* Bold font */
731         bold_font = g_strdup(optarg);
732         break;
733       case 'B':        /* Byte view pane height */
734         bv_size = atoi(optarg);
735         break;
736       case 'c':        /* Capture xxx packets */
737         cf.count = atoi(optarg);
738         break;
739       case 'f':
740         cf.cfilter = g_strdup(optarg);
741         break;
742       case 'F':        /* Fork to capture */
743         fork_mode = 1;
744         break;
745       case 'h':        /* Print help and exit */
746         print_usage();
747         exit(0);
748         break;
749       case 'i':        /* Use interface xxx */
750         cf.iface = g_strdup(optarg);
751         break;
752       case 'm':        /* Medium font */
753         medium_font = g_strdup(optarg);
754         break;
755       case 'n':        /* No name resolution */
756         g_resolving_actif = 0;
757         break;
758       case 'k':        /* Start capture immediately */
759         start_capture = 1;
760         break;
761       case 'P':        /* Packet list pane height */
762         pl_size = atoi(optarg);
763         break;
764       case 'Q':        /* Quit after capture (just capture to file) */
765         quit_after_cap = 1;
766         start_capture = 1;  /*** -Q implies -k !! ***/
767         break;
768       case 'r':        /* Read capture file xxx */
769         cf_name = g_strdup(optarg);
770         break;
771       case 's':        /* Set the snapshot (capture) length */
772         cf.snap = atoi(optarg);
773         break;
774       case 'S':        /* "Sync" mode: used for following file ala tail -f */
775         sync_mode = 1;
776         fork_mode = 1; /* -S implies -F */
777         break;
778       case 't':        /* Time stamp type */
779         if (strcmp(optarg, "r") == 0)
780           timestamp_type = RELATIVE;
781         else if (strcmp(optarg, "a") == 0)
782           timestamp_type = ABSOLUTE;
783         else if (strcmp(optarg, "d") == 0)
784           timestamp_type = DELTA;
785         else {
786           fprintf(stderr, "ethereal: Invalid time stamp type \"%s\"\n",
787             optarg);
788           fprintf(stderr, "It must be \"r\" for relative, \"a\" for absolute,\n");
789           fprintf(stderr, "or \"d\" for delta.\n");
790           exit(1);
791         }
792         break;
793       case 'T':        /* Tree view pane height */
794         tv_size = atoi(optarg);
795         break;
796       case 'v':        /* Show version and exit */
797         printf("%s %s, with %s\n", PACKAGE, VERSION, comp_info_str);
798         exit(0);
799         break;
800       case 'w':        /* Write capture file xxx */
801         cf.save_file = g_strdup(optarg);
802         break;
803     }
804   }
805
806   if (start_capture) {
807     if (cf.iface == NULL) {
808       fprintf(stderr, "ethereal: \"-k\" flag was specified without \"-i\" flag\n");
809       exit(1);
810     }
811     if (cf.save_file == NULL) {
812       fprintf(stderr, "ethereal: \"-k\" flag was specified without \"-w\" flag\n");
813       exit(1);
814     }
815   }
816
817   if (sync_mode)
818     signal(SIGUSR2, sigusr2_handler);
819
820   /* Build the column format array */  
821   col_fmt   = (gint *) g_malloc(sizeof(gint) * cf.cinfo.num_cols);
822   
823   for (i = 0; i < cf.cinfo.num_cols; i++) {
824     col_fmt[i]   = get_column_format(i);
825     cf.cinfo.col_title[i] = g_strdup(get_column_title(i));
826     cf.cinfo.fmt_matx[i] = (gboolean *) g_malloc0(sizeof(gboolean) *
827       NUM_COL_FMTS);
828     get_column_format_matches(cf.cinfo.fmt_matx[i], col_fmt[i]);
829     cf.cinfo.col_data[i] = (gchar *) g_malloc(sizeof(gchar) * COL_MAX_LEN);
830     cf.cinfo.col_width[i] = 0;
831   }
832
833   if (cf.snap < 1)
834     cf.snap = MAX_PACKET_SIZE;
835   else if (cf.snap < MIN_PACKET_SIZE)
836     cf.snap = MIN_PACKET_SIZE;
837   
838   rc_file = (gchar *) g_malloc(strlen(getenv("HOME")) + strlen(RC_FILE) + 4);
839   sprintf(rc_file, "%s/%s", getenv("HOME"), RC_FILE);
840   gtk_rc_parse(rc_file);
841
842   if ((m_r_font = gdk_font_load(medium_font)) == NULL) {
843     fprintf(stderr, "ethereal: Error font %s not found (use -m option)\n", medium_font);
844     exit(1);
845   }
846
847   if ((m_b_font = gdk_font_load(bold_font)) == NULL) {
848     fprintf(stderr, "ethereal: Error font %s not found (use -b option)\n", bold_font);
849     exit(1);
850   }
851
852   /* Main window */  
853   window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
854   gtk_widget_set_name(window, "main window");
855   gtk_signal_connect(GTK_OBJECT(window), "delete_event",
856     GTK_SIGNAL_FUNC(file_quit_cmd_cb), "WM destroy");
857   gtk_signal_connect(GTK_OBJECT(window), "destroy", 
858     GTK_SIGNAL_FUNC(file_quit_cmd_cb), "WM destroy");
859   gtk_signal_connect(GTK_OBJECT (window), "realize",
860     GTK_SIGNAL_FUNC(main_realize_cb), NULL);
861   gtk_window_set_title(GTK_WINDOW(window), "The Ethereal Network Analyzer");
862   gtk_widget_set_usize(GTK_WIDGET(window), DEF_WIDTH, -1);
863   gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE);
864
865   /* Container for menu bar, paned windows and progress/info box */
866   main_vbox = gtk_vbox_new(FALSE, 1);
867   gtk_container_border_width(GTK_CONTAINER(main_vbox), 1);
868   gtk_container_add(GTK_CONTAINER(window), main_vbox);
869   gtk_widget_show(main_vbox);
870
871   /* Menu bar */
872   get_main_menu(&menubar, &accel);
873 #ifdef GTK_HAVE_FEATURES_1_1_0
874   gtk_window_add_accel_group(GTK_WINDOW(window), accel);
875 #else
876   gtk_window_add_accelerator_table(GTK_WINDOW(window), accel);
877 #endif
878   gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
879   gtk_widget_show(menubar);
880
881   /* Panes for the packet list, tree, and byte view */
882   u_pane = gtk_vpaned_new();
883   gtk_paned_gutter_size(GTK_PANED(u_pane), (GTK_PANED(u_pane))->handle_size);
884   l_pane = gtk_vpaned_new();
885   gtk_paned_gutter_size(GTK_PANED(l_pane), (GTK_PANED(l_pane))->handle_size);
886   gtk_container_add(GTK_CONTAINER(main_vbox), u_pane);
887   gtk_widget_show(u_pane);
888   gtk_paned_add2 (GTK_PANED(u_pane), l_pane);
889   gtk_widget_show(l_pane);
890
891   /* Packet list */
892   packet_list = gtk_clist_new_with_titles(cf.cinfo.num_cols,
893     cf.cinfo.col_title);
894   gtk_clist_column_titles_passive(GTK_CLIST(packet_list));
895 #ifdef GTK_HAVE_FEATURES_1_1_4
896   packet_sw = gtk_scrolled_window_new(NULL, NULL);
897   gtk_widget_show(packet_sw);
898   gtk_container_add(GTK_CONTAINER(packet_sw), packet_list);
899 #endif
900   pl_style = gtk_style_new();
901   gdk_font_unref(pl_style->font);
902   pl_style->font = m_r_font;
903   gtk_widget_set_style(packet_list, pl_style);
904   gtk_widget_set_name(packet_list, "packet list");
905   gtk_signal_connect(GTK_OBJECT(packet_list), "select_row",
906     GTK_SIGNAL_FUNC(packet_list_select_cb), NULL);
907   gtk_signal_connect(GTK_OBJECT(packet_list), "unselect_row",
908     GTK_SIGNAL_FUNC(packet_list_unselect_cb), NULL);
909   for (i = 0; i < cf.cinfo.num_cols; i++) {
910     gtk_clist_set_column_width(GTK_CLIST(packet_list), i,
911       gdk_string_width(pl_style->font, cf.cinfo.col_title[i]));
912     if (col_fmt[i] == COL_NUMBER)
913       gtk_clist_set_column_justification(GTK_CLIST(packet_list), i, 
914         GTK_JUSTIFY_RIGHT);
915   }
916   gtk_widget_set_usize(packet_list, -1, pl_size);
917 #ifdef GTK_HAVE_FEATURES_1_1_4
918   gtk_paned_add1(GTK_PANED(u_pane), packet_sw);
919 #else
920   gtk_paned_add1(GTK_PANED(u_pane), packet_list);
921 #endif
922   gtk_widget_show(packet_list);
923   
924   /* Tree view */
925   tv_scrollw = gtk_scrolled_window_new(NULL, NULL);
926   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(tv_scrollw),
927     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
928   gtk_paned_add1(GTK_PANED(l_pane), tv_scrollw);
929   gtk_widget_set_usize(tv_scrollw, -1, tv_size);
930   gtk_widget_show(tv_scrollw);
931   
932   tree_view = gtk_tree_new();
933 #ifdef GTK_HAVE_FEATURES_1_1_4
934   gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(tv_scrollw),
935                   tree_view);
936 #else
937   gtk_container_add(GTK_CONTAINER(tv_scrollw), tree_view);
938 #endif
939   gtk_tree_set_selection_mode(GTK_TREE(tree_view), GTK_SELECTION_SINGLE);
940   gtk_tree_set_view_lines(GTK_TREE(tree_view), FALSE);
941   gtk_tree_set_view_mode(GTK_TREE(tree_view), TRUE);
942   gtk_signal_connect(GTK_OBJECT(tree_view), "selection_changed",
943     GTK_SIGNAL_FUNC(tree_view_cb), NULL);
944   gtk_widget_show(tree_view);
945
946   item_style = gtk_style_new();
947   gdk_font_unref(item_style->font);
948   item_style->font = m_r_font;
949
950   /* Byte view */
951   bv_table = gtk_table_new (2, 2, FALSE);
952   gtk_paned_add2(GTK_PANED(l_pane), bv_table);
953   gtk_widget_set_usize(bv_table, -1, bv_size);
954   gtk_widget_show(bv_table);
955
956   byte_view = gtk_text_new(NULL, NULL);
957   gtk_text_set_editable(GTK_TEXT(byte_view), FALSE);
958   gtk_text_set_word_wrap(GTK_TEXT(byte_view), FALSE);
959   gtk_table_attach (GTK_TABLE (bv_table), byte_view, 0, 1, 0, 1,
960     GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND | GTK_SHRINK, 0, 0);
961   gtk_widget_show(byte_view);
962
963   bv_hscroll = gtk_hscrollbar_new(GTK_TEXT(byte_view)->hadj);
964   gtk_table_attach(GTK_TABLE(bv_table), bv_hscroll, 0, 1, 1, 2,
965     GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
966   gtk_widget_show (bv_hscroll);
967
968   bv_vscroll = gtk_vscrollbar_new(GTK_TEXT(byte_view)->vadj);
969   gtk_table_attach(GTK_TABLE(bv_table), bv_vscroll, 1, 2, 0, 1,
970     GTK_FILL, GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0);
971   gtk_widget_show(bv_vscroll);
972   
973   /* Progress/filter/info box */
974   stat_hbox = gtk_hbox_new(FALSE, 1);
975   gtk_container_border_width(GTK_CONTAINER(stat_hbox), 0);
976   gtk_box_pack_start(GTK_BOX(main_vbox), stat_hbox, FALSE, TRUE, 0);
977   gtk_widget_show(stat_hbox);
978
979   prog_bar = gtk_progress_bar_new();  
980   gtk_box_pack_start(GTK_BOX(stat_hbox), prog_bar, FALSE, TRUE, 3);
981   gtk_widget_show(prog_bar);
982
983   filter_bt = gtk_button_new_with_label("Filter:");
984   gtk_signal_connect(GTK_OBJECT(filter_bt), "clicked",
985     GTK_SIGNAL_FUNC(prefs_cb), (gpointer) E_PR_PG_FILTER);
986   gtk_box_pack_start(GTK_BOX(stat_hbox), filter_bt, FALSE, TRUE, 0);
987   gtk_widget_show(filter_bt);
988   
989   filter_te = gtk_entry_new();
990   gtk_object_set_data(GTK_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
991   gtk_box_pack_start(GTK_BOX(stat_hbox), filter_te, TRUE, TRUE, 3);
992   gtk_widget_show(filter_te);
993
994 #ifdef USE_ITEM
995   set_menu_object_data("/File/Open...", E_DFILTER_TE_KEY, filter_te);
996   set_menu_object_data("/File/Reload", E_DFILTER_TE_KEY, filter_te);
997   set_menu_object_data("/Tools/Follow TCP Stream", E_DFILTER_TE_KEY,
998     filter_te);
999 #else
1000   set_menu_object_data("<Main>/File/Open...", E_DFILTER_TE_KEY, filter_te);
1001   set_menu_object_data("<Main>/File/Reload", E_DFILTER_TE_KEY, filter_te);
1002   set_menu_object_data("<Main>/Tools/Follow TCP Stream", E_DFILTER_TE_KEY,
1003     filter_te);
1004 #endif
1005   info_bar = gtk_statusbar_new();
1006   main_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "main");
1007   file_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "file");
1008   gtk_statusbar_push(GTK_STATUSBAR(info_bar), main_ctx, DEF_READY_MESSAGE);
1009   gtk_box_pack_start(GTK_BOX(stat_hbox), info_bar, TRUE, TRUE, 0);
1010   gtk_widget_show(info_bar);
1011
1012 /* 
1013    Hmmm should we do it here
1014 */
1015
1016   ethereal_proto_init();   /* Init anything that needs initializing */
1017
1018   gtk_widget_show(window);
1019
1020   /* If we were given the name of a capture file, read it in now;
1021      we defer it until now, so that, if we can't open it, and pop
1022      up an alert box, the alert box is more likely to cmoe up on
1023      top of the main window - but before the preference-file-error
1024      alert box, so, if we get one of those, it's more likely to come
1025      up on top of us. */
1026   if (cf_name) {
1027     err = load_cap_file(cf_name, &cf);
1028     if (err != 0) {
1029       simple_dialog(ESD_TYPE_WARN, NULL, file_open_error_message(err, FALSE),
1030                 cf_name);
1031     }
1032     cf_name[0] = '\0';
1033 #ifdef USE_ITEM
1034     set_menu_sensitivity("/File/Save As...", TRUE);
1035     set_menu_sensitivity("/Tools/Summary", TRUE);
1036 #else
1037     set_menu_sensitivity("<Main>/File/Save As...", TRUE);
1038     set_menu_sensitivity("<Main>/Tools/Summary", TRUE);
1039 #endif
1040   }
1041
1042   /* If we failed to open the preferences file, pop up an alert box;
1043      we defer it until now, so that the alert box is more likely to
1044      come up on top of the main window. */
1045   if (pf_path != NULL) {
1046       simple_dialog(ESD_TYPE_WARN, NULL,
1047         "Can't open preferences file\n\"%s\": %s.", pf_path,
1048         strerror(pf_open_errno));
1049   }
1050
1051   gtk_main();
1052
1053   exit(0);
1054 }