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