Assorted GUI cleanups.
[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.6 2003/09/26 02:09:44 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 "epan/packet_info.h"
31 #include "epan/epan.h"
32 #include "menu.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-http.h"
40 #include <string.h>
41
42         
43 /* used to keep track of the statictics for an entire program interface */
44 typedef struct _http_stats_t {
45         char            *filter;
46         GtkWidget       *win;
47         GHashTable      *hash_responses;
48         GHashTable      *hash_requests;
49         guint32          packets;               /* number of http packets, including HTTP continuation */
50         GtkWidget       *packets_label;
51
52         GtkWidget       *request_box;           /* container for GET, ... */
53
54         GtkWidget       *informational_table;   /* Status code between  100 and 199 */
55         GtkWidget       *success_table;         /*                      200 and 299 */
56         GtkWidget       *redirection_table;     /*                      300 and 399 */
57         GtkWidget       *client_error_table;    /*                      400 and 499 */
58         GtkWidget       *server_errors_table;   /*                      500 and 599 */
59 } httpstat_t;
60
61 /* used to keep track of the stats for a specific response code
62  * for example it can be { 3, 404, "Not Found" ,...}
63  * which means we captured 3 reply http/1.1 404 Not Found */
64 typedef struct _http_response_code_t {
65         guint32          packets;               /* 3 */
66         guint            response_code;         /* 404 */
67         gchar           *name;                  /* Not Found */
68         GtkWidget       *widget;                /* Label where we display it */
69         GtkWidget       *table;                 /* Table in which we put it, e.g. client_error_box */
70         httpstat_t      *sp;
71 } http_response_code_t;
72
73 /* used to keep track of the stats for a specific request string */
74 typedef struct _http_request_methode_t {
75         gchar           *response;      /* eg. : GET */
76         guint32          packets;
77         GtkWidget       *widget;
78         httpstat_t      *sp;
79 } http_request_methode_t;
80
81 static GtkWidget *dlg=NULL;
82 static GtkWidget *filter_entry;
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 statistics with filter: %s", filter);
392         } else {
393                 sp->filter=NULL;
394                 title=g_strdup("HTTP statistics");
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         GString *str;
509         char *filter;
510
511         str = g_string_new("http,stat,");
512         filter=(char *)gtk_entry_get_text(GTK_ENTRY(filter_entry));
513         str = g_string_append(str, filter);
514         gtk_httpstat_init(str->str);
515         g_string_free(str, TRUE);
516 }
517
518 static void
519 dlg_destroy_cb(void)
520 {
521         dlg=NULL;
522 }
523
524 static void
525 dlg_cancel_cb(GtkWidget *cancel_bt _U_, gpointer parent_w)
526 {
527         gtk_widget_destroy(GTK_WIDGET(parent_w));
528 }
529
530 static void
531 gtk_httpstat_cb(GtkWidget *w _U_, gpointer d _U_)
532 {
533         GtkWidget *dlg_box;
534         GtkWidget *filter_box, *filter_label;
535         GtkWidget *bbox, *start_button, *cancel_button;
536
537         /* if the window is already open, bring it to front */
538         if(dlg){
539                 gdk_window_raise(dlg->window);
540                 return;
541         }
542
543         dlg=dlg_window_new("Ethereal: Compute HTTP statistics");
544         SIGNAL_CONNECT(dlg, "destroy", dlg_destroy_cb, NULL);
545
546         dlg_box=gtk_vbox_new(FALSE, 10);
547         gtk_container_border_width(GTK_CONTAINER(dlg_box), 10);
548         gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
549         gtk_widget_show(dlg_box);
550
551         /* Filter box */
552         filter_box=gtk_hbox_new(FALSE, 3);
553
554         /* Filter label */
555         filter_label=gtk_label_new("Filter:");
556         gtk_box_pack_start(GTK_BOX(filter_box), filter_label, FALSE, FALSE, 0);
557         gtk_widget_show(filter_label);
558
559         /* Filter entry */
560         filter_entry=gtk_entry_new();
561         gtk_widget_set_usize(filter_entry, 300, -2);
562         gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, TRUE, TRUE, 0);
563         gtk_widget_show(filter_entry);
564         
565         gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
566         gtk_widget_show(filter_box);
567
568         /* button box */
569         bbox = gtk_hbutton_box_new();
570         gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_DEFAULT_STYLE);
571         gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
572         gtk_box_pack_start(GTK_BOX(dlg_box), bbox, FALSE, FALSE, 0);
573         gtk_widget_show(bbox);
574
575         /* the start button */
576         start_button=gtk_button_new_with_label("Create Stat");
577         SIGNAL_CONNECT_OBJECT(start_button, "clicked",
578                               httpstat_start_button_clicked, NULL);
579         gtk_box_pack_start(GTK_BOX(bbox), start_button, TRUE, TRUE, 0);
580         GTK_WIDGET_SET_FLAGS(start_button, GTK_CAN_DEFAULT);
581         gtk_widget_grab_default(start_button);
582         gtk_widget_show(start_button);
583
584 #if GTK_MAJOR_VERSION < 2
585         cancel_button=gtk_button_new_with_label("Cancel");
586 #else
587         cancel_button=gtk_button_new_from_stock(GTK_STOCK_CANCEL);
588 #endif
589         SIGNAL_CONNECT(cancel_button, "clicked", dlg_cancel_cb, dlg);
590         GTK_WIDGET_SET_FLAGS(cancel_button, GTK_CAN_DEFAULT);
591         gtk_box_pack_start(GTK_BOX(bbox), cancel_button, TRUE, TRUE, 0);
592         gtk_widget_show(cancel_button);
593
594         /* Catch the "activate" signal on the filter text entry, so that
595            if the user types Return there, we act as if the "Create Stat"
596            button had been selected, as happens if Return is typed if
597            some widget that *doesn't* handle the Return key has the input
598            focus. */
599         dlg_set_activate(filter_entry, start_button);
600
601         /* Catch the "key_press_event" signal in the window, so that we can
602            catch the ESC key being pressed and act as if the "Cancel" button
603            had been selected. */
604         dlg_set_cancel(dlg, cancel_button);
605
606         /* Give the initial focus to the "Filter" entry box. */
607         gtk_widget_grab_focus(filter_entry);
608
609         gtk_widget_show_all(dlg);
610 }
611
612
613 void
614 register_tap_listener_gtkhttpstat(void)
615 {
616         register_ethereal_tap("http,stat,", gtk_httpstat_init);
617 }
618
619 void
620 register_tap_menu_gtkhttpstat(void)
621 {
622         register_tap_menu_item("Statistics/Watch protocol/HTTP...",
623             gtk_httpstat_cb, NULL, NULL);
624 }