Have tap listeners specify whether the "packet" routine requires
[obnox/wireshark/wip.git] / gtk / sip_stat.c
1 /* sip_stat.c
2  * sip_stat   2004 Martin Mathieson
3  *
4  * $Id$
5  * Copied from http_stat.c
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29 #include <string.h>
30
31 #include <gtk/gtk.h>
32
33 #include <epan/packet_info.h>
34 #include <epan/epan.h>
35 #include <epan/tap.h>
36 #include <epan/dissectors/packet-sip.h>
37
38 #include "../simple_dialog.h"
39 #include "../register.h"
40 #include "../globals.h"
41 #include "../stat_menu.h"
42
43 #include "gtk/gui_utils.h"
44 #include "gtk/dlg_utils.h"
45 #include "gtk/tap_dfilter_dlg.h"
46 #include "gtk/main.h"
47
48
49 #define SUM_STR_MAX     1024
50
51 /* Used to keep track of the statistics for an entire program interface */
52 typedef struct _sip_stats_t {
53     char        *filter;
54     GtkWidget   *win;
55     GHashTable  *hash_responses;
56     GHashTable  *hash_requests;
57     guint32         packets;        /* number of sip packets, including continuations */
58     guint32     resent_packets;
59         guint32         average_setup_time;
60         guint32         max_setup_time;
61         guint32         min_setup_time;
62         guint32         no_of_completed_calls;
63         guint64         total_setup_time;
64     GtkWidget   *packets_label;
65     GtkWidget   *resent_label;
66     GtkWidget   *average_setup_time_label;
67
68     GtkWidget   *request_box;           /* container for INVITE, ... */
69
70     GtkWidget   *informational_table;   /* Status code between 100 and 199 */
71     GtkWidget   *success_table;         /*   200 and 299 */
72     GtkWidget   *redirection_table;         /*   300 and 399 */
73     GtkWidget   *client_error_table;    /*   400 and 499 */
74     GtkWidget   *server_errors_table;   /*   500 and 599 */
75     GtkWidget   *global_failures_table; /*   600 and 699 */
76 } sipstat_t;
77
78 /* Used to keep track of the stats for a specific response code
79  * for example it can be { 3, 404, "Not Found" ,...}
80  * which means we captured 3 reply sip/1.1 404 Not Found */
81 typedef struct _sip_response_code_t {
82     guint32      packets;               /* 3 */
83     guint        response_code;         /* 404 */
84     const gchar *name;                  /* "Not Found" */
85     GtkWidget   *widget;                /* Label where we display it */
86     GtkWidget   *table;                 /* Table in which we put it,
87                                            e.g. client_error_table */
88     sipstat_t   *sp;                    /* Pointer back to main struct */
89 } sip_response_code_t;
90
91 /* Used to keep track of the stats for a specific request string */
92 typedef struct _sip_request_method_t {
93     gchar               *response;              /* eg. : INVITE */
94     guint32              packets;
95     GtkWidget   *widget;
96     sipstat_t   *sp;                /* Pointer back to main struct */
97 } sip_request_method_t;
98
99 /* TODO: extra codes to be added from SIP extensions? */
100 static const value_string vals_status_code[] = {
101     { 100, "Trying"},
102     { 180, "Ringing"},
103     { 181, "Call Is Being Forwarded"},
104     { 182, "Queued"},
105     { 183, "Session Progress"},
106     { 199, "Informational - Others" },
107
108     { 200, "OK"},
109     { 202, "Accepted"},
110     { 299, "Success - Others"}, /* used to keep track of other Success packets */
111
112     { 300, "Multiple Choices"},
113     { 301, "Moved Permanently"},
114     { 302, "Moved Temporarily"},
115     { 305, "Use Proxy"},
116     { 380, "Alternative Service"},
117     { 399, "Redirection - Others"},
118
119     { 400, "Bad Request"},
120     { 401, "Unauthorized"},
121     { 402, "Payment Required"},
122     { 403, "Forbidden"},
123     { 404, "Not Found"},
124     { 405, "Method Not Allowed"},
125     { 406, "Not Acceptable"},
126     { 407, "Proxy Authentication Required"},
127     { 408, "Request Timeout"},
128     { 410, "Gone"},
129     { 412, "Conditional Request Failed"},
130     { 413, "Request Entity Too Large"},
131     { 414, "Request-URI Too Long"},
132     { 415, "Unsupported Media Type"},
133     { 416, "Unsupported URI Scheme"},
134     { 420, "Bad Extension"},
135     { 421, "Extension Required"},
136     { 422, "Session Timer Too Small"},
137     { 423, "Interval Too Brief"},
138     { 429, "Provide Referrer Identity"},
139     { 480, "Temporarily Unavailable"},
140     { 481, "Call/Transaction Does Not Exist"},
141     { 482, "Loop Detected"},
142     { 483, "Too Many Hops"},
143     { 484, "Address Incomplete"},
144     { 485, "Ambiguous"},
145     { 486, "Busy Here"},
146     { 487, "Request Terminated"},
147     { 488, "Not Acceptable Here"},
148     { 489, "Bad Event"},
149     { 491, "Request Pending"},
150     { 493, "Undecipherable"},
151     { 494, "Security Agreement Required"},
152     { 499, "Client Error - Others"},
153
154     { 500, "Server Internal Error"},
155     { 501, "Not Implemented"},
156     { 502, "Bad Gateway"},
157     { 503, "Service Unavailable"},
158     { 504, "Server Time-out"},
159     { 505, "Version Not Supported"},
160     { 513, "Message Too Large"},
161     { 599, "Server Error - Others"},
162
163     { 600, "Busy Everywhere"},
164     { 603, "Decline"},
165     { 604, "Does Not Exist Anywhere"},
166     { 606, "Not Acceptable"},
167     { 699, "Global Failure - Others"},
168
169     { 0,        NULL}
170 };
171
172 void register_tap_listener_gtksipstat(void);
173
174
175 /* Create tables for responses and requests */
176 static void
177 sip_init_hash(sipstat_t *sp)
178 {
179     int i;
180
181     /* Create responses table */
182     sp->hash_responses = g_hash_table_new(g_int_hash, g_int_equal);
183
184     /* Add all response codes */
185     for (i=0 ; vals_status_code[i].strptr ; i++)
186     {
187         gint *key = g_malloc (sizeof(gint));
188         sip_response_code_t *sc = g_malloc (sizeof(sip_response_code_t));
189         *key = vals_status_code[i].value;
190         sc->packets=0;
191         sc->response_code =  *key;
192         sc->name=vals_status_code[i].strptr;
193         sc->widget=NULL;
194         sc->table=NULL;
195         sc->sp = sp;
196         g_hash_table_insert(sc->sp->hash_responses, key, sc);
197     }
198
199     /* Create empty requests table */
200     sp->hash_requests = g_hash_table_new(g_str_hash, g_str_equal);
201 }
202
203 /* Draw the entry for an individual request message */
204 static void
205 sip_draw_hash_requests(gchar *key _U_ , sip_request_method_t *data, gchar * unused _U_)
206 {
207     gchar string_buff[SUM_STR_MAX];
208
209     g_assert(data!=NULL);
210
211     if (data->packets==0)
212     {
213         return;
214     }
215
216     /* Build string showing method and count */
217     g_snprintf(string_buff, sizeof(string_buff),
218                "     %-11s : %3d packets", data->response, data->packets);
219     if (data->widget==NULL)
220     {
221         /* Create new label */
222         data->widget=gtk_label_new(string_buff);
223         gtk_misc_set_alignment(GTK_MISC(data->widget), 0.0f, 0.5f);
224         gtk_box_pack_start(GTK_BOX(data->sp->request_box), data->widget,FALSE,FALSE, 0);
225         gtk_widget_show(data->widget);
226     }
227     else
228     {
229         /* Update existing label */
230          gtk_label_set_text(GTK_LABEL(data->widget), string_buff);
231     }
232 }
233
234 /* Draw an individual response entry */
235 static void
236 sip_draw_hash_responses(gint * key _U_ , sip_response_code_t *data, gchar * unused _U_)
237 {
238     gchar string_buff[SUM_STR_MAX];
239
240     g_assert(data!=NULL);
241
242     if (data->packets==0)
243     {
244         return;
245     }
246
247     /* Create an entry in the relevant box of the window */
248     if (data->widget==NULL)
249     {
250         guint16 x;
251         GtkWidget *tmp;
252         guint i = data->response_code;
253
254         /* Out of valid range - ignore */
255         if ((i<100)||(i>=700))
256         {
257             return;
258         }
259
260         /* Find the table matching the code */
261         if (i<200)
262         {
263             data->table = data->sp->informational_table;
264         }
265         else if (i<300)
266         {
267             data->table = data->sp->success_table;
268         }
269         else if (i<400)
270         {
271             data->table = data->sp->redirection_table;
272         }
273         else if (i<500)
274         {
275             data->table = data->sp->client_error_table;
276         }
277         else if (i < 600)
278         {
279             data->table = data->sp->server_errors_table;
280         }
281         else
282         {
283             data->table = data->sp->global_failures_table;
284         }
285
286         /* Get number of rows in table */
287         x = GTK_TABLE(data->table)->nrows;
288
289         /* Create a new label with this response, e.g. "SIP 180 Ringing" */
290         g_snprintf(string_buff, sizeof(string_buff),
291                    "SIP %3d %s ", data->response_code, data->name);
292         tmp = gtk_label_new(string_buff);
293
294         /* Insert the label in the correct place in the table */
295         gtk_table_attach_defaults(GTK_TABLE(data->table), tmp,  0, 1, x, x+1);
296         gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
297         gtk_widget_show(tmp);
298
299         /* Show number of packets */
300         g_snprintf(string_buff, sizeof(string_buff), "%9d", data->packets);
301         data->widget=gtk_label_new(string_buff);
302
303         /* Show this widget in the right place */
304         gtk_table_attach_defaults(GTK_TABLE(data->table), data->widget, 1, 2,x,x+1);
305         gtk_label_set_justify(GTK_LABEL(data->widget), GTK_JUSTIFY_RIGHT);
306         gtk_widget_show(data->widget);
307
308         gtk_table_resize(GTK_TABLE(data->table), x+1, 4);
309
310     } else
311     {
312         /* Just update the existing label string */
313         g_snprintf(string_buff, sizeof(string_buff), "%9d", data->packets);
314          gtk_label_set_text(GTK_LABEL(data->widget), string_buff);
315     }
316 }
317
318
319
320 static void
321 sip_free_hash(gpointer key, gpointer value, gpointer user_data _U_)
322 {
323     g_free(key);
324     g_free(value);
325 }
326
327 static void
328 sip_reset_hash_responses(gchar *key _U_ , sip_response_code_t *data, gpointer ptr _U_)
329 {
330     data->packets = 0;
331 }
332
333 static void
334 sip_reset_hash_requests(gchar *key _U_ , sip_request_method_t *data, gpointer ptr _U_)
335 {
336     data->packets = 0;
337 }
338
339 static void
340 sipstat_reset(void *psp)
341 {
342     sipstat_t *sp = psp;
343     if (sp)
344     {
345         sp->packets = 0;
346         sp->resent_packets = 0;
347                 sp->average_setup_time = 0;
348                 sp->max_setup_time = 0;
349                 sp->max_setup_time = 0;
350                 sp->no_of_completed_calls = 0;
351                 sp->total_setup_time = 0;
352         g_hash_table_foreach(sp->hash_responses, (GHFunc)sip_reset_hash_responses, NULL);
353         g_hash_table_foreach(sp->hash_requests, (GHFunc)sip_reset_hash_requests, NULL);
354     }
355 }
356
357 /* Main entry point to SIP tap */
358 static int
359 sipstat_packet(void *psp, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *pri)
360 {
361     const sip_info_value_t *value=pri;
362     sipstat_t *sp = (sipstat_t *)psp;
363
364     /* Total number of packets, including continuation packets */
365     sp->packets++;
366
367     /* Update resent count if flag set */
368     if (value->resend)
369     {
370         sp->resent_packets++;
371     }
372
373         /* Calculate average setup time */
374         if (value->setup_time){
375                 sp->no_of_completed_calls++;
376                 /* Check if it's the first value */
377                 if ( sp->total_setup_time == 0 ){
378                         sp->average_setup_time = value->setup_time;
379                         sp->total_setup_time = value->setup_time;
380                         sp->max_setup_time = value->setup_time;
381                         sp->min_setup_time = value->setup_time;
382                 }else{
383                         sp->total_setup_time = sp->total_setup_time + value->setup_time;
384                         if (sp->max_setup_time < value->setup_time){
385                                 sp->max_setup_time = value->setup_time;
386                         }
387                         if (sp->min_setup_time > value->setup_time){
388                                 sp->min_setup_time = value->setup_time;
389                         }
390                         /* Calculate average */
391                         sp->average_setup_time = (guint32)(sp->total_setup_time / sp->no_of_completed_calls); 
392                 }
393         }
394
395     /* Looking at both requests and responses */
396     if (value->response_code != 0)
397     {
398         /* Responses */
399         guint *key = g_malloc(sizeof(guint));
400         sip_response_code_t *sc;
401
402         /* Look up response code in hash table */
403         *key = value->response_code;
404         sc = g_hash_table_lookup(sp->hash_responses, key);
405         if (sc==NULL)
406         {
407             /* Non-standard status code ; we classify it as others
408              * in the relevant category
409              * (Informational,Success,Redirection,Client Error,Server Error,Global Failure)
410              */
411             int i = value->response_code;
412             if ((i<100) || (i>=700))
413             {
414                 /* Forget about crazy values */
415                 return 0;
416             }
417             else if (i<200)
418             {
419                 *key=199;       /* Hopefully, this status code will never be used */
420             }
421             else if (i<300)
422             {
423                 *key=299;
424             }
425             else if (i<400)
426             {
427                 *key=399;
428             }
429             else if (i<500)
430             {
431                 *key=499;
432             }
433             else if (i<600)
434             {
435                 *key=599;
436             }
437             else
438             {
439                 *key = 699;
440             }
441
442             /* Now look up this fallback code to get its text description */
443             sc = g_hash_table_lookup(sp->hash_responses, key);
444             if (sc==NULL)
445             {
446                 return 0;
447             }
448         }
449         sc->packets++;
450     }
451     else if (value->request_method)
452     {
453         /* Requests */
454         sip_request_method_t *sc;
455
456         /* Look up the request method in the table */
457         sc = g_hash_table_lookup(sp->hash_requests, value->request_method);
458         if (sc == NULL)
459         {
460             /* First of this type. Create structure and initialise */
461             sc=g_malloc(sizeof(sip_request_method_t));
462             sc->response = g_strdup(value->request_method);
463             sc->packets = 1;
464             sc->widget = NULL;
465             sc->sp = sp;
466             /* Insert it into request table */
467             g_hash_table_insert(sp->hash_requests, sc->response, sc);
468         }
469         else
470         {
471             /* Already existed, just update count for that method */
472             sc->packets++;
473         }
474         /* g_free(value->request_method); */
475     }
476     else
477     {
478         /* No request method set. Just ignore */
479         return 0;
480     }
481
482     return 1;
483 }
484
485 /* Redraw the whole stats window */
486 static void
487 sipstat_draw(void *psp)
488 {
489     gchar      string_buff[SUM_STR_MAX];
490     sipstat_t *sp=psp;
491
492     /* Set summary label */
493     g_snprintf(string_buff, sizeof(string_buff),
494                 "SIP stats (%d packets)", sp->packets);
495      gtk_label_set_text(GTK_LABEL(sp->packets_label), string_buff);
496
497     /* Set resend count label */
498     g_snprintf(string_buff, sizeof(string_buff),
499                 "(%d resent packets)", sp->resent_packets);
500      gtk_label_set_text(GTK_LABEL(sp->resent_label), string_buff);
501
502     /* Draw responses and requests from their tables */
503     g_hash_table_foreach(sp->hash_responses, (GHFunc)sip_draw_hash_responses, NULL);
504     g_hash_table_foreach(sp->hash_requests,  (GHFunc)sip_draw_hash_requests, NULL);
505
506     /* Set resend count label */
507     g_snprintf(string_buff, sizeof(string_buff),
508                 "Average setup time %d ms\n Min %d ms\n Max %d ms", sp->average_setup_time, sp->min_setup_time, sp->max_setup_time);
509      gtk_label_set_text(GTK_LABEL(sp->average_setup_time_label), string_buff);
510
511         gtk_widget_show_all(sp->win);
512 }
513
514
515 /* since the gtk2 implementation of tap is multithreaded we must protect
516  * remove_tap_listener() from modifying the list while draw_tap_listener()
517  * is running.  the other protected block is in main.c
518  *
519  * there should not be any other critical regions in gtk2
520  */
521 /* When window is destroyed, clean up */
522 static void
523 win_destroy_cb(GtkWindow *win _U_, gpointer data)
524 {
525     sipstat_t *sp=(sipstat_t *)data;
526
527     protect_thread_critical_region();
528     remove_tap_listener(sp);
529     unprotect_thread_critical_region();
530
531     g_hash_table_foreach(sp->hash_responses, (GHFunc)sip_free_hash, NULL);
532     g_hash_table_destroy(sp->hash_responses);
533     g_hash_table_foreach(sp->hash_requests, (GHFunc)sip_free_hash, NULL);
534     g_hash_table_destroy(sp->hash_requests);
535     g_free(sp->filter);
536     g_free(sp);
537 }
538
539
540 /* Create a new instance of gtk_sipstat. */
541 static void
542 gtk_sipstat_init(const char *optarg, void *userdata _U_)
543 {
544     sipstat_t *sp;
545     const char *filter = NULL;
546     GString     *error_string;
547     char *title = NULL;
548     GtkWidget  *main_vb, *separator,
549                *informational_fr, *success_fr, *redirection_fr,
550                *client_errors_fr, *server_errors_fr, *global_failures_fr,
551                *request_fr;
552     GtkWidget   *bt_close;
553     GtkWidget   *bbox;
554
555
556     if (strncmp (optarg, "sip,stat,", 9) == 0)
557     {
558         /* Skip those characters from filter to display */
559         filter=optarg + 9;
560     }
561     else
562     {
563         /* No filter */
564         filter = NULL;
565     }
566
567     /* Create sip stats window structure */
568     sp = g_malloc(sizeof(sipstat_t));
569     sp->win = window_new(GTK_WINDOW_TOPLEVEL, "sip-stat");
570
571     /* Set title to include any filter given */
572     if (filter)
573     {
574         sp->filter = g_strdup(filter);
575         title = g_strdup_printf("SIP statistics with filter: %s", filter);
576     }
577     else
578     {
579         sp->filter = NULL;
580         title = g_strdup("SIP statistics");
581     }
582
583     gtk_window_set_title(GTK_WINDOW(sp->win), title);
584     g_free(title);
585
586
587     /* Create container for all widgets */
588     main_vb = gtk_vbox_new(FALSE, 12);
589     gtk_container_set_border_width(GTK_CONTAINER(main_vb), 12);
590     gtk_container_add(GTK_CONTAINER(sp->win), main_vb);
591
592     /* Initialise & show number of packets */
593     sp->packets = 0;
594     sp->packets_label = gtk_label_new("SIP stats (0 SIP packets)");
595     gtk_container_add(GTK_CONTAINER(main_vb), sp->packets_label);
596
597     sp->resent_packets = 0;
598     sp->resent_label = gtk_label_new("(0 resent packets)");
599     gtk_container_add(GTK_CONTAINER(main_vb), sp->resent_label);
600     gtk_widget_show(sp->resent_label);
601
602
603     /* Informational response frame */
604     informational_fr = gtk_frame_new("Informational  SIP 1xx");
605     gtk_container_add(GTK_CONTAINER(main_vb), informational_fr);
606
607     /* Information table (within that frame) */
608     sp->informational_table = gtk_table_new(0, 2, FALSE);
609     gtk_container_add(GTK_CONTAINER(informational_fr), sp->informational_table);
610
611     /* Success table and frame */
612     success_fr = gtk_frame_new  ("Success         SIP 2xx");
613     gtk_container_add(GTK_CONTAINER(main_vb), success_fr);
614
615     sp->success_table = gtk_table_new(0, 2, FALSE);
616     gtk_container_add(GTK_CONTAINER(success_fr), sp->success_table);
617
618     /* Redirection table and frame */
619     redirection_fr = gtk_frame_new      ("Redirection     SIP 3xx");
620     gtk_container_add(GTK_CONTAINER(main_vb), redirection_fr);
621
622     sp->redirection_table = gtk_table_new(0, 2, FALSE);
623     gtk_container_add(GTK_CONTAINER(redirection_fr), sp->redirection_table);
624
625     /* Client Errors table and frame */
626     client_errors_fr = gtk_frame_new("Client errors  SIP 4xx");
627     gtk_container_add(GTK_CONTAINER(main_vb), client_errors_fr);
628
629     sp->client_error_table = gtk_table_new(0, 2, FALSE);
630     gtk_container_add(GTK_CONTAINER(client_errors_fr), sp->client_error_table);
631
632     /* Server Errors table and frame */
633     server_errors_fr = gtk_frame_new("Server errors  SIP 5xx");
634     gtk_container_add(GTK_CONTAINER(main_vb), server_errors_fr);
635
636     sp->server_errors_table = gtk_table_new(0, 2, FALSE);
637     gtk_container_add(GTK_CONTAINER(server_errors_fr), sp->server_errors_table);
638
639     /* Global Failures table and frame */
640     global_failures_fr = gtk_frame_new("Global failures  SIP 6xx");
641     gtk_container_add(GTK_CONTAINER(main_vb), global_failures_fr);
642
643     sp->global_failures_table = gtk_table_new(0, 2, FALSE);
644     gtk_container_add(GTK_CONTAINER(global_failures_fr), sp->global_failures_table);
645
646
647     /* Separator between requests and responses */
648     separator = gtk_hseparator_new();
649     gtk_container_add(GTK_CONTAINER(main_vb), separator);
650
651     /* Request table and frame */
652     request_fr = gtk_frame_new("List of request methods");
653     gtk_container_add(GTK_CONTAINER(main_vb), request_fr);
654     gtk_container_set_border_width(GTK_CONTAINER(request_fr), 0);
655
656     sp->request_box = gtk_vbox_new(FALSE, 10);
657     gtk_container_add(GTK_CONTAINER(request_fr), sp->request_box);
658
659     sp->average_setup_time = 0;
660         sp->max_setup_time =0;
661         sp->min_setup_time =0;
662     sp->average_setup_time_label = gtk_label_new("(Not calculated)");
663     gtk_container_add(GTK_CONTAINER(main_vb), sp->average_setup_time_label);
664     gtk_widget_show(sp->average_setup_time_label);
665
666
667     /* Register this tap listener now */
668     error_string = register_tap_listener("sip",
669                                          sp,
670                                          filter,
671                                          0,
672                                          sipstat_reset,
673                                          sipstat_packet,
674                                          sipstat_draw);
675     if (error_string)
676     {
677         /* Error.  We failed to attach to the tap. Clean up */
678         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
679         g_free(sp->filter);
680         g_free(sp);
681         g_string_free(error_string, TRUE);
682         return;
683     }
684
685         /* Button row. */
686     bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
687     gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
688
689     bt_close = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
690     window_set_cancel_button(sp->win, bt_close, window_cancel_button_cb);
691
692     g_signal_connect(sp->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
693     g_signal_connect(sp->win, "destroy", G_CALLBACK(win_destroy_cb), sp);
694
695     /* Display up-to-date contents */
696     gtk_widget_show_all(sp->win);
697     window_present(sp->win);
698
699     sip_init_hash(sp);
700     cf_retap_packets(&cfile);
701     gdk_window_raise(sp->win->window);
702 }
703
704 static tap_dfilter_dlg sip_stat_dlg = {
705         "SIP Packet Counter",
706         "sip,stat",
707         gtk_sipstat_init,
708         -1
709 };
710
711 /* Register this tap listener and add menu item. */
712 void
713 register_tap_listener_gtksipstat(void)
714 {
715     register_dfilter_stat(&sip_stat_dlg, "SIP", REGISTER_STAT_GROUP_TELEPHONY);
716 }