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