Have "register_tap_menu_item()" put the item under Tools, not
[obnox/wireshark/wip.git] / gtk / http_stat.c
1 /* http_stat.c
2  * http_stat   2003 Jean-Michel FAYARD
3  *
4  * $Id: http_stat.c,v 1.3 2003/09/17 19:39:33 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 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <gtk/gtk.h>
30 #include "menu.h"
31 #include "epan/packet_info.h"
32 #include "simple_dialog.h"
33 #include "tap.h"
34 #include "../register.h"
35 #include "../globals.h"
36 #include "compat_macros.h"
37 #include "../packet-http.h"
38 #include <string.h>
39
40         
41 /* used to keep track of the statictics for an entire program interface */
42 typedef struct _http_stats_t {
43         char            *filter;
44         GtkWidget       *win;
45         GHashTable      *hash_responses;
46         GHashTable      *hash_requests;
47         guint32          packets;               /* number of http packets, including HTTP continuation */
48         GtkWidget       *packets_label;
49
50         GtkWidget       *request_box;           /* container for GET, ... */
51
52         GtkWidget       *informational_table;   /* Status code between  100 and 199 */
53         GtkWidget       *success_table;         /*                      200 and 299 */
54         GtkWidget       *redirection_table;     /*                      300 and 399 */
55         GtkWidget       *client_error_table;    /*                      400 and 499 */
56         GtkWidget       *server_errors_table;   /*                      500 and 599 */
57 } httpstat_t;
58
59 /* used to keep track of the stats for a specific response code
60  * for example it can be { 3, 404, "Not Found" ,...}
61  * which means we captured 3 reply http/1.1 404 Not Found */
62 typedef struct _http_response_code_t {
63         guint32          packets;               /* 3 */
64         guint            response_code;         /* 404 */
65         gchar           *name;                  /* Not Found */
66         GtkWidget       *widget;                /* Label where we display it */
67         GtkWidget       *table;                 /* Table in which we put it, e.g. client_error_box */
68         httpstat_t      *sp;
69 } http_response_code_t;
70
71 /* used to keep track of the stats for a specific request string */
72 typedef struct _http_request_methode_t {
73         gchar           *response;      /* eg. : GET */
74         guint32          packets;
75         GtkWidget       *widget;
76         httpstat_t      *sp;
77 } http_request_methode_t;
78
79 static GtkWidget *dlg=NULL, *dlg_box;
80 static GtkWidget *filter_box;
81 static GtkWidget *filter_label, *filter_entry;
82 static GtkWidget *start_button;
83
84 static const value_string vals_status_code[] = {
85         { 100, "Continue" },
86         { 101, "Switching Protocols" },
87         { 199, "Informational - Others" },
88
89         { 200, "OK"},
90         { 201, "Created"},
91         { 202, "Accepted"},
92         { 203, "Non-authoritative Information"},
93         { 204, "No Content"},
94         { 205, "Reset Content"},
95         { 206, "Partial Content"},
96         { 299, "Success - Others"},     /* used to keep track of others Success packets */
97
98         { 300, "Multiple Choices"},
99         { 301, "Moved Permanently"},
100         { 302, "Moved Temporarily"},
101         { 303, "See Other"},
102         { 304, "Not Modified"},
103         { 305, "Use Proxy"},
104         { 399, "Redirection - Others"},
105
106         { 400, "Bad Request"},
107         { 401, "Unauthorized"},
108         { 402, "Payment Required"},
109         { 403, "Forbidden"},
110         { 404, "Not Found"},
111         { 405, "Method Not Allowed"},
112         { 406, "Not Acceptable"},
113         { 407, "Proxy Authentication Required"},
114         { 408, "Request Time-out"},
115         { 409, "Conflict"},
116         { 410, "Gone"},
117         { 411, "Length Required"},
118         { 412, "Precondition Failed"},
119         { 413, "Request Entity Too Large"},
120         { 414, "Request-URI Too Large"},
121         { 415, "Unsupported Media Type"},
122         { 499, "Client Error - Others"},
123         
124         { 500, "Internal Server Error"},
125         { 501, "Not Implemented"},
126         { 502, "Bad Gateway"},
127         { 503, "Service Unavailable"},
128         { 504, "Gateway Time-out"},
129         { 505, "HTTP Version not supported"},
130         { 599, "Server Error - Others"},
131
132         { 0,    NULL}
133 } ;
134
135 /* insert some entries */
136 static void
137 http_init_hash( httpstat_t *sp)
138 {
139         int i;
140
141         sp->hash_responses = g_hash_table_new( g_int_hash, g_int_equal);                
142                         
143         for (i=0 ; vals_status_code[i].strptr ; i++ )
144         {
145                 gint *key = g_malloc (sizeof(gint));
146                 http_response_code_t *sc = g_malloc (sizeof(http_response_code_t));
147                 *key = vals_status_code[i].value;
148                 sc->packets=0;
149                 sc->response_code =  *key;
150                 sc->name=vals_status_code[i].strptr;
151                 sc->widget=NULL;
152                 sc->table=NULL;
153                 sc->sp = sp;
154                 g_hash_table_insert( sc->sp->hash_responses, key, sc);
155         }
156         sp->hash_requests = g_hash_table_new( g_str_hash, g_str_equal);
157 }
158 static void
159 http_draw_hash_requests( gchar *key _U_ , http_request_methode_t *data, gchar * string_buff)
160 {
161         if (data->packets==0)
162                 return;
163         sprintf(string_buff, "     %-11s : %3d packets", data->response, data->packets); 
164         if (data->widget==NULL){
165                 data->widget=gtk_label_new( string_buff );
166                 gtk_misc_set_alignment(GTK_MISC(data->widget), 0.0, 0.5);
167                 gtk_box_pack_start(GTK_BOX(data->sp->request_box), data->widget,FALSE,FALSE, 0);
168                 gtk_widget_show(data->widget);
169         } else {
170                 gtk_label_set( GTK_LABEL(data->widget), string_buff);
171         }
172 }
173                 
174
175 static void
176 http_draw_hash_responses( gint * key _U_ , http_response_code_t *data, gchar * string_buff)
177 {
178         if (data==NULL)
179                 g_warning("C'est quoi ce borderl key=%d\n", *key);
180         if (data->packets==0)
181                 return;
182         /*sprintf(string_buff, "%d packets %d:%s", data->packets, data->response_code, data->name); */
183         if (data->widget==NULL){        /* create an entry in the relevant box of the window */
184                 guint16 x;
185                 GtkWidget *tmp;
186                 guint i = data->response_code;
187
188                 if ( (i<100)||(i>=600) ) 
189                         return;
190                 if (i<200)
191                         data->table = data->sp->informational_table;
192                 else if (i<300)
193                         data->table = data->sp->success_table;
194                 else if (i<400)
195                         data->table = data->sp->redirection_table;
196                 else if (i<500)
197                         data->table = data->sp->client_error_table;
198                 else
199                         data->table = data->sp->server_errors_table;
200                 x=GTK_TABLE( data->table)->nrows;
201                 
202                 sprintf(string_buff, "HTTP %3d %s ", data->response_code, data->name ); 
203                 tmp = gtk_label_new( string_buff );
204                 
205                 gtk_table_attach_defaults( GTK_TABLE(data->table), tmp,  0,1, x, x+1);
206                 gtk_label_set_justify( GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
207                 gtk_widget_show( tmp );
208
209                 sprintf(string_buff, "%9d", data->packets);
210                 data->widget=gtk_label_new( string_buff);
211
212                 gtk_table_attach_defaults( GTK_TABLE(data->table), data->widget, 1, 2,x,x+1);
213                 gtk_label_set_justify( GTK_LABEL(data->widget), GTK_JUSTIFY_RIGHT);
214                 gtk_widget_show( data->widget );
215                 
216                 gtk_table_resize( GTK_TABLE(data->table), x+1, 4);
217                 
218         } else {
219                 /* Just update the label string */
220                 sprintf(string_buff, "%9d", data->packets );
221                 gtk_label_set( GTK_LABEL(data->widget), string_buff);
222         }
223 }
224                 
225
226                 
227 static void
228 http_free_hash( gpointer key, gpointer value, gpointer user_data _U_ )
229 {
230         g_free(key);
231         g_free(value);
232 }
233 static void
234 http_reset_hash_responses(gchar *key _U_ , http_response_code_t *data, gpointer ptr _U_ ) 
235 {       
236         data->packets = 0;
237 }
238 static void
239 http_reset_hash_requests(gchar *key _U_ , http_request_methode_t *data, gpointer ptr _U_ ) 
240 {       
241         data->packets = 0;
242 }
243
244 static void
245 httpstat_reset(void *psp  )
246 {
247         httpstat_t *sp=psp;
248         if (!sp) {
249                 g_hash_table_foreach( sp->hash_responses, (GHFunc)http_reset_hash_responses, NULL);
250                 g_hash_table_foreach( sp->hash_responses, (GHFunc)http_reset_hash_requests, NULL);
251         }
252 }
253
254 static int
255 httpstat_packet(void *psp , packet_info *pinfo _U_, epan_dissect_t *edt _U_, void *pri)
256 {
257         http_info_value_t *value=pri;
258         httpstat_t *sp=(httpstat_t *) psp;
259
260         /* total number of packets, including HTTP continuation packets */
261         sp->packets++;
262
263         /* We are only interested in reply packets with a status code */
264         /* Request or reply packets ? */
265         if (value->response_code!=0) {
266                 guint *key=g_malloc( sizeof(guint) );
267                 http_response_code_t *sc;
268                 
269                 *key=value->response_code;
270                 sc =  g_hash_table_lookup( 
271                                 sp->hash_responses, 
272                                 key);
273                 if (sc==NULL){
274                         /* non standard status code ; we classify it as others
275                          * in the relevant category (Informational,Success,Redirection,Client Error,Server Error)
276                          */
277                         int i = value->response_code;
278                         if ((i<100) || (i>=600)) {
279                                 return 0;
280                         }
281                         else if (i<200){
282                                 *key=199;       /* Hopefully, this status code will never be used */
283                         }
284                         else if (i<300){
285                                 *key=299;
286                         }
287                         else if (i<400){
288                                 *key=399;
289                         }
290                         else if (i<500){
291                                 *key=499;
292                         }
293                         else{
294                                 *key=599;
295                         }
296                         sc =  g_hash_table_lookup( 
297                                 sp->hash_responses, 
298                                 key);
299                         if (sc==NULL)
300                                 return 0;
301                 }
302                 sc->packets++;
303         } 
304         else if (value->request_method){
305                 http_request_methode_t *sc;
306
307                 sc =  g_hash_table_lookup( 
308                                 sp->hash_requests, 
309                                 value->request_method);
310                 if (sc==NULL){
311                         sc=g_malloc( sizeof(http_request_methode_t) );
312                         sc->response=g_strdup( value->request_method );
313                         sc->packets=1;
314                         sc->widget=NULL;
315                         sc->sp = sp;
316                         g_hash_table_insert( sp->hash_requests, sc->response, sc);
317                 } else {
318                         sc->packets++;
319                 }
320                 /* g_free( value->request_method ); */
321         } else {
322                 return 0;
323         }
324         return 1;
325 }
326
327
328 #define SUM_STR_MAX     1024
329 static void
330 httpstat_draw(void *psp  )
331 {
332         gchar   string_buff[SUM_STR_MAX];
333         httpstat_t *sp=psp;
334
335         sprintf( string_buff, "HTTP stats (%d packets)", sp->packets);
336         gtk_label_set( GTK_LABEL(sp->packets_label), string_buff);
337         g_hash_table_foreach( sp->hash_responses, (GHFunc)http_draw_hash_responses, string_buff);
338         g_hash_table_foreach( sp->hash_requests,  (GHFunc)http_draw_hash_requests, string_buff);
339 }
340
341
342 /* since the gtk2 implementation of tap is multithreaded we must protect
343  * remove_tap_listener() from modifying the list while draw_tap_listener()
344  * is running.  the other protected block is in main.c
345  *
346  * there should not be any other critical regions in gtk2
347  */
348 void protect_thread_critical_region(void);
349 void unprotect_thread_critical_region(void);
350 static void
351 win_destroy_cb(GtkWindow *win _U_, gpointer data)
352 {
353         httpstat_t *sp=(httpstat_t *)data;
354
355         protect_thread_critical_region();
356         remove_tap_listener(sp);
357         unprotect_thread_critical_region();
358
359         g_hash_table_foreach( sp->hash_responses, (GHFunc)http_free_hash, NULL);
360         g_hash_table_destroy( sp->hash_responses);
361         g_hash_table_foreach( sp->hash_requests, (GHFunc)http_free_hash, NULL);
362         g_hash_table_destroy( sp->hash_requests);
363         g_free(sp->filter);
364         g_free(sp);
365 }
366
367
368 /* When called, this function will create a new instance of gtk_httpstat.
369  */
370 static void
371 gtk_httpstat_init(char *optarg)
372 {
373         httpstat_t *sp;
374         char *filter=NULL;
375         char *title=NULL;
376         GString *error_string;
377         GtkWidget  *main_vb, *separator,
378                 *informational_fr, *success_fr, *redirection_fr, 
379                 *client_errors_fr, *server_errors_fr, *request_fr;
380         
381         if (!strncmp (optarg, "http,stat,", 10)){
382                 filter=optarg+10;
383         } else {
384                 filter=NULL;
385         }
386         
387         sp = g_malloc( sizeof(httpstat_t) );
388         if(filter){
389                 sp->filter=g_malloc(strlen(filter)+1);
390                 strcpy(sp->filter,filter);
391                 title=g_strdup_printf("HTTP Stats with filter: %s", filter);
392         } else {
393                 sp->filter=NULL;
394                 title=g_strdup("HTTP Stats");
395         }
396
397         /* top level window */
398         sp->win = gtk_window_new( GTK_WINDOW_TOPLEVEL);
399         gtk_window_set_title( GTK_WINDOW(sp->win), title );
400         g_free(title);
401         SIGNAL_CONNECT( sp->win, "destroy", win_destroy_cb, sp);
402
403         /* container for each group of status code */
404         main_vb = gtk_vbox_new(FALSE, 10);
405         gtk_container_border_width(GTK_CONTAINER(main_vb), 10);
406         gtk_container_add(GTK_CONTAINER(sp->win), main_vb);
407         gtk_widget_show(main_vb);
408         
409         /* number of packets */
410         sp->packets=0;
411         sp->packets_label = gtk_label_new("HTTP stats (0 HTTP packets)");
412         gtk_container_add( GTK_CONTAINER(main_vb), sp->packets_label);
413         gtk_widget_show(sp->packets_label);
414
415         /* Informational response frame */
416         informational_fr = gtk_frame_new("Informational  HTTP 1xx");
417         gtk_container_add(GTK_CONTAINER(main_vb), informational_fr);
418         gtk_widget_show(informational_fr);
419         
420         sp->informational_table = gtk_table_new( 0, 2, FALSE);
421         gtk_container_add(GTK_CONTAINER(informational_fr), sp->informational_table);
422         gtk_widget_show(sp->informational_table);
423
424
425         /* success response frame */
426         success_fr = gtk_frame_new      ("Success         HTTP 2xx");
427         gtk_container_add(GTK_CONTAINER(main_vb), success_fr);
428         gtk_widget_show(success_fr);
429         
430         sp->success_table = gtk_table_new( 0, 2, FALSE);
431         gtk_container_add(GTK_CONTAINER(success_fr), sp->success_table);
432         gtk_widget_show(sp->success_table);
433
434
435         /* redirection response frame */
436         redirection_fr = gtk_frame_new  ("Redirection     HTTP 3xx");
437         gtk_container_add(GTK_CONTAINER(main_vb), redirection_fr);
438         gtk_widget_show(redirection_fr);
439         
440         sp->redirection_table = gtk_table_new( 0, 2, FALSE);
441         gtk_container_add(GTK_CONTAINER(redirection_fr), sp->redirection_table);
442         gtk_widget_show(sp->redirection_table);
443
444
445         /* client_errors response frame */
446         client_errors_fr = gtk_frame_new("Client errors  HTTP 4xx");
447         gtk_container_add(GTK_CONTAINER(main_vb), client_errors_fr);
448         gtk_widget_show(client_errors_fr);
449         
450         sp->client_error_table = gtk_table_new( 0, 2, FALSE);
451         gtk_container_add(GTK_CONTAINER(client_errors_fr), sp->client_error_table);
452         gtk_widget_show(sp->client_error_table);
453
454
455         /* server_errors response frame */
456         server_errors_fr = gtk_frame_new("Server errors  HTTP 5xx");
457         gtk_container_add(GTK_CONTAINER(main_vb), server_errors_fr);
458         gtk_widget_show(server_errors_fr);
459         
460         sp->server_errors_table = gtk_table_new( 0, 2, FALSE);
461         gtk_container_add(GTK_CONTAINER(server_errors_fr), sp->server_errors_table);
462         gtk_widget_show(sp->server_errors_table);
463
464
465         separator = gtk_hseparator_new();
466         gtk_container_add(GTK_CONTAINER(main_vb), separator );
467
468         /* request response frame */
469         request_fr = gtk_frame_new("List of request methods");
470         gtk_container_add(GTK_CONTAINER(main_vb), request_fr);
471         gtk_container_border_width(GTK_CONTAINER(request_fr), 0);
472         gtk_widget_show(request_fr);
473         
474         sp->request_box = gtk_vbox_new(FALSE, 10);
475         gtk_container_add(GTK_CONTAINER(request_fr), sp->request_box);
476         gtk_widget_show(sp->request_box);
477
478         error_string = register_tap_listener( 
479                         "http",
480                         sp,
481                         filter,
482                         httpstat_reset,
483                         httpstat_packet,
484                         httpstat_draw);
485         if (error_string){
486                 /* error, we failed to attach to the tap. clean up */
487                 simple_dialog( ESD_TYPE_WARN, NULL, error_string->str );
488                 g_free(sp->filter);
489                 g_free(sp);
490                 g_string_free(error_string, TRUE);
491                 return ;
492         }
493         if (dlg)
494         {
495                 gtk_widget_destroy( dlg );
496         }
497
498         gtk_widget_show_all( sp->win );
499         http_init_hash(sp);
500         redissect_packets(&cfile);
501 }
502
503
504
505 static void
506 httpstat_start_button_clicked(GtkWidget *item _U_, gpointer data _U_)
507 {
508         char *filter;
509         char str[256];
510
511         filter=(char *)gtk_entry_get_text(GTK_ENTRY(filter_entry));
512         if(filter[0]==0){
513                 gtk_httpstat_init("http,stat,");
514         } else {
515                 sprintf(str, "http,stat,%s", filter);
516                 gtk_httpstat_init(str);
517         }
518 }
519
520 static void
521 dlg_destroy_cb(void)
522 {
523         dlg=NULL;
524 }
525
526 static void
527 gtk_httpstat_cb(GtkWidget *w _U_, gpointer d _U_)
528 {
529         /* if the window is already open, bring it to front */
530         if(dlg){
531                 gdk_window_raise(dlg->window);
532                 return;
533         }
534
535         dlg=gtk_window_new(GTK_WINDOW_TOPLEVEL);
536         gtk_window_set_title(GTK_WINDOW(dlg), "HTTP Statistics");
537         SIGNAL_CONNECT(dlg, "destroy", dlg_destroy_cb, NULL);
538         dlg_box=gtk_vbox_new(FALSE, 0);
539         gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
540         gtk_widget_show(dlg_box);
541
542
543         /* filter box */
544         filter_box=gtk_hbox_new(FALSE, 10);
545         /* Filter label */
546         gtk_container_set_border_width(GTK_CONTAINER(filter_box), 10);
547         filter_label=gtk_label_new("Filter:");
548         gtk_box_pack_start(GTK_BOX(filter_box), filter_label, FALSE, FALSE, 0);
549         gtk_widget_show(filter_label);
550
551         filter_entry=gtk_entry_new_with_max_length(250);
552         gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, FALSE, FALSE, 0);
553         gtk_widget_show(filter_entry);
554         
555         gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
556         gtk_widget_show(filter_box);
557
558
559         /* the start button */
560         start_button=gtk_button_new_with_label("Create Stat");
561         SIGNAL_CONNECT_OBJECT(start_button, "clicked",
562                               httpstat_start_button_clicked, NULL);
563         gtk_box_pack_start(GTK_BOX(dlg_box), start_button, TRUE, TRUE, 0);
564         gtk_widget_show(start_button);
565
566         gtk_widget_show_all(dlg);
567 }
568
569
570 void
571 register_tap_listener_gtkhttpstat(void)
572 {
573         register_ethereal_tap("http,stat,", gtk_httpstat_init);
574 }
575
576 void
577 register_tap_menu_gtkhttpstat(void)
578 {
579         register_tap_menu_item("Statistics/Watch protocol/HTTP", gtk_httpstat_cb);
580 }