8083ac1b4c299c393aa05b0256a6f3b8ab549406
[obnox/wireshark/wip.git] / gtk / wsp_stat.c
1 /* wsp_stat.c
2  * wsp_stat   2003 Jean-Michel FAYARD
3  *
4  * $Id: wsp_stat.c,v 1.6 2003/12/13 17:10:21 ulfl 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 "epan/packet_info.h"
32 #include "epan/epan.h"
33 #include "simple_dialog.h"
34 #include "dlg_utils.h"
35 #include "tap.h"
36 #include "../register.h"
37 #include "../globals.h"
38 #include "compat_macros.h"
39 #include "../packet-wsp.h"
40 #include <string.h>
41
42 /* used to keep track of the stats for a specific PDU type*/
43 typedef struct _wsp_pdu_t {
44         GtkLabel        *widget;
45         guint32          packets;
46 } wsp_pdu_t;
47
48 /* used to keep track of the statictics for an entire program interface */
49 typedef struct _wsp_stats_t {
50         char            *filter;
51         wsp_pdu_t       *pdu_stats;
52         guint32          num_pdus;
53         GtkWidget       *win;
54         GHashTable      *hash;
55         GtkWidget       *table_pdu_types;       
56         GtkWidget       *table_status_code;
57         guint            index; /* Number of status code to display */
58 } wspstat_t;
59 /* used to keep track of a single type of status code */
60 typedef struct _wsp_status_code_t {
61         gchar           *name;
62         guint32          packets;
63         GtkWidget       *widget;/* label in which we print the number of packets */
64         wspstat_t       *sp;    /* entire program interface */
65 } wsp_status_code_t;
66
67 static GtkWidget *dlg=NULL;
68 static GtkWidget *filter_entry;
69
70 static void
71 wsp_free_hash( gpointer key, gpointer value, gpointer user_data _U_ )
72 {
73         g_free(key);
74         g_free(value);
75 }
76 static void
77 wsp_reset_hash(gchar *key _U_ , wsp_status_code_t *data, gpointer ptr _U_ ) 
78 {       
79         data->packets = 0;
80 }
81
82 /* Update the entry corresponding to the number of packets of a special status code
83  * or create it if it don't exist.
84  */
85 static void
86 wsp_draw_statuscode(gchar *key _U_, wsp_status_code_t *data, gchar * string_buff )
87 {
88         if ((data==NULL) || (data->packets==0))
89                 return;
90         if (data->widget==NULL){        /* create an entry in the table */
91                 GtkWidget       *tmp;
92                 int x = 2*((data->sp->index) % 2);
93                 int y = (data->sp->index) /2;
94
95
96                 /* Maybe we should display the hexadecimal value ? */
97                 /* sprintf(string_buff, "%s  (0X%x)", data->name, *key); */
98                 tmp = gtk_label_new( data->name  /* string_buff */ );
99                 gtk_table_attach_defaults(GTK_TABLE(data->sp->table_status_code), tmp, x, x+1, y, y+1);
100                 gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
101                 gtk_widget_show(tmp);
102
103                 sprintf( string_buff, "%9d", data->packets );
104                 data->widget = gtk_label_new( string_buff );
105                 gtk_table_attach_defaults(GTK_TABLE(data->sp->table_status_code), data->widget, x+1, x+2, y, y+1);
106                 gtk_label_set_justify(GTK_LABEL(data->widget), GTK_JUSTIFY_LEFT);
107                 gtk_widget_show( data->widget );
108
109                 data->sp->index++;
110         } else {
111                 /* Just update the label string */
112                 sprintf( string_buff, "%9d", data->packets );
113                 gtk_label_set( GTK_LABEL(data->widget), string_buff);
114         }
115 }
116 static void
117 wspstat_reset(void *psp)
118 {
119         wspstat_t *sp=psp;
120         guint32 i;
121
122         for(i=1;i<=sp->num_pdus;i++)
123         {
124                 sp->pdu_stats[i].packets=0;
125         }
126         g_hash_table_foreach( sp->hash, (GHFunc)wsp_reset_hash, NULL);  
127 }
128 static gint 
129 pdut2index(gint pdut)
130 {
131         if (pdut<=0x09)         return pdut;
132         if (pdut>=0x40){
133                 if (pdut <= 0x44){
134                         return pdut-54;
135                 } else if (pdut==0x60||pdut==0x61){
136                         return pdut-81;
137                 }
138         }
139         return 0;
140 }
141 static gint
142 index2pdut(gint pdut)
143 {
144         if (pdut<=0x09)
145                 return pdut;
146         if (pdut<=14)
147                 return pdut+54;
148         if (pdut<=16)
149                 return pdut+81;
150         return 0;
151 }
152
153 static int
154 wspstat_packet(void *psp, packet_info *pinfo _U_, epan_dissect_t *edt _U_, void *pri)
155 {
156         wspstat_t *sp=psp;
157         wsp_info_value_t *value=pri;
158         gint index = pdut2index(value->pdut);
159         int retour=0;
160
161         if (value->status_code != 0) {
162                 gint *key=g_malloc( sizeof(gint) );
163                 wsp_status_code_t *sc;
164                 *key=value->status_code ;
165                 sc = g_hash_table_lookup( 
166                                 sp->hash, 
167                                 key);
168                 if (!sc) {
169                         g_warning("%s:%d What's Wrong, doc ?\n", __FILE__, __LINE__);
170                         sc = g_malloc( sizeof(wsp_status_code_t) );
171                         sc -> packets = 1;
172                         sc -> name = NULL;
173                         sc -> widget=NULL;
174                         sc -> sp = sp;
175                         g_hash_table_insert(
176                                 sp->hash,
177                                 key,
178                                 sc);
179                 } else {
180                         sc->packets++;
181                 }
182                 retour=1;
183         }
184
185                 
186
187         if (index!=0) {
188                 sp->pdu_stats[ index ].packets++;
189                 retour = 1;
190         }
191         return retour;
192
193 }
194
195
196
197 static void
198 wspstat_draw(void *psp)
199 {
200         wspstat_t *sp=psp;
201         guint32 i;
202         char str[256];
203         guint index;
204
205         for(i=1;i<=sp->num_pdus ; i++)
206         {
207                 sprintf(str, "%9d",  sp->pdu_stats[i ].packets);
208                 gtk_label_set( GTK_LABEL(sp->pdu_stats[i].widget), str);
209         }
210
211         index=sp->index;
212         g_hash_table_foreach( sp->hash, (GHFunc) wsp_draw_statuscode, str );
213         if (index != sp->index){
214                 /* We have inserted a new entry corresponding to a status code ,
215                  * let's resize the table */
216                 gtk_table_resize ( GTK_TABLE(sp->table_status_code), sp->index  % 2 , 4);
217         }
218         
219 }
220
221
222
223 /* since the gtk2 implementation of tap is multithreaded we must protect
224  * remove_tap_listener() from modifying the list while draw_tap_listener()
225  * is running.  the other protected block is in main.c
226  *
227  * there should not be any other critical regions in gtk2
228  */
229 void protect_thread_critical_region(void);
230 void unprotect_thread_critical_region(void);
231 static void
232 win_destroy_cb(GtkWindow *win _U_, gpointer data)
233 {
234         wspstat_t *sp=(wspstat_t *)data;
235
236         protect_thread_critical_region();
237         remove_tap_listener(sp);
238         unprotect_thread_critical_region();
239
240         g_free(sp->pdu_stats);
241         g_free(sp->filter);
242         g_hash_table_foreach( sp->hash, (GHFunc)wsp_free_hash, NULL);
243         g_hash_table_destroy( sp->hash);        
244         g_free(sp);
245 }
246
247 static void
248 add_table_entry(wspstat_t *sp, char *str, int x, int y, int index)
249 {
250         GtkWidget *tmp;
251
252         tmp=gtk_label_new( str );
253         gtk_table_attach_defaults(GTK_TABLE(sp->table_pdu_types), tmp, x, x+1, y, y+1);
254         gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
255         gtk_widget_show(tmp);
256         if (index != 0) {
257                 sp->pdu_stats [index] .widget = GTK_LABEL( tmp ) ;
258         }
259 }
260
261
262 static void
263 wsp_init_table(wspstat_t *sp)
264 {
265         int pos=0;
266         guint32 i;
267         /* gchar        buffer[51];     */
268         
269         add_table_entry( sp, "PDU Type               "  , 0, pos, 0);
270         add_table_entry( sp, "packets  "        , 1, pos, 0);
271         add_table_entry( sp, "PDU Type               "  , 2, pos, 0);
272         add_table_entry( sp, "packets  "        , 3, pos, 0);
273         pos++;
274         for (i=1 ; i <= sp->num_pdus ; i++ )
275         {
276                 int x = 0;
277                 if (i> (sp->num_pdus+1) /2 ){
278                         x=2;
279                 }
280                 /* Maybe we should display the hexadecimal value ? */
281                 /* snprintf(buffer, 50, "%s  (0X%x)", match_strval( index2pdut( i ), vals_pdu_type), index2pdut(i) );*/
282                 add_table_entry( sp, 
283                                 match_strval(index2pdut(i), vals_pdu_type), /* or buffer, */
284                                 x,
285                                 pos,
286                                 0
287                                 );
288                 add_table_entry( sp, "0", x+1, pos
289                                 , i /* keep a pointer to this widget to update it in _draw() */
290                                 );      
291                 pos++;
292                 if (i== (sp->num_pdus+1) /2) {
293                         pos=1;
294                 }
295         }
296 }
297
298 /* When called, this function will create a new instance of gtk2-wspstat.
299  */
300 static void
301 gtk_wspstat_init(char *optarg)
302 {
303         wspstat_t *sp;
304         char            *filter=NULL;
305         char            *title=NULL;
306         GString         *error_string;
307         GtkWidget       *main_vb, *pdutypes_fr, *statuscode_fr ;
308         guint32          i;
309         wsp_status_code_t *sc;
310         
311         
312         if (!strncmp (optarg, "wsp,stat,", 9)){
313                 filter=optarg+9;
314         } else {
315                 filter=NULL;
316         }
317         
318         sp = g_malloc( sizeof(wspstat_t) );
319         sp->hash = g_hash_table_new( g_int_hash, g_int_equal);
320         for (i=0 ; vals_status[i].strptr ; i++ )
321         {
322                 gint *key;
323                 sc=g_malloc( sizeof(wsp_status_code_t) );
324                 key=g_malloc( sizeof(gint) );
325                 sc->name=vals_status[i].strptr;
326                 sc->packets=0;
327                 sc->widget=NULL;
328                 sc->sp = sp;
329                 *key=vals_status[i].value;
330                 g_hash_table_insert(
331                                 sp->hash,
332                                 key,
333                                 sc);
334         }
335         sp->num_pdus = 16;
336         sp->pdu_stats=g_malloc( (sp->num_pdus+1) * sizeof( wsp_pdu_t) );
337         if(filter){
338                 sp->filter=g_malloc(strlen(filter)+1);
339                 strcpy(sp->filter,filter);
340                 title=g_strdup_printf("Ethereal: WSP statistics with filter: %s", filter);
341         } else {
342                 sp->filter=NULL;
343                 title=g_strdup("Ethereal: WSP statistics");
344         }
345         for (i=0;i<=sp->num_pdus; i++)
346         {
347                 sp->pdu_stats[i].packets=0;
348         }
349
350         sp->win = gtk_window_new( GTK_WINDOW_TOPLEVEL);
351         gtk_window_set_title( GTK_WINDOW(sp->win), title );
352         g_free(title);
353         SIGNAL_CONNECT( sp->win, "destroy", win_destroy_cb, sp);
354
355
356         /* container for the two frames */
357         main_vb = gtk_vbox_new(FALSE, 10);
358         gtk_container_border_width(GTK_CONTAINER(main_vb), 10);
359         gtk_container_add(GTK_CONTAINER(sp->win), main_vb);
360         gtk_widget_show(main_vb);
361
362         /* PDU Types frame */
363         pdutypes_fr = gtk_frame_new("Summary of PDU Types (wsp.pdu_type)");
364         gtk_container_add(GTK_CONTAINER(main_vb), pdutypes_fr);
365         gtk_widget_show(pdutypes_fr);
366         
367         sp->table_pdu_types = gtk_table_new( (sp->num_pdus+1) / 2 + 1, 4, FALSE);
368         gtk_container_add( GTK_CONTAINER( pdutypes_fr), sp->table_pdu_types);
369         gtk_container_set_border_width( GTK_CONTAINER(sp->table_pdu_types) , 10);
370
371         wsp_init_table(sp);
372         gtk_widget_show( sp->table_pdu_types );
373
374         /* Status Codes frame */
375         statuscode_fr = gtk_frame_new("Summary of Status Code (wsp.reply.status)");
376         gtk_container_add(GTK_CONTAINER(main_vb), statuscode_fr);
377         gtk_widget_show(statuscode_fr);
378         
379         sp->table_status_code = gtk_table_new( 0, 4, FALSE);
380         gtk_container_add( GTK_CONTAINER( statuscode_fr), sp->table_status_code);
381         gtk_container_set_border_width( GTK_CONTAINER(sp->table_status_code) , 10);
382         sp->index = 0;          /* No answers to display yet */
383
384
385         error_string = register_tap_listener( 
386                         "wsp",
387                         sp,
388                         filter,
389                         wspstat_reset,
390                         wspstat_packet,
391                         wspstat_draw);
392         if (error_string){
393                 /* error, we failed to attach to the tap. clean up */
394                 simple_dialog( ESD_TYPE_WARN, NULL, error_string->str );
395                 g_free(sp->pdu_stats);
396                 g_free(sp->filter);
397                 g_free(sp);
398                 g_string_free(error_string, TRUE);
399                 return ;
400         }
401         gtk_widget_show_all( sp->win );
402         redissect_packets(&cfile);
403 }
404
405
406
407 static void
408 wspstat_start_button_clicked(GtkWidget *item _U_, gpointer data _U_)
409 {
410         GString *str;
411         char *filter;
412
413         str = g_string_new("wsp,stat,");
414         filter=(char *)gtk_entry_get_text(GTK_ENTRY(filter_entry));
415         str = g_string_append(str, filter);
416         gtk_wspstat_init(str->str);
417         g_string_free(str, TRUE);
418 }
419
420 static void
421 dlg_destroy_cb(void)
422 {
423         dlg=NULL;
424 }
425
426 static void
427 dlg_cancel_cb(GtkWidget *cancel_bt _U_, gpointer parent_w)
428 {
429         gtk_widget_destroy(GTK_WIDGET(parent_w));
430 }
431
432 static void
433 gtk_wspstat_cb(GtkWidget *w _U_, gpointer d _U_)
434 {
435         GtkWidget *dlg_box;
436         GtkWidget *filter_box, *filter_label;
437         GtkWidget *bbox, *start_button, *cancel_button;
438
439         /* if the window is already open, bring it to front */
440         if(dlg){
441                 gdk_window_raise(dlg->window);
442                 return;
443         }
444
445         dlg=dlg_window_new("Ethereal: Compute WSP statistics");
446         SIGNAL_CONNECT(dlg, "destroy", dlg_destroy_cb, NULL);
447
448         dlg_box=gtk_vbox_new(FALSE, 10);
449         gtk_container_border_width(GTK_CONTAINER(dlg_box), 10);
450         gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
451         gtk_widget_show(dlg_box);
452
453         /* Filter box */
454         filter_box=gtk_hbox_new(FALSE, 3);
455
456         /* Filter label */
457         filter_label=gtk_label_new("Filter:");
458         gtk_box_pack_start(GTK_BOX(filter_box), filter_label, FALSE, FALSE, 0);
459         gtk_widget_show(filter_label);
460
461         /* Filter entry */
462         filter_entry=gtk_entry_new();
463         gtk_widget_set_usize(filter_entry, 300, -2);
464         gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, TRUE, TRUE, 0);
465         gtk_widget_show(filter_entry);
466         
467         gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
468         gtk_widget_show(filter_box);
469
470         /* button box */
471         bbox = gtk_hbutton_box_new();
472         gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_DEFAULT_STYLE);
473         gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
474         gtk_box_pack_start(GTK_BOX(dlg_box), bbox, FALSE, FALSE, 0);
475         gtk_widget_show(bbox);
476
477         /* the start button */
478         start_button=gtk_button_new_with_label("Create Stat");
479         SIGNAL_CONNECT_OBJECT(start_button, "clicked",
480                               wspstat_start_button_clicked, NULL);
481         gtk_box_pack_start(GTK_BOX(bbox), start_button, TRUE, TRUE, 0);
482         GTK_WIDGET_SET_FLAGS(start_button, GTK_CAN_DEFAULT);
483         gtk_widget_grab_default(start_button);
484         gtk_widget_show(start_button);
485
486 #if GTK_MAJOR_VERSION < 2
487         cancel_button=gtk_button_new_with_label("Cancel");
488 #else
489         cancel_button=gtk_button_new_from_stock(GTK_STOCK_CANCEL);
490 #endif
491         SIGNAL_CONNECT(cancel_button, "clicked", dlg_cancel_cb, dlg);
492         GTK_WIDGET_SET_FLAGS(cancel_button, GTK_CAN_DEFAULT);
493         gtk_box_pack_start(GTK_BOX(bbox), cancel_button, TRUE, TRUE, 0);
494         gtk_widget_show(cancel_button);
495
496         /* Catch the "activate" signal on the filter text entry, so that
497            if the user types Return there, we act as if the "Create Stat"
498            button had been selected, as happens if Return is typed if
499            some widget that *doesn't* handle the Return key has the input
500            focus. */
501         dlg_set_activate(filter_entry, start_button);
502
503         /* Catch the "key_press_event" signal in the window, so that we can
504            catch the ESC key being pressed and act as if the "Cancel" button
505            had been selected. */
506         dlg_set_cancel(dlg, cancel_button);
507
508         /* Give the initial focus to the "Filter" entry box. */
509         gtk_widget_grab_focus(filter_entry);
510
511         gtk_widget_show_all(dlg);
512 }
513
514
515 void
516 register_tap_listener_gtkwspstat(void)
517 {
518         register_ethereal_tap("wsp,stat,", gtk_wspstat_init);
519 }
520
521 void
522 register_tap_menu_gtkwspstat(void)
523 {
524         register_tap_menu_item("Statistics/Watch protocol/WAP-WSP...",
525             gtk_wspstat_cb, NULL, NULL);
526 }