Make the signatures of functions passed to "register_tap_listener()"
[obnox/wireshark/wip.git] / gtk / hostlist_table.c
1 /* hostlist_table.c   2004 Ian Schorr
2  * modified from endpoint_talkers_table.c   2003 Ronnie Sahlberg
3  * Helper routines common to all host list taps.
4  *
5  * $Id$
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 #include <stdlib.h>
32 #include <stdio.h>
33 #include <math.h>
34
35 #include <gtk/gtk.h>
36
37 #include <epan/packet_info.h>
38 #include <epan/to_str.h>
39 #include <epan/addr_resolv.h>
40 #include <epan/tap.h>
41
42 #include "compat_macros.h"
43 #include "hostlist_table.h"
44 #include "image/clist_ascend.xpm"
45 #include "image/clist_descend.xpm"
46 #include "simple_dialog.h"
47 #include "globals.h"
48 #include "gtk/find_dlg.h"
49 #include "color.h"
50 #include "gtk/color_dlg.h"
51 #include "gtkglobals.h"
52 #include "main.h"
53 #include "ui_util.h"
54 #include "dlg_utils.h"
55
56
57 #define GTK_MENU_FUNC(a) ((GtkItemFactoryCallback)(a))
58
59 #define NUM_COLS 8
60
61
62 /* convert a port number into a string */
63 static char *
64 hostlist_port_to_str(int port_type, guint32 port)
65 {
66         static int i=0;
67         static gchar *strp, str[4][12];
68
69         i++;
70         if(i>=4){
71                 i=0;
72         }
73         strp=str[i];
74
75         switch(port_type){
76         case PT_TCP:
77         case PT_UDP:
78                 g_snprintf(strp, 11, "%d", port);
79                 return strp;
80         }
81         return NULL;
82 }
83
84
85 #define FN_ANY_ADDRESS          0
86 #define FN_ANY_PORT             1
87
88 /* given an address (to distinguis between ipv4 and ipv6 for tcp/udp
89    a port_type and a name_type (FN_...)
90    return a string for the filter name
91
92    some addresses, like AT_ETHER may actually be any of multiple types
93    of protocols,   either ethernet, tokenring, fddi etc so we must be more
94    specific there  thats why we need specific_addr_type
95 */
96 static char *
97 hostlist_get_filter_name(address *addr, int specific_addr_type, int port_type, int name_type)
98 {
99         switch(name_type){
100         case FN_ANY_ADDRESS:
101                 switch(addr->type){
102                 case AT_ETHER:
103                         switch(specific_addr_type){
104                         case SAT_ETHER:
105                                 return "eth.addr";
106                         case SAT_FDDI:
107                                 return "fddi.addr";
108                         case SAT_TOKENRING:
109                                 return "tr.addr";
110                         }
111                 case AT_IPv4:
112                         return "ip.addr";
113                 case AT_IPv6:
114                         return "ipv6.addr";
115                 case AT_IPX:
116                         return "ipx.addr";
117                 case AT_FC:
118                         return "fc.id";
119                 default:
120                         ;
121                 }
122         case FN_ANY_PORT:
123                 switch(port_type){
124                 case PT_TCP:
125                         return "tcp.port";
126                 case PT_UDP:
127                         return "udp.port";
128                 }
129                 break;
130         }
131
132         g_assert_not_reached();
133         return NULL;
134 }
135
136
137 typedef struct column_arrows {
138         GtkWidget *table;
139         GtkWidget *ascend_pm;
140         GtkWidget *descend_pm;
141 } column_arrows;
142
143
144 static void
145 reset_hostlist_table_data(hostlist_table *hosts)
146 {
147     guint32 i;
148     char title[256];
149         
150         /* Allow clist to update */
151     gtk_clist_thaw(hosts->table);
152
153     if(hosts->page_lb) {
154         g_snprintf(title, 255, "Endpoints: %s", cf_get_display_name(&cfile));
155         gtk_window_set_title(GTK_WINDOW(hosts->win), title);
156         g_snprintf(title, 255, "%s", hosts->name);
157         gtk_label_set_text(GTK_LABEL(hosts->page_lb), title);
158         gtk_widget_set_sensitive(hosts->page_lb, FALSE);
159     } else {
160         g_snprintf(title, 255, "%s Endpoints: %s", hosts->name, cf_get_display_name(&cfile));
161         gtk_window_set_title(GTK_WINDOW(hosts->win), title);
162     }
163
164     /* remove all entries from the clist */
165     gtk_clist_clear(hosts->table);
166
167     /* delete all hosts */
168     for(i=0;i<hosts->num_hosts;i++){
169         g_free((gpointer)hosts->hosts[i].address.data);
170     }
171     g_free(hosts->hosts);
172     hosts->hosts=NULL;
173     hosts->num_hosts=0;
174 }
175
176 static void
177 reset_hostlist_table_data_cb(void *arg)
178 {
179     reset_hostlist_table_data(arg);
180 }
181
182 void protect_thread_critical_region(void);
183 void unprotect_thread_critical_region(void);
184 static void
185 hostlist_win_destroy_cb(GtkWindow *win _U_, gpointer data)
186 {
187         hostlist_table *hosts=(hostlist_table *)data;
188
189         protect_thread_critical_region();
190         remove_tap_listener(hosts);
191         unprotect_thread_critical_region();
192
193         reset_hostlist_table_data(hosts);
194         g_free(hosts);
195 }
196
197 static gint
198 hostlist_sort_column(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
199 {
200         char *text1 = NULL;
201         char *text2 = NULL;
202         int i1, i2;
203
204         const GtkCListRow *row1 = ptr1;
205         const GtkCListRow *row2 = ptr2;
206
207         text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
208         text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
209
210         switch(clist->sort_column){
211         case 0:
212         case 2:
213                 return strcmp (text1, text2);
214         case 1:
215         case 3:
216         case 4:
217         case 5:
218         case 6:
219         case 7:
220                 i1=atoi(text1);
221                 i2=atoi(text2);
222                 return i1-i2;
223         }
224         g_assert_not_reached();
225         
226         /* Allow clist to redraw */
227         
228         gtk_clist_thaw(clist);
229         gtk_clist_freeze(clist);
230         
231         return 0;
232 }
233
234 static void
235 hostlist_click_column_cb(GtkCList *clist, gint column, gpointer data)
236 {
237         column_arrows *col_arrows = (column_arrows *) data;
238         int i;
239
240         for (i = 0; i < NUM_COLS; i++) {
241                 gtk_widget_hide(col_arrows[i].ascend_pm);
242                 gtk_widget_hide(col_arrows[i].descend_pm);
243         }
244
245         if (column == clist->sort_column) {
246                 if (clist->sort_type == GTK_SORT_ASCENDING) {
247                         clist->sort_type = GTK_SORT_DESCENDING;
248                         gtk_widget_show(col_arrows[column].descend_pm);
249                 } else {
250                         clist->sort_type = GTK_SORT_ASCENDING;
251                         gtk_widget_show(col_arrows[column].ascend_pm);
252                 }
253         } else {
254                 clist->sort_type = GTK_SORT_DESCENDING;
255                 gtk_widget_show(col_arrows[column].descend_pm);
256                 gtk_clist_set_sort_column(clist, column);
257         }
258
259         gtk_clist_sort(clist);
260
261         /* Allow update of clist */
262         gtk_clist_thaw(clist);
263         gtk_clist_freeze(clist);
264 }
265
266
267 /* action is encoded as
268    filter_action*256+filter_type
269
270    filter_action:
271         0: Match
272         1: Prepare
273         2: Find Frame
274         3:   Find Next
275         4:   Find Previous
276         5: Colorize Host Traffic
277    filter_type:
278         0: Selected
279         1: Not Selected
280         2: And Selected
281         3: Or Selected
282         4: And Not Selected
283         5: Or Not Selected
284 */
285 static void
286 hostlist_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callback_action)
287 {
288         int action, type;
289         int selection;
290         hostlist_table *hl=(hostlist_table *)callback_data;
291         char dirstr[128];
292         char str[256];
293         const char *current_filter;
294         char *sport;
295
296         action = (callback_action>>8)&0xff;
297         type = callback_action&0xff;
298
299         selection=GPOINTER_TO_INT(g_list_nth_data(GTK_CLIST(hl->table)->selection, 0));
300         if(selection>=(int)hl->num_hosts){
301                 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No hostlist selected");
302                 return;
303         }
304         /* translate it back from row index to index in hostlist array */
305         selection=GPOINTER_TO_INT(gtk_clist_get_row_data(hl->table, selection));
306
307         sport=hostlist_port_to_str(hl->hosts[selection].port_type, hl->hosts[selection].port);
308
309         g_snprintf(dirstr, 127, "%s==%s %s%s%s%s",
310                 hostlist_get_filter_name(&hl->hosts[selection].address,
311                 hl->hosts[selection].sat, hl->hosts[selection].port_type,  FN_ANY_ADDRESS),
312                 address_to_str(&hl->hosts[selection].address),
313                 sport?" && ":"",
314                 sport?hostlist_get_filter_name(&hl->hosts[selection].address, hl->hosts[selection].sat, hl->hosts[selection].port_type,  FN_ANY_PORT):"",
315                 sport?"==":"",
316                 sport?sport:"");
317
318         current_filter=gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
319         switch(type){
320         case 0:
321                 /* selected */
322                 g_snprintf(str, 255, "%s", dirstr);
323                 break;
324         case 1:
325                 /* not selected */
326                 g_snprintf(str, 255, "!(%s)", dirstr);
327                 break;
328         case 2:
329                 /* and selected */
330                 g_snprintf(str, 255, "(%s) && (%s)", current_filter, dirstr);
331                 break;
332         case 3:
333                 /* or selected */
334                 g_snprintf(str, 255, "(%s) || (%s)", current_filter, dirstr);
335                 break;
336         case 4:
337                 /* and not selected */
338                 g_snprintf(str, 255, "(%s) && !(%s)", current_filter, dirstr);
339                 break;
340         case 5:
341                 /* or not selected */
342                 g_snprintf(str, 255, "(%s) || !(%s)", current_filter, dirstr);
343                 break;
344         }
345
346         switch(action){
347         case 0:
348                 /* match */
349                 gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), str);
350                 main_filter_packets(&cfile, str, FALSE);
351                 gdk_window_raise(top_level->window);
352                 break;
353         case 1:
354                 /* prepare */
355                 gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), str);
356                 break;
357         case 2:
358                 /* find frame */
359                 find_frame_with_filter(str);
360                 break;
361         case 3:
362                 /* find next */
363                 find_previous_next_frame_with_filter(str, FALSE);
364                 break;
365         case 4:
366                 /* find previous */
367                 find_previous_next_frame_with_filter(str, TRUE);
368                 break;
369         case 5:
370                 /* colorize host traffic */
371                 color_display_with_filter(str);
372                 break;
373         }
374 }
375 static gint
376 hostlist_show_popup_menu_cb(void *widg _U_, GdkEvent *event, hostlist_table *et)
377 {
378     GdkEventButton *bevent = (GdkEventButton *)event;
379     gint row;
380     gint column;
381
382     /* To qoute the "Gdk Event Structures" doc:
383      * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
384     if(event->type==GDK_BUTTON_PRESS && bevent->button==3){
385         /* if this is a right click on one of our columns, select it and popup the context menu */
386         if(gtk_clist_get_selection_info(et->table,
387                                           (gint) (((GdkEventButton *)event)->x),
388                                           (gint) (((GdkEventButton *)event)->y),
389                                              &row, &column)) {
390             gtk_clist_unselect_all(et->table);
391             gtk_clist_select_row(et->table, row, -1);
392
393             gtk_menu_popup(GTK_MENU(et->menu), NULL, NULL, NULL, NULL,
394                 bevent->button, bevent->time);
395         }
396     }
397
398     return FALSE;
399 }
400
401 static GtkItemFactoryEntry hostlist_list_menu_items[] =
402 {
403         /* Match */
404         ITEM_FACTORY_ENTRY("/Apply as Filter", NULL, NULL, 0, "<Branch>", NULL),
405         ITEM_FACTORY_ENTRY("/Apply as Filter/Selected", NULL,
406                 hostlist_select_filter_cb, 0*256+0, NULL, NULL),
407         ITEM_FACTORY_ENTRY("/Apply as Filter/Not Selected", NULL,
408                 hostlist_select_filter_cb, 0*256+1, NULL, NULL),
409         ITEM_FACTORY_ENTRY("/Apply as Filter/... and Selected", NULL,
410                 hostlist_select_filter_cb, 0*256+2, NULL, NULL),
411         ITEM_FACTORY_ENTRY("/Apply as Filter/... or Selected", NULL,
412                 hostlist_select_filter_cb, 0*256+3, NULL, NULL),
413         ITEM_FACTORY_ENTRY("/Apply as Filter/... and not Selected", NULL,
414                 hostlist_select_filter_cb, 0*256+4, NULL, NULL),
415         ITEM_FACTORY_ENTRY("/Apply as Filter/... or not Selected", NULL,
416                 hostlist_select_filter_cb, 0*256+5, NULL, NULL),
417
418         /* Prepare */
419         ITEM_FACTORY_ENTRY("/Prepare a Filter", NULL, NULL, 0, "<Branch>", NULL),
420         ITEM_FACTORY_ENTRY("/Prepare a Filter/Selected", NULL,
421                 hostlist_select_filter_cb, 1*256+0, NULL, NULL),
422         ITEM_FACTORY_ENTRY("/Prepare a Filter/Not Selected", NULL,
423                 hostlist_select_filter_cb, 1*256+1, NULL, NULL),
424         ITEM_FACTORY_ENTRY("/Prepare a Filter/... and Selected", NULL,
425                 hostlist_select_filter_cb, 1*256+2, NULL, NULL),
426         ITEM_FACTORY_ENTRY("/Prepare a Filter/... or Selected", NULL,
427                 hostlist_select_filter_cb, 1*256+3, NULL, NULL),
428         ITEM_FACTORY_ENTRY("/Prepare a Filter/... and not Selected", NULL,
429                 hostlist_select_filter_cb, 1*256+4, NULL, NULL),
430         ITEM_FACTORY_ENTRY("/Prepare a Filter/... or not Selected", NULL,
431                 hostlist_select_filter_cb, 1*256+5, NULL, NULL),
432
433         /* Find Frame */
434         ITEM_FACTORY_ENTRY("/Find Frame", NULL, NULL, 0, "<Branch>", NULL),
435         ITEM_FACTORY_ENTRY("/Find Frame/Find Frame", NULL,
436                 hostlist_select_filter_cb, 2*256+0, NULL, NULL),
437         /* Find Next */
438         ITEM_FACTORY_ENTRY("/Find Frame/Find Next", NULL,
439                 hostlist_select_filter_cb, 3*256+0, NULL, NULL),
440         /* Find Previous */
441         ITEM_FACTORY_ENTRY("/Find Frame/Find Previous", NULL,
442                 hostlist_select_filter_cb, 4*256+0, NULL, NULL),
443         /* Colorize Host Traffic */
444         ITEM_FACTORY_ENTRY("/Colorize Host Traffic", NULL,
445                 hostlist_select_filter_cb, 5*256+0, NULL, NULL),
446
447 };
448
449 static void
450 hostlist_create_popup_menu(hostlist_table *hl)
451 {
452     GtkItemFactory *item_factory;
453
454     item_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<main>", NULL);
455
456     gtk_item_factory_create_items_ac(item_factory, sizeof(hostlist_list_menu_items)/sizeof(hostlist_list_menu_items[0]), hostlist_list_menu_items, hl, 2);
457
458     hl->menu = gtk_item_factory_get_widget(item_factory, "<main>");
459     SIGNAL_CONNECT(hl->table, "button_press_event", hostlist_show_popup_menu_cb, hl);
460 }
461
462
463 /* Draw/refresh the address field of a single entry at the specified index */
464 static void
465 draw_hostlist_table_address(hostlist_table *hl, int hostlist_idx)
466 {
467     const char *entry;
468     char *port;
469     guint32 pt;
470     int rownum;
471
472     rownum=gtk_clist_find_row_from_data(hl->table, (gpointer)hostlist_idx);
473
474     if(!hl->resolve_names)
475         entry=address_to_str(&hl->hosts[hostlist_idx].address);
476     else {
477         entry=get_addr_name(&hl->hosts[hostlist_idx].address);
478         if(!entry)
479             entry=address_to_str(&hl->hosts[hostlist_idx].address);
480     }
481     gtk_clist_set_text(hl->table, rownum, 0, entry);
482
483     pt = hl->hosts[hostlist_idx].port_type;
484     if(!hl->resolve_names) pt = PT_NONE;
485     switch(pt) {
486     case(PT_TCP):
487         entry=get_tcp_port(hl->hosts[hostlist_idx].port);
488         break;
489     case(PT_UDP):
490         entry=get_udp_port(hl->hosts[hostlist_idx].port);
491         break;
492     default:
493         port=hostlist_port_to_str(hl->hosts[hostlist_idx].port_type, hl->hosts[hostlist_idx].port);
494         entry=port?port:"";
495     }
496     gtk_clist_set_text(hl->table, rownum, 1, entry);
497 }
498
499 /* Refresh the address fields of all entries in the list */
500 static void
501 draw_hostlist_table_addresses(hostlist_table *hl)
502 {
503     guint32 i;
504
505     for(i=0;i<hl->num_hosts;i++){
506         draw_hostlist_table_address(hl, i);
507     }
508 }
509
510
511 static void
512 draw_hostlist_table_data(hostlist_table *hl)
513 {
514     guint32 i;
515     int j;
516     char title[256];
517
518     if (hl->page_lb) {
519         if(hl->num_hosts) {
520             g_snprintf(title, 255, "%s: %u", hl->name, hl->num_hosts);
521         } else {
522             g_snprintf(title, 255, "%s", hl->name);
523         }
524         gtk_label_set_text(GTK_LABEL(hl->page_lb), title);
525         gtk_widget_set_sensitive(hl->page_lb, hl->num_hosts);
526     }
527
528     for(i=0;i<hl->num_hosts;i++){
529         char str[16];
530
531         j=gtk_clist_find_row_from_data(hl->table, (gpointer)i);
532
533         g_snprintf(str, 16, "%u", hl->hosts[i].tx_frames+hl->hosts[i].rx_frames);
534         gtk_clist_set_text(hl->table, j, 2, str);
535         g_snprintf(str, 16, "%u", hl->hosts[i].tx_bytes+hl->hosts[i].rx_bytes);
536         gtk_clist_set_text(hl->table, j, 3, str);
537
538
539         g_snprintf(str, 16, "%u", hl->hosts[i].tx_frames);
540         gtk_clist_set_text(hl->table, j, 4, str);
541         g_snprintf(str, 16, "%u", hl->hosts[i].tx_bytes);
542         gtk_clist_set_text(hl->table, j, 5, str);
543
544
545         g_snprintf(str, 16, "%u", hl->hosts[i].rx_frames);
546         gtk_clist_set_text(hl->table, j, 6, str);
547         g_snprintf(str, 16, "%u", hl->hosts[i].rx_bytes);
548         gtk_clist_set_text(hl->table, j, 7, str);
549
550     }
551         
552     draw_hostlist_table_addresses(hl);
553         
554     gtk_clist_sort(hl->table);
555
556     /* Allow table to redraw. */
557     gtk_clist_thaw(hl->table);
558     gtk_clist_freeze(hl->table);
559 }
560
561 static void
562 draw_hostlist_table_data_cb(void *arg)
563 {
564     draw_hostlist_table_data(arg);
565 }
566
567 #if (GTK_MAJOR_VERSION >= 2)
568 static void
569 copy_as_csv_cb(GtkWindow *win _U_, gpointer data)
570 {
571    guint32         i,j;
572    gchar           *table_entry;
573    GtkClipboard    *cb;  
574    GString         *CSV_str = g_string_new("");
575    
576    hostlist_table *hosts=(hostlist_table *)data;
577    
578    /* Add the column headers to the CSV data */
579    for(i=0;i<hosts->num_columns;i++){                  /* all columns         */
580     if(i==1 && !hosts->has_ports) continue;            /* Don't add the port column if it's empty */
581      g_string_append(CSV_str,hosts->default_titles[i]);/* add the column heading to the CSV string */
582     if(i!=hosts->num_columns-1)
583      g_string_append(CSV_str,",");
584    }
585    g_string_append(CSV_str,"\n");                      /* new row */
586  
587    /* Add the column values to the CSV data */
588    for(i=0;i<hosts->num_hosts;i++){                    /* all rows            */
589     for(j=0;j<hosts->num_columns;j++){                 /* all columns         */
590      if(j==1 && !hosts->has_ports) continue;           /* Don't add the port column if it's empty */
591      gtk_clist_get_text(hosts->table,i,j,&table_entry);/* copy table item into string */
592      g_string_append(CSV_str,table_entry);             /* add the table entry to the CSV string */
593     if(j!=hosts->num_columns-1)
594      g_string_append(CSV_str,",");
595     } 
596     g_string_append(CSV_str,"\n");                     /* new row */  
597    }
598
599    /* Now that we have the CSV data, copy it into the default clipboard */
600    cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);    /* Get the default clipboard */
601    gtk_clipboard_set_text(cb, CSV_str->str, -1);       /* Copy the CSV data into the clipboard */
602    g_string_free(CSV_str, TRUE);                       /* Free the memory */
603
604 #endif
605
606
607 static gboolean
608 init_hostlist_table_page(hostlist_table *hosttable, GtkWidget *vbox, gboolean hide_ports, char *table_name, char *tap_name, char *filter, tap_packet_cb packet_func)
609 {
610     int i;
611     column_arrows *col_arrows;
612     GtkStyle *win_style;
613     GtkWidget *column_lb;
614     GString *error_string;
615     char title[256];
616 #if (GTK_MAJOR_VERSION >= 2)
617     GtkWidget *copy_bt;
618     GtkTooltips *tooltips = gtk_tooltips_new();
619 #endif           
620
621
622     hosttable->num_columns=NUM_COLS; 
623     hosttable->default_titles[0] = "Address";
624     hosttable->default_titles[1] = "Port";
625     hosttable->default_titles[2] = "Packets";
626     hosttable->default_titles[3] = "Bytes";
627     hosttable->default_titles[4] = "Tx Packets";
628     hosttable->default_titles[5] = "Tx Bytes";
629     hosttable->default_titles[6] = "Rx Packets";
630     hosttable->default_titles[7] = "Rx Bytes";
631     hosttable->has_ports=!hide_ports;
632     hosttable->num_hosts = 0;
633     hosttable->resolve_names=TRUE;
634
635     g_snprintf(title, 255, "%s Endpoints", table_name); 
636     hosttable->page_lb = gtk_label_new(title);                                                 
637     gtk_box_pack_start(GTK_BOX(vbox), hosttable->page_lb, FALSE, FALSE, 0);
638
639     hosttable->scrolled_window=scrolled_window_new(NULL, NULL);
640     gtk_box_pack_start(GTK_BOX(vbox), hosttable->scrolled_window, TRUE, TRUE, 0);
641
642     hosttable->table=(GtkCList *)gtk_clist_new(NUM_COLS);
643
644     col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * NUM_COLS);
645     win_style = gtk_widget_get_style(hosttable->scrolled_window);
646     for (i = 0; i < NUM_COLS; i++) {
647         col_arrows[i].table = gtk_table_new(2, 2, FALSE);
648         gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
649         column_lb = gtk_label_new(hosttable->default_titles[i]);
650         gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
651         gtk_widget_show(column_lb);
652
653         col_arrows[i].ascend_pm = xpm_to_widget((const char **) clist_ascend_xpm);
654         gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
655         col_arrows[i].descend_pm = xpm_to_widget((const char **) clist_descend_xpm);
656         gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
657         /* make total frames be the default sort order */
658         if (i == 4) {
659             gtk_widget_show(col_arrows[i].descend_pm);
660         }
661         gtk_clist_set_column_widget(GTK_CLIST(hosttable->table), i, col_arrows[i].table);
662         gtk_widget_show(col_arrows[i].table);
663     }
664     gtk_clist_column_titles_show(GTK_CLIST(hosttable->table));
665
666     gtk_clist_set_compare_func(hosttable->table, hostlist_sort_column);
667     gtk_clist_set_sort_column(hosttable->table, 4);
668     gtk_clist_set_sort_type(hosttable->table, GTK_SORT_DESCENDING);
669
670     gtk_clist_set_column_auto_resize(hosttable->table, 0, TRUE);
671     gtk_clist_set_column_auto_resize(hosttable->table, 1, TRUE);
672     gtk_clist_set_column_auto_resize(hosttable->table, 2, TRUE);
673     gtk_clist_set_column_auto_resize(hosttable->table, 3, TRUE);
674     gtk_clist_set_column_auto_resize(hosttable->table, 4, TRUE);
675     gtk_clist_set_column_auto_resize(hosttable->table, 5, TRUE);
676     gtk_clist_set_column_auto_resize(hosttable->table, 6, TRUE);
677     gtk_clist_set_column_auto_resize(hosttable->table, 7, TRUE);
678
679     gtk_clist_set_shadow_type(hosttable->table, GTK_SHADOW_IN);
680     gtk_clist_column_titles_show(hosttable->table);
681     gtk_container_add(GTK_CONTAINER(hosttable->scrolled_window), (GtkWidget *)hosttable->table);
682
683     SIGNAL_CONNECT(hosttable->table, "click-column", hostlist_click_column_cb, col_arrows);
684
685     hosttable->num_hosts=0;
686     hosttable->hosts=NULL;
687
688     /* hide srcport and dstport if we don't use ports */
689     if(hide_ports){
690         gtk_clist_set_column_visibility(hosttable->table, 1, FALSE);
691     }
692
693     /* create popup menu for this table */
694     hostlist_create_popup_menu(hosttable);
695
696 #if (GTK_MAJOR_VERSION >= 2)
697     /* XXX - maybe we want to have a "Copy as CSV" stock button here? */
698     /*copy_bt = gtk_button_new_with_label ("Copy content to clipboard as CSV");*/
699     copy_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_COPY);
700     gtk_tooltips_set_tip(tooltips, copy_bt, 
701         "Copy all statistical values of this page to the clipboard in CSV (Comma Seperated Values) format.", NULL);
702     SIGNAL_CONNECT(copy_bt, "clicked", copy_as_csv_cb,(gpointer *) hosttable);
703     gtk_box_pack_start(GTK_BOX(vbox), copy_bt, FALSE, FALSE, 0);
704 #endif
705
706     /* register the tap and rerun the taps on the packet list */
707     error_string=register_tap_listener(tap_name, hosttable, filter, reset_hostlist_table_data_cb, packet_func, draw_hostlist_table_data_cb);
708     if(error_string){
709         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str);
710         g_string_free(error_string, TRUE);
711         g_free(hosttable);
712         return FALSE;
713     }
714
715     return TRUE;
716 }
717
718
719 void
720 init_hostlist_table(gboolean hide_ports, char *table_name, char *tap_name, char *filter, tap_packet_cb packet_func)
721 {
722     hostlist_table *hosttable;
723     char title[256];
724     GtkWidget *vbox;
725     GtkWidget *bbox;
726     GtkWidget *close_bt;
727     gboolean ret;
728
729
730     hosttable=g_malloc(sizeof(hostlist_table));
731
732     hosttable->name=table_name;
733     g_snprintf(title, 255, "%s Endpoints: %s", table_name, cf_get_display_name(&cfile));
734     hosttable->win=window_new(GTK_WINDOW_TOPLEVEL, title);
735     
736     gtk_window_set_default_size(GTK_WINDOW(hosttable->win), 750, 400);
737
738     vbox=gtk_vbox_new(FALSE, 3);
739     gtk_container_add(GTK_CONTAINER(hosttable->win), vbox);
740     gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
741
742     ret = init_hostlist_table_page(hosttable, vbox, hide_ports, table_name, tap_name, filter, packet_func);
743     if(ret == FALSE) {
744         g_free(hosttable);
745         return;
746     }
747
748     /* Button row. */
749     bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
750     gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
751
752     close_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_CLOSE);
753     window_set_cancel_button(hosttable->win, close_bt, window_cancel_button_cb);
754
755     SIGNAL_CONNECT(hosttable->win, "delete_event", window_delete_event_cb, NULL);
756     SIGNAL_CONNECT(hosttable->win, "destroy", hostlist_win_destroy_cb, hosttable);
757
758     gtk_widget_show_all(hosttable->win);
759     window_present(hosttable->win);
760
761     retap_packets(&cfile);
762         
763     /* Keep clist frozen to cause modifications to the clist (inserts, appends, others that are extremely slow
764            in GTK2) to not be drawn, allow refreshes to occur at strategic points for performance */
765         gtk_clist_freeze(hosttable->table);
766
767
768     /* after retapping, redraw table */
769     draw_hostlist_table_data(hosttable);
770 }
771
772
773 static void
774 hostlist_win_destroy_notebook_cb(GtkWindow *win _U_, gpointer data)
775 {
776     void ** pages = data;
777     int page;
778
779     /* first "page" contains the number of pages */
780     for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) {
781         hostlist_win_destroy_cb(NULL, pages[page]);
782     }
783 }
784
785
786
787
788 static hostlist_table *
789 init_hostlist_notebook_page_cb(gboolean hide_ports, char *table_name, char *tap_name, char *filter, tap_packet_cb packet_func)
790 {
791     gboolean ret;
792     GtkWidget *page_vbox;
793     hostlist_table *hosttable;
794
795     hosttable=g_malloc(sizeof(hostlist_table));
796     hosttable->name=table_name;
797     hosttable->resolve_names=TRUE;
798
799     page_vbox=gtk_vbox_new(FALSE, 6);
800     hosttable->win = page_vbox;
801     gtk_container_set_border_width(GTK_CONTAINER(page_vbox), 6);
802
803     ret = init_hostlist_table_page(hosttable, page_vbox, hide_ports, table_name, tap_name, filter, packet_func);
804     if(ret == FALSE) {
805         g_free(hosttable);
806         return NULL;
807     }
808
809     return hosttable;
810 }
811
812
813 typedef struct {
814     gboolean hide_ports;       /* hide TCP / UDP port columns */
815     char *table_name;          /* GUI output name */
816     char *tap_name;            /* internal name */
817     char *filter;              /* display filter string (unused) */
818     tap_packet_cb packet_func; /* function to be called for new incoming packets */
819 } register_hostlist_t;
820
821
822 static GSList *registered_hostlist_tables = NULL;
823
824 void
825 register_hostlist_table(gboolean hide_ports, char *table_name, char *tap_name, char *filter, tap_packet_cb packet_func)
826 {
827     register_hostlist_t *table;
828
829     table = g_malloc(sizeof(register_hostlist_t));
830
831     table->hide_ports   = hide_ports;
832     table->table_name   = table_name;
833     table->tap_name     = tap_name;
834     table->filter       = filter;
835     table->packet_func  = packet_func;
836
837     registered_hostlist_tables = g_slist_append(registered_hostlist_tables, table);
838 }
839
840
841 static void
842 hostlist_resolve_toggle_dest(GtkWidget *widget, gpointer data)
843 {
844     int page;
845     void ** pages = data;
846     gboolean resolve_names;
847     hostlist_table *hosttable;
848
849
850     resolve_names = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
851
852     for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) {
853         hosttable = pages[page];
854         hosttable->resolve_names = resolve_names;
855
856         draw_hostlist_table_addresses(hosttable);
857
858         gtk_clist_thaw(hosttable->table);
859         gtk_clist_freeze(hosttable->table);
860     }
861 }
862
863
864 void
865 init_hostlist_notebook_cb(GtkWidget *w _U_, gpointer d _U_)
866 {
867     hostlist_table *hosttable;
868     char title[256];
869     GtkWidget *vbox;
870     GtkWidget *hbox;
871     GtkWidget *bbox;
872     GtkWidget *close_bt;
873     GtkWidget *win;
874     GtkWidget *resolv_cb;
875     int page;
876     void ** pages;
877     GtkWidget *nb;
878     GtkWidget *page_lb;
879     GSList  *current_table;
880     register_hostlist_t *registered;
881     GtkTooltips *tooltips = gtk_tooltips_new();
882
883
884     pages = g_malloc(sizeof(void *) * (g_slist_length(registered_hostlist_tables) + 1));
885
886     win=window_new(GTK_WINDOW_TOPLEVEL, "hostlist");
887     g_snprintf(title, 255, "Endpoints: %s", cf_get_display_name(&cfile));
888     gtk_window_set_title(GTK_WINDOW(win), title);
889     gtk_window_set_default_size(GTK_WINDOW(win), 750, 400);
890
891     vbox=gtk_vbox_new(FALSE, 6);
892     gtk_container_add(GTK_CONTAINER(win), vbox);
893     gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
894
895     nb = gtk_notebook_new();
896     gtk_container_add(GTK_CONTAINER(vbox), nb);
897
898     page = 0;
899
900     current_table = registered_hostlist_tables;
901     while(current_table) {
902         registered = current_table->data;
903         page_lb = gtk_label_new("");
904         hosttable = init_hostlist_notebook_page_cb(registered->hide_ports, registered->table_name, registered->tap_name,
905             registered->filter, registered->packet_func);
906         gtk_notebook_append_page(GTK_NOTEBOOK(nb), hosttable->win, page_lb);
907         hosttable->win = win;
908         hosttable->page_lb = page_lb;
909         pages[++page] = hosttable;
910
911         current_table = g_slist_next(current_table);
912     }
913
914     pages[0] = GINT_TO_POINTER(page);
915
916     hbox = gtk_hbox_new(FALSE, 3);
917     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
918
919     resolv_cb = CHECK_BUTTON_NEW_WITH_MNEMONIC("Name resolution", NULL);
920     gtk_container_add(GTK_CONTAINER(hbox), resolv_cb);
921     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(resolv_cb), TRUE);
922     gtk_tooltips_set_tip(tooltips, resolv_cb, "Show results of name resolutions rather than the \"raw\" values. "
923         "Please note: The corresponding name resolution must be enabled.", NULL);
924
925     SIGNAL_CONNECT(resolv_cb, "toggled", hostlist_resolve_toggle_dest, pages);
926
927     /* Button row. */
928     bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
929     gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
930
931     close_bt = OBJECT_GET_DATA(bbox, GTK_STOCK_CLOSE);
932     window_set_cancel_button(win, close_bt, window_cancel_button_cb);
933
934     SIGNAL_CONNECT(win, "delete_event", window_delete_event_cb, NULL);
935     SIGNAL_CONNECT(win, "destroy", hostlist_win_destroy_notebook_cb, pages);
936
937     gtk_widget_show_all(win);
938     window_present(win);
939
940     retap_packets(&cfile);
941
942     /* after retapping, redraw table */
943     for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) {
944         draw_hostlist_table_data(pages[page]);
945     }
946 }
947
948
949
950 void
951 add_hostlist_table_data(hostlist_table *hl, const address *addr, guint32 port, gboolean sender, int num_frames, int num_bytes, SAT_E sat, int port_type)
952 {
953     hostlist_talker_t *talker=NULL;
954     int talker_idx=0;
955     gboolean new_talker;
956
957     new_talker=FALSE;
958     /* XXX should be optimized to allocate n extra entries at a time
959        instead of just one */
960     /* if we dont have any entries at all yet */
961     if(hl->hosts==NULL){
962         hl->hosts=g_malloc(sizeof(hostlist_talker_t));
963         hl->num_hosts=1;
964         talker=&hl->hosts[0];
965         talker_idx=0;
966         new_talker=TRUE;
967     }
968
969     /* try to find it among the existing known hosts */
970     if(talker==NULL){
971         guint32 i;
972         for(i=0;i<hl->num_hosts;i++){
973             if(  (!CMP_ADDRESS(&hl->hosts[i].address, addr))&&(hl->hosts[i].port==port) ){
974                 talker=&hl->hosts[i];
975                 talker_idx=i;
976                 break;
977             }
978         }
979     }
980
981     /* if we still dont know what talker this is it has to be a new one
982        and we have to allocate it and append it to the end of the list */
983     if(talker==NULL){
984         new_talker=TRUE;
985         hl->num_hosts++;
986         hl->hosts=g_realloc(hl->hosts, hl->num_hosts*sizeof(hostlist_talker_t));
987         talker=&hl->hosts[hl->num_hosts-1];
988         talker_idx=hl->num_hosts-1;
989     }
990
991     /* if this is a new talker we need to initialize the struct */
992     if(new_talker){
993         COPY_ADDRESS(&talker->address, addr);
994         talker->sat=sat;
995         talker->port_type=port_type;
996         talker->port=port;
997         talker->rx_frames=0;
998         talker->tx_frames=0;
999         talker->rx_bytes=0;
1000         talker->tx_bytes=0;
1001     }
1002
1003     /* update the talker struct */
1004     if( sender ){
1005         talker->tx_frames+=num_frames;
1006         talker->tx_bytes+=num_bytes;
1007     } else {
1008         talker->rx_frames+=num_frames;
1009         talker->rx_bytes+=num_bytes;
1010     }
1011
1012     /* if this was a new talker we have to create a clist row for it */
1013     if(new_talker){
1014         char *entries[NUM_COLS];
1015         char frames[16],bytes[16],txframes[16],txbytes[16],rxframes[16],rxbytes[16];
1016
1017         /* these values will be filled by call to draw_hostlist_table_addresses() below */
1018         entries[0]="";
1019         entries[1]="";
1020
1021         g_snprintf(frames, 16, "%u", talker->tx_frames+talker->rx_frames);
1022         entries[2]=frames;
1023         g_snprintf(bytes, 16, "%u", talker->tx_bytes+talker->rx_bytes);
1024         entries[3]=bytes;
1025
1026         g_snprintf(txframes, 16, "%u", talker->tx_frames);
1027         entries[4]=txframes;
1028         g_snprintf(txbytes, 16, "%u", talker->tx_bytes);
1029         entries[5]=txbytes;
1030
1031         g_snprintf(rxframes, 16, "%u", talker->rx_frames);
1032         entries[6]=rxframes;
1033         g_snprintf(rxbytes, 16, "%u", talker->rx_bytes);
1034         entries[7]=rxbytes;
1035
1036         gtk_clist_insert(hl->table, talker_idx, entries);
1037         gtk_clist_set_row_data(hl->table, talker_idx, (gpointer) talker_idx);
1038
1039                 draw_hostlist_table_address(hl, talker_idx);
1040     }
1041 }