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