"hex_str_to_bytes()" modifies the GByteArray supplied to it, so don't
[obnox/wireshark/wip.git] / gtk / bootp_stat.c
1 /* bootp_stat.c
2  * boop_stat   2003 Jean-Michel FAYARD
3  *
4  * $Id: bootp_stat.c,v 1.9 2003/12/17 22:13:06 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  * 
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  * 
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 /* #define DEBUG        do{ printf("%s:%d  ",__FILE__,__LINE__);} while(0); */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <gtk/gtk.h>
31 #include <stdio.h>
32 #include <string.h>
33
34 #include "epan/packet_info.h"
35 #include "epan/epan.h"
36 #include "menu.h"
37 #include "simple_dialog.h"
38 #include "dlg_utils.h"
39 #include "tap.h"
40 #include "../register.h"
41 #include "../globals.h"
42 #include "compat_macros.h"
43
44 typedef const char* bootp_info_value_t;
45
46 /* used to keep track of the statictics for an entire program interface */
47 typedef struct _dhcp_stats_t {
48         char            *filter;
49         GtkWidget       *win;
50         GHashTable      *hash;
51         GtkWidget       *table_message_type;
52         guint            index; /* Number of  to display */
53 } dhcpstat_t;
54 /* used to keep track of a single DHCP message type */
55 typedef struct _dhcp_message_type_t {
56         const char      *name;
57         guint32          packets;
58         GtkWidget       *widget;/* label in which we print the number of packets */
59         dhcpstat_t      *sp;    /* entire program interface */
60 } dhcp_message_type_t;
61
62 static GtkWidget *dlg=NULL;
63 static GtkWidget *filter_entry;
64
65 static void
66 dhcp_free_hash( gpointer key _U_ , gpointer value, gpointer user_data _U_ )
67 {
68         g_free(value);
69 }
70 static void
71 dhcp_reset_hash(gchar *key _U_ , dhcp_message_type_t *data, gpointer ptr _U_ ) 
72 {       
73         data->packets = 0;
74 }
75
76 /* Update the entry corresponding to the number of packets of a special DHCP Message Type
77  * or create it if it don't exist.
78  */
79 static void
80 dhcp_draw_message_type(gchar *key _U_, dhcp_message_type_t *data, gchar * string_buff )
81 {
82         if ((data==NULL) || (data->packets==0))
83                 return;
84         if (data->widget==NULL){        /* create an entry in the table */
85                 GtkWidget       *tmp;
86                 int x = 2*((data->sp->index) % 2);
87                 int y = (data->sp->index) /2;
88
89
90                 /* Maybe we should display the hexadecimal value ? */
91                 /* sprintf(string_buff, "%s  (0X%x)", data->name, *key); */
92                 tmp = gtk_label_new( data->name  /* string_buff */ );
93                 gtk_table_attach_defaults(GTK_TABLE(data->sp->table_message_type), tmp, x, x+1, y, y+1);
94                 gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
95                 gtk_widget_show(tmp);
96
97                 sprintf( string_buff, "%9d", data->packets );
98                 data->widget = gtk_label_new( string_buff );
99                 gtk_table_attach_defaults(GTK_TABLE(data->sp->table_message_type), data->widget, x+1, x+2, y, y+1);
100                 gtk_label_set_justify(GTK_LABEL(data->widget), GTK_JUSTIFY_LEFT);
101                 gtk_widget_show( data->widget );
102
103                 data->sp->index++;
104         } else {
105                 /* Just update the label string */
106                 sprintf( string_buff, "%9d", data->packets );
107                 gtk_label_set( GTK_LABEL(data->widget), string_buff);
108         }
109 }
110 static void
111 dhcpstat_reset(void *psp)
112 {
113         dhcpstat_t *sp=psp;
114         g_hash_table_foreach( sp->hash, (GHFunc)dhcp_reset_hash, NULL); 
115 }
116 static int
117 dhcpstat_packet(void *psp, packet_info *pinfo _U_, epan_dissect_t *edt _U_, void *pri)
118 {
119         dhcpstat_t *sp=psp;
120         bootp_info_value_t value=pri;
121         dhcp_message_type_t *sc;
122
123         if (sp==NULL)
124                 return 0;
125         sc = g_hash_table_lookup( 
126                         sp->hash, 
127                         value);
128         if (!sc) {
129                 /*g_warning("%s:%d What's Wrong for %s, doc ?", __FILE__, __LINE__, value);*/
130                 sc = g_malloc( sizeof(dhcp_message_type_t) );
131                 sc -> packets = 1;
132                 sc -> name = value;
133                 sc -> widget=NULL;
134                 sc -> sp = sp;
135                 g_hash_table_insert(
136                                 sp->hash,
137                                 (gpointer) value,
138                                 sc);
139         } else {
140                 /*g_warning("sc(%s)->packets++", sc->name);*/
141                 sc->packets++;
142         }
143         return 1;
144 }
145
146
147 static void
148 dhcpstat_draw(void *psp)
149 {
150         dhcpstat_t *sp=psp;
151         char str[256];
152         guint index;
153
154         index=sp->index;
155         g_hash_table_foreach( sp->hash, (GHFunc) dhcp_draw_message_type, str );
156         if (index != sp->index){
157                 /* We have inserted a new entry corresponding to a status code ,
158                  * let's resize the table */
159                 gtk_table_resize ( GTK_TABLE(sp->table_message_type), sp->index  % 2 , 4);
160         }
161         
162 }
163
164
165
166 /* since the gtk2 implementation of tap is multithreaded we must protect
167  * remove_tap_listener() from modifying the list while draw_tap_listener()
168  * is running.  the other protected block is in main.c
169  *
170  * there should not be any other critical regions in gtk2
171  */
172 void protect_thread_critical_region(void);
173 void unprotect_thread_critical_region(void);
174 static void
175 win_destroy_cb(GtkWindow *win _U_, gpointer data)
176 {
177         dhcpstat_t *sp=(dhcpstat_t *)data;
178
179         protect_thread_critical_region();
180         remove_tap_listener(sp);
181         unprotect_thread_critical_region();
182
183         g_free(sp->filter);
184         g_hash_table_foreach( sp->hash, (GHFunc)dhcp_free_hash, NULL);
185         g_hash_table_destroy( sp->hash);        
186         g_free(sp);
187 }
188
189
190
191 /* When called, this function will create a new instance of gtk2-dhcpstat.
192  */
193 static void
194 gtk_dhcpstat_init(char *optarg)
195 {
196         dhcpstat_t *sp;
197         char            *filter=NULL;
198         char            *title=NULL;
199         GString         *error_string;
200         GtkWidget       *message_type_fr;
201         
202         if (!strncmp (optarg, "bootp,stat,", 11)){
203                 filter=optarg+11;
204         } else {
205                 filter=NULL;
206         }
207         
208         sp = g_malloc( sizeof(dhcpstat_t) );
209         sp->hash = g_hash_table_new( g_str_hash, g_str_equal);
210         if(filter){
211                 sp->filter=g_malloc(strlen(filter)+1);
212                 strcpy(sp->filter,filter);
213                 title=g_strdup_printf("DHCP statistics with filter: %s", filter);
214         } else {
215                 sp->filter=NULL;
216                 title=g_strdup("DHCP statistics");
217         }
218
219         /* top level window */
220         sp->win = gtk_window_new( GTK_WINDOW_TOPLEVEL);
221         gtk_window_set_title( GTK_WINDOW(sp->win), title );
222         g_free(title);
223         SIGNAL_CONNECT( sp->win, "destroy", win_destroy_cb, sp);
224
225         /* Status Codes frame */
226         message_type_fr = gtk_frame_new("DHCP Message Type");
227         gtk_container_add(GTK_CONTAINER(sp->win), message_type_fr);
228         gtk_widget_show(message_type_fr);
229         
230         sp->table_message_type = gtk_table_new( 0, 4, FALSE);
231         gtk_table_set_col_spacings( GTK_TABLE(sp->table_message_type), 10);
232         gtk_container_add( GTK_CONTAINER( message_type_fr), sp->table_message_type);
233         gtk_container_set_border_width( GTK_CONTAINER(sp->table_message_type) , 10);
234         sp->index = 0;          /* Nothing to display yet */
235
236
237         error_string = register_tap_listener( 
238                         "bootp",
239                         sp,
240                         filter,
241                         dhcpstat_reset,
242                         dhcpstat_packet,
243                         dhcpstat_draw);
244         if (error_string){
245                 /* error, we failed to attach to the tap. clean up */
246                 simple_dialog( ESD_TYPE_WARN, NULL, error_string->str );
247                 g_free(sp->filter);
248                 g_free(sp);
249                 g_string_free(error_string, TRUE);
250                 return ;
251         }
252         if (dlg){
253                 gtk_widget_destroy( dlg );
254         }
255         gtk_widget_show_all( sp->win );
256         redissect_packets(&cfile);
257 }
258
259
260
261 static void
262 dhcp_start_button_clicked(GtkWidget *item _U_, gpointer data _U_)
263 {
264         GString *str;
265         char *filter;
266
267         str = g_string_new("dhcp,stat,");
268         filter=(char *)gtk_entry_get_text(GTK_ENTRY(filter_entry));
269         str = g_string_append(str, filter);
270         gtk_dhcpstat_init(str->str);
271         g_string_free(str, TRUE);
272 }
273
274 static void
275 dlg_destroy_cb(void)
276 {
277         dlg=NULL;
278 }
279
280 static void
281 dlg_cancel_cb(GtkWidget *cancel_bt _U_, gpointer parent_w)
282 {
283         gtk_widget_destroy(GTK_WIDGET(parent_w));
284 }
285
286 static void
287 gtk_dhcpstat_cb(GtkWidget *w _U_, gpointer d _U_)
288 {
289         GtkWidget *dlg_box;
290         GtkWidget *filter_box, *filter_label;
291         GtkWidget *bbox, *start_button, *cancel_button;
292
293         /* if the window is already open, bring it to front */
294         if(dlg){
295                 gdk_window_raise(dlg->window);
296                 return;
297         }
298
299         dlg=dlg_window_new("Ethereal: Compute DHCP statistics");
300         SIGNAL_CONNECT(dlg, "destroy", dlg_destroy_cb, NULL);
301
302         dlg_box=gtk_vbox_new(FALSE, 10);
303         gtk_container_border_width(GTK_CONTAINER(dlg_box), 10);
304         gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
305         gtk_widget_show(dlg_box);
306
307         /* Filter box */
308         filter_box=gtk_hbox_new(FALSE, 3);
309
310         /* Filter label */
311         filter_label=gtk_label_new("Filter:");
312         gtk_box_pack_start(GTK_BOX(filter_box), filter_label, FALSE, FALSE, 0);
313         gtk_widget_show(filter_label);
314
315         /* Filter entry */
316         filter_entry=gtk_entry_new();
317         WIDGET_SET_SIZE(filter_entry, 300, -2);
318         gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, TRUE, TRUE, 0);
319         gtk_widget_show(filter_entry);
320         
321         gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
322         gtk_widget_show(filter_box);
323
324         /* button box */
325         bbox = gtk_hbutton_box_new();
326         gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_DEFAULT_STYLE);
327         gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
328         gtk_box_pack_start(GTK_BOX(dlg_box), bbox, FALSE, FALSE, 0);
329         gtk_widget_show(bbox);
330
331         /* the start button */
332         start_button=gtk_button_new_with_label("Create Stat");
333         SIGNAL_CONNECT_OBJECT(start_button, "clicked",
334                               dhcp_start_button_clicked, NULL);
335         gtk_box_pack_start(GTK_BOX(bbox), start_button, TRUE, TRUE, 0);
336         GTK_WIDGET_SET_FLAGS(start_button, GTK_CAN_DEFAULT);
337         gtk_widget_grab_default(start_button);
338         gtk_widget_show(start_button);
339
340 #if GTK_MAJOR_VERSION < 2
341         cancel_button=gtk_button_new_with_label("Cancel");
342 #else
343         cancel_button=gtk_button_new_from_stock(GTK_STOCK_CANCEL);
344 #endif
345         SIGNAL_CONNECT(cancel_button, "clicked", dlg_cancel_cb, dlg);
346         GTK_WIDGET_SET_FLAGS(cancel_button, GTK_CAN_DEFAULT);
347         gtk_box_pack_start(GTK_BOX(bbox), cancel_button, TRUE, TRUE, 0);
348         gtk_widget_show(cancel_button);
349
350         /* Catch the "activate" signal on the filter text entry, so that
351            if the user types Return there, we act as if the "Create Stat"
352            button had been selected, as happens if Return is typed if
353            some widget that *doesn't* handle the Return key has the input
354            focus. */
355         dlg_set_activate(filter_entry, start_button);
356
357         /* Catch the "key_press_event" signal in the window, so that we can
358            catch the ESC key being pressed and act as if the "Cancel" button
359            had been selected. */
360         dlg_set_cancel(dlg, cancel_button);
361
362         /* Give the initial focus to the "Filter" entry box. */
363         gtk_widget_grab_focus(filter_entry);
364
365         gtk_widget_show_all(dlg);
366 }
367
368
369 void
370 register_tap_listener_gtkdhcpstat(void)
371 {
372         register_ethereal_tap("bootp,stat,", gtk_dhcpstat_init);
373 }
374
375 void
376 register_tap_menu_gtkdhcpstat(void)
377 {
378         register_tap_menu_item("Statistics/Watch protocol/BOOTP-DHCP...",
379             gtk_dhcpstat_cb, NULL, NULL, NULL);
380 }