From 30f3d524411445c26418c757ac9deb0940afc409 Mon Sep 17 00:00:00 2001 From: Gerald Combs Date: Wed, 6 Aug 2014 10:07:42 -0700 Subject: [PATCH] Qt: Refactor ConversationDialog for endpoints. Create a TrafficTableDialog (for lack of a better name) parent class from the general parts of ConversationDialog. Use it to create EndpointsDialog. Move the contents of conversation_tree_widget.{cpp,h} to conversation_dialog.{cpp,h} to match endpoint_dialog and traffic_table_dialog. Fill in GeoIP columns dynamically instead of using a hard-coded limit. Use "endp_" and "ENDP_" prefixes for a lot of endpoint variables and defines. Try to make geoip_db_lookup_ipv4 and geoip_db_lookup_ipv6 more robust. Clean up some includes. Fix a shadowed variable. Change-Id: I23054816ac7f8c6edb3b1f01c8536db37ba4122d Reviewed-on: https://code.wireshark.org/review/3462 Petri-Dish: Gerald Combs Tested-by: Petri Dish Buildbot Reviewed-by: Gerald Combs --- epan/conversation_table.h | 2 +- epan/geoip_db.c | 69 +- epan/geoip_db.h | 6 +- ui/CMakeLists.txt | 2 +- ui/Makefile.common | 4 +- ui/cli/tap-iousers.c | 2 +- ui/gtk/conversations_table.c | 6 +- ui/gtk/conversations_table.h | 2 +- ui/gtk/hostlist_table.c | 181 ++--- ui/gtk/hostlist_table.h | 56 +- ui/qt/CMakeLists.txt | 8 +- ui/qt/Makefile.am | 4 +- ui/qt/Makefile.common | 14 +- ui/qt/Wireshark.pro | 8 +- ui/qt/conversation_dialog.cpp | 617 ++++++++++++------ ui/qt/conversation_dialog.h | 46 +- ui/qt/conversation_tree_widget.cpp | 487 -------------- ui/qt/conversation_tree_widget.h | 112 ---- ui/qt/endpoint_dialog.cpp | 545 ++++++++++++++++ ui/qt/endpoint_dialog.h | 88 +++ ui/qt/filter_action.cpp | 11 +- ui/qt/filter_action.h | 3 +- ui/qt/main.cpp | 3 +- ui/qt/main_window.h | 4 +- ui/qt/main_window.ui | 11 +- ui/qt/main_window_slots.cpp | 24 +- ui/qt/traffic_table_dialog.cpp | 347 ++++++++++ ui/qt/traffic_table_dialog.h | 168 +++++ ...tion_dialog.ui => traffic_table_dialog.ui} | 18 +- ui/recent.c | 11 +- ui/recent.h | 1 + ui/{conversation_ui.c => traffic_table_ui.c} | 21 +- ui/{conversation_ui.h => traffic_table_ui.h} | 26 +- 33 files changed, 1866 insertions(+), 1041 deletions(-) delete mode 100644 ui/qt/conversation_tree_widget.cpp delete mode 100644 ui/qt/conversation_tree_widget.h create mode 100644 ui/qt/endpoint_dialog.cpp create mode 100644 ui/qt/endpoint_dialog.h create mode 100644 ui/qt/traffic_table_dialog.cpp create mode 100644 ui/qt/traffic_table_dialog.h rename ui/qt/{conversation_dialog.ui => traffic_table_dialog.ui} (86%) rename ui/{conversation_ui.c => traffic_table_ui.c} (75%) rename ui/{conversation_ui.h => traffic_table_ui.h} (77%) diff --git a/epan/conversation_table.h b/epan/conversation_table.h index 4f79667dbc..6de15c7ca3 100644 --- a/epan/conversation_table.h +++ b/epan/conversation_table.h @@ -144,7 +144,7 @@ typedef struct _hostlist_talker_t { } hostlist_talker_t; -/** Register the conversation table for the multiple conversation window. +/** Register the conversation table for the conversation and endpoint windows. * * @param proto_id is the protocol with conversation * @param hide_ports hide the port columns diff --git a/epan/geoip_db.c b/epan/geoip_db.c index 8ca86c0603..9d9c2ebe53 100644 --- a/epan/geoip_db.c +++ b/epan/geoip_db.c @@ -282,24 +282,27 @@ geoip_db_lookup_latlon4(guint32 addr, float *lat, float *lon) { return -1; } -#define VAL_STR_LEN 100 - /* * GeoIP 1.4.3 and later provide GeoIP_set_charset(), but in versions * 1.4.3 to 1.4.6 that only applies to the City databases. I.e., it's * possible to produce invalid UTF-8 sequences even if GeoIP_set_charset() * is used. */ -static void -iso_8859_1_to_utf_8(char *val) { - char *utf8_val; - utf8_val = g_convert(val, VAL_STR_LEN, "UTF-8", "ISO-8859-1", NULL, NULL, NULL); - if (utf8_val) { - g_strlcpy(val, utf8_val, VAL_STR_LEN); - g_free(utf8_val); +/* Ensure that a given db value is UTF-8 */ +static const char * +db_val_to_utf_8(const char *val, GeoIP *gi) { + + if (GeoIP_charset(gi) == GEOIP_CHARSET_ISO_8859_1) { + char *utf8_val; + utf8_val = g_convert(val, -1, "UTF-8", "ISO-8859-1", NULL, NULL, NULL); + if (utf8_val) { + char *ret_val = ep_strdup(utf8_val); + g_free(utf8_val); + return ret_val; + } } - + return val; } const char * @@ -307,7 +310,7 @@ geoip_db_lookup_ipv4(guint dbnum, guint32 addr, const char *not_found) { GeoIP *gi; GeoIPRecord *gir; const char *raw_val, *ret = not_found; - static char val[VAL_STR_LEN]; + char *val; gi = g_array_index(geoip_dat_arr, GeoIP *, dbnum); if (gi) { @@ -315,9 +318,7 @@ geoip_db_lookup_ipv4(guint dbnum, guint32 addr, const char *not_found) { case GEOIP_COUNTRY_EDITION: raw_val = GeoIP_country_name_by_ipnum(gi, addr); if (raw_val) { - g_snprintf(val, VAL_STR_LEN, "%s", raw_val); - iso_8859_1_to_utf_8(val); - ret = val; + ret = db_val_to_utf_8(raw_val, gi); } break; @@ -325,13 +326,10 @@ geoip_db_lookup_ipv4(guint dbnum, guint32 addr, const char *not_found) { case GEOIP_CITY_EDITION_REV1: gir = GeoIP_record_by_ipnum(gi, addr); if (gir && gir->city && gir->region) { - g_snprintf(val, VAL_STR_LEN, "%s, %s", gir->city, gir->region); - iso_8859_1_to_utf_8(val); - ret = val; + val = ep_strdup_printf("%s, %s", gir->city, gir->region); + ret = db_val_to_utf_8(val, gi); } else if (gir && gir->city) { - g_snprintf(val, VAL_STR_LEN, "%s", gir->city); - iso_8859_1_to_utf_8(val); - ret = val; + ret = db_val_to_utf_8(gir->city, gi); } break; @@ -340,9 +338,7 @@ geoip_db_lookup_ipv4(guint dbnum, guint32 addr, const char *not_found) { case GEOIP_ASNUM_EDITION: raw_val = GeoIP_name_by_ipnum(gi, addr); if (raw_val) { - g_snprintf(val, VAL_STR_LEN, "%s", raw_val); - iso_8859_1_to_utf_8(val); - ret = val; + ret = db_val_to_utf_8(raw_val, gi); } break; @@ -352,7 +348,7 @@ geoip_db_lookup_ipv4(guint dbnum, guint32 addr, const char *not_found) { float lon; char *c; if(geoip_db_lookup_latlon4(addr, &lat, &lon) == 0) { - g_snprintf(val, VAL_STR_LEN, "%f", lat); + val = ep_strdup_printf("%f", lat); c = strchr(val, ','); if (c != NULL) *c = '.'; ret = val; @@ -366,7 +362,7 @@ geoip_db_lookup_ipv4(guint dbnum, guint32 addr, const char *not_found) { float lon; char *c; if(geoip_db_lookup_latlon4(addr, &lat, &lon) == 0) { - g_snprintf(val, VAL_STR_LEN, "%f", lon); + val = ep_strdup_printf("%f", lat); c = strchr(val, ','); if (c != NULL) *c = '.'; ret = val; @@ -423,7 +419,7 @@ geoip_db_lookup_ipv6(guint dbnum, struct e_in6_addr addr, const char *not_found) GeoIP *gi; geoipv6_t gaddr; const char *raw_val, *ret = not_found; - static char val[VAL_STR_LEN]; + char *val; #if NUM_DB_TYPES > 31 GeoIPRecord *gir; #endif @@ -436,9 +432,7 @@ geoip_db_lookup_ipv6(guint dbnum, struct e_in6_addr addr, const char *not_found) case GEOIP_COUNTRY_EDITION_V6: raw_val = GeoIP_country_name_by_ipnum_v6(gi, gaddr); if (raw_val) { - g_snprintf(val, VAL_STR_LEN, "%s", raw_val); - iso_8859_1_to_utf_8(val); - ret = val; + ret = db_val_to_utf_8(raw_val, gi); } break; @@ -447,13 +441,10 @@ geoip_db_lookup_ipv6(guint dbnum, struct e_in6_addr addr, const char *not_found) case GEOIP_CITY_EDITION_REV1_V6: gir = GeoIP_record_by_ipnum_v6(gi, gaddr); if (gir && gir->city && gir->region) { - g_snprintf(val, VAL_STR_LEN, "%s, %s", gir->city, gir->region); - iso_8859_1_to_utf_8(val); - ret = val; + val = ep_strdup_printf("%s, %s", gir->city, gir->region); + ret = db_val_to_utf_8(val, gi); } else if (gir && gir->city) { - g_snprintf(val, VAL_STR_LEN, "%s", gir->city); - iso_8859_1_to_utf_8(val); - ret = val; + ret = db_val_to_utf_8(gir->city, gi); } break; @@ -462,9 +453,7 @@ geoip_db_lookup_ipv6(guint dbnum, struct e_in6_addr addr, const char *not_found) case GEOIP_ASNUM_EDITION_V6: raw_val = GeoIP_name_by_ipnum_v6(gi, gaddr); if (raw_val) { - g_snprintf(val, VAL_STR_LEN, "%s", raw_val); - iso_8859_1_to_utf_8(val); - ret = val; + ret = db_val_to_utf_8(raw_val, gi); } break; #endif /* NUM_DB_TYPES */ @@ -475,7 +464,7 @@ geoip_db_lookup_ipv6(guint dbnum, struct e_in6_addr addr, const char *not_found) float lon; char *c; if(geoip_db_lookup_latlon6(gaddr, &lat, &lon) == 0) { - g_snprintf(val, VAL_STR_LEN, "%f", lat); + val = ep_strdup_printf("%f", lat); c = strchr(val, ','); if (c != NULL) *c = '.'; ret = val; @@ -489,7 +478,7 @@ geoip_db_lookup_ipv6(guint dbnum, struct e_in6_addr addr, const char *not_found) float lon; char *c; if(geoip_db_lookup_latlon6(gaddr, &lat, &lon) == 0) { - g_snprintf(val, VAL_STR_LEN, "%f", lon); + val = ep_strdup_printf("%f", lat); c = strchr(val, ','); if (c != NULL) *c = '.'; ret = val; diff --git a/epan/geoip_db.h b/epan/geoip_db.h index 8388b0b83e..346b50b602 100644 --- a/epan/geoip_db.h +++ b/epan/geoip_db.h @@ -73,7 +73,8 @@ WS_DLL_PUBLIC int geoip_db_type(guint dbnum); * @param dbnum Database index * @param addr IPv4 address to look up * @param not_found The string to return if the lookup fails. May be NULL. - * @return The database entry if found, else not_found + * + * @return The database entry if found, else not_found. Return value must not be freed. */ WS_DLL_PUBLIC const char *geoip_db_lookup_ipv4(guint dbnum, guint32 addr, const char *not_found); @@ -83,7 +84,8 @@ WS_DLL_PUBLIC const char *geoip_db_lookup_ipv4(guint dbnum, guint32 addr, const * @param dbnum Database index * @param addr IPv6 address to look up * @param not_found The string to return if the lookup fails. May be NULL. - * @return The database entry if found, else not_found + * + * @return The database entry if found, else not_found. Return value must not be freed. */ WS_DLL_PUBLIC const char *geoip_db_lookup_ipv6(guint dbnum, struct e_in6_addr addr, const char *not_found); diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 5926d905e2..1c209848fa 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -24,7 +24,6 @@ set(COMMON_UI_SRC alert_box.c capture.c capture_ui_utils.c - conversation_ui.c decode_as_utils.c export_object.c export_object_dicom.c @@ -52,6 +51,7 @@ set(COMMON_UI_SRC tap-tcp-stream.c text_import.c time_shift.c + traffic_table_ui.c util.c ) diff --git a/ui/Makefile.common b/ui/Makefile.common index e36980afc2..bcfb54e9f1 100644 --- a/ui/Makefile.common +++ b/ui/Makefile.common @@ -45,7 +45,6 @@ WIRESHARK_UI_SRC = \ alert_box.c \ capture.c \ capture_ui_utils.c \ - conversation_ui.c \ decode_as_utils.c \ export_object.c \ export_object_dicom.c \ @@ -73,6 +72,7 @@ WIRESHARK_UI_SRC = \ tap-tcp-stream.c \ text_import.c \ time_shift.c \ + traffic_table_ui.c \ util.c noinst_HEADERS = \ @@ -80,7 +80,6 @@ noinst_HEADERS = \ capture.h \ capture_globals.h \ capture_ui_utils.h \ - conversation_ui.h \ decode_as_utils.h \ export_object.h \ last_open_dir.h \ @@ -113,6 +112,7 @@ noinst_HEADERS = \ text_import.h \ text_import_scanner.h \ time_shift.h \ + traffic_table_ui.h \ ui_util.h \ utf8_entities.h \ util.h diff --git a/ui/cli/tap-iousers.c b/ui/cli/tap-iousers.c index 57afbcaeae..36669e47a6 100644 --- a/ui/cli/tap-iousers.c +++ b/ui/cli/tap-iousers.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include diff --git a/ui/gtk/conversations_table.c b/ui/gtk/conversations_table.c index ec7fb717a3..7e30c3c425 100644 --- a/ui/gtk/conversations_table.c +++ b/ui/gtk/conversations_table.c @@ -1926,12 +1926,12 @@ init_ct_table_page(conversations_table *conversations, GtkWidget *vbox, gboolean conversations->fixed_col = FALSE; for (i = 0; i < CONV_NUM_COLUMNS; i++) { - conversations->default_titles[i] = column_titles[i]; + conversations->default_titles[i] = conv_column_titles[i]; } if (strcmp(table_name, "NCP")==0) { - conversations->default_titles[CONV_COLUMN_SRC_PORT] = conn_a_title; - conversations->default_titles[CONV_COLUMN_DST_PORT] = conn_b_title; + conversations->default_titles[CONV_COLUMN_SRC_PORT] = conv_conn_a_title; + conversations->default_titles[CONV_COLUMN_DST_PORT] = conv_conn_b_title; } g_snprintf(title, sizeof(title), "%s Conversations", table_name); diff --git a/ui/gtk/conversations_table.h b/ui/gtk/conversations_table.h index d1e2ec3fe9..28dbd60a9b 100644 --- a/ui/gtk/conversations_table.h +++ b/ui/gtk/conversations_table.h @@ -26,7 +26,7 @@ #include #include -#include +#include /** @file * Conversation definitions. diff --git a/ui/gtk/hostlist_table.c b/ui/gtk/hostlist_table.c index b8dfa4424e..171c371769 100644 --- a/ui/gtk/hostlist_table.c +++ b/ui/gtk/hostlist_table.c @@ -167,8 +167,8 @@ hostlist_sort_column(GtkTreeModel *model, hostlist_talker_t *host1 = NULL; hostlist_talker_t *host2 = NULL; - gtk_tree_model_get(model, a, HOST_INDEX_COLUMN, &idx1, -1); - gtk_tree_model_get(model, b, HOST_INDEX_COLUMN, &idx2, -1); + gtk_tree_model_get(model, a, ENDP_INDEX_COLUMN, &idx1, -1); + gtk_tree_model_get(model, b, ENDP_INDEX_COLUMN, &idx2, -1); if (!hl || idx1 >= hl->hash.conv_array->len || idx2 >= hl->hash.conv_array->len) return 0; @@ -177,9 +177,9 @@ hostlist_sort_column(GtkTreeModel *model, host2 = &g_array_index(hl->hash.conv_array, hostlist_talker_t, idx2); switch(data_column){ - case HOST_ADR_COLUMN: /* Address */ + case ENDP_COLUMN_ADDR: /* Address */ return(CMP_ADDRESS(&host1->myaddress, &host2->myaddress)); - case HOST_PORT_COLUMN: /* (Port) */ + case ENDP_COLUMN_PORT: /* (Port) */ CMP_INT(host1->port, host2->port); #ifdef HAVE_GEOIP default: @@ -224,7 +224,7 @@ hostlist_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint c return; gtk_tree_model_get (model, &iter, - HOST_INDEX_COLUMN, &idx, + ENDP_INDEX_COLUMN, &idx, -1); if(idx>= hl->hash.conv_array->len){ @@ -486,12 +486,12 @@ draw_hostlist_table_addresses(hostlist_table *hl) while (iter_valid) { hostlist_talker_t *host; - gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, HOST_INDEX_COLUMN, &idx, -1); + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, ENDP_INDEX_COLUMN, &idx, -1); host = &g_array_index(hl->hash.conv_array, hostlist_talker_t, idx); gtk_list_store_set (store, &iter, - HOST_ADR_COLUMN, get_conversation_address(&host->myaddress, hl->resolve_names), - HOST_PORT_COLUMN, get_conversation_port(host->port, host->ptype, hl->resolve_names), + ENDP_COLUMN_ADDR, get_conversation_address(&host->myaddress, hl->resolve_names), + ENDP_COLUMN_PORT, get_conversation_port(host->port, host->ptype, hl->resolve_names), -1); iter_valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter); @@ -536,7 +536,7 @@ draw_hostlist_table_data(hostlist_table *hl) hostlist_talker_t *host; if (iter_valid) { - gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, HOST_INDEX_COLUMN, &idx, -1); + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, ENDP_INDEX_COLUMN, &idx, -1); } else { idx = new_idx; new_idx++; @@ -557,7 +557,7 @@ draw_hostlist_table_data(hostlist_table *hl) host->modified = FALSE; if (!iter_valid) { #ifdef HAVE_GEOIP - char *geoip[NUM_GEOIP_COLS]; + char *geoip[ENDP_NUM_GEOIP_COLUMNS]; guint j; if ((host->myaddress.type == AT_IPv4 || host->myaddress.type == AT_IPv6) && !hl->geoip_visible) { @@ -572,7 +572,7 @@ draw_hostlist_table_data(hostlist_table *hl) column = (GtkTreeViewColumn *)columns->data; title_p = gtk_tree_view_column_get_title(column); id = gtk_tree_view_column_get_sort_column_id(column); - if (title_p[0] != 0 && id >= HOST_GEOIP1_COLUMN) { + if (title_p[0] != 0 && id >= ENDP_COLUMN_GEOIP1) { gtk_tree_view_column_set_visible(column, TRUE); } columns = g_list_next(columns); @@ -582,7 +582,7 @@ draw_hostlist_table_data(hostlist_table *hl) } /* Filled in from the GeoIP config, if any */ - for (j = 0; j < NUM_GEOIP_COLS; j++) { + for (j = 0; j < ENDP_NUM_GEOIP_COLUMNS; j++) { if (host->myaddress.type == AT_IPv4 && j < geoip_db_num_dbs()) { const guchar *name = geoip_db_lookup_ipv4(j, pntoh32(host->myaddress.data), "-"); geoip[j] = g_strdup(name); @@ -599,45 +599,45 @@ draw_hostlist_table_data(hostlist_table *hl) #endif /* HAVE_GEOIP */ gtk_list_store_insert_with_values( store, &iter, G_MAXINT, - HOST_ADR_COLUMN, get_conversation_address(&host->myaddress, hl->resolve_names), - HOST_PORT_COLUMN, get_conversation_port(host->port, host->ptype, hl->resolve_names), - HOST_PACKETS_COLUMN, host->tx_frames+host->rx_frames, - HOST_BYTES_COLUMN, host->tx_bytes+host->rx_bytes, - HOST_PKT_AB_COLUMN, host->tx_frames, - HOST_BYTES_AB_COLUMN, host->tx_bytes, - HOST_PKT_BA_COLUMN, host->rx_frames, - HOST_BYTES_BA_COLUMN, host->rx_bytes, + ENDP_COLUMN_ADDR, get_conversation_address(&host->myaddress, hl->resolve_names), + ENDP_COLUMN_PORT, get_conversation_port(host->port, host->ptype, hl->resolve_names), + ENDP_COLUMN_PACKETS, host->tx_frames+host->rx_frames, + ENDP_COLUMN_BYTES, host->tx_bytes+host->rx_bytes, + ENDP_COLUMN_PKT_AB, host->tx_frames, + ENDP_COLUMN_BYTES_AB, host->tx_bytes, + ENDP_COLUMN_PKT_BA, host->rx_frames, + ENDP_COLUMN_BYTES_BA, host->rx_bytes, #ifdef HAVE_GEOIP - HOST_GEOIP1_COLUMN, geoip[0], - HOST_GEOIP2_COLUMN, geoip[1], - HOST_GEOIP3_COLUMN, geoip[2], - HOST_GEOIP4_COLUMN, geoip[3], - HOST_GEOIP5_COLUMN, geoip[4], - HOST_GEOIP6_COLUMN, geoip[5], - HOST_GEOIP7_COLUMN, geoip[6], - HOST_GEOIP8_COLUMN, geoip[7], - HOST_GEOIP9_COLUMN, geoip[8], - HOST_GEOIP10_COLUMN, geoip[9], - HOST_GEOIP11_COLUMN, geoip[10], - HOST_GEOIP12_COLUMN, geoip[11], - HOST_GEOIP13_COLUMN, geoip[12], + ENDP_COLUMN_GEOIP1, geoip[0], + ENDP_COLUMN_GEOIP2, geoip[1], + ENDP_COLUMN_GEOIP3, geoip[2], + ENDP_COLUMN_GEOIP4, geoip[3], + ENDP_COLUMN_GEOIP5, geoip[4], + ENDP_COLUMN_GEOIP6, geoip[5], + ENDP_COLUMN_GEOIP7, geoip[6], + ENDP_COLUMN_GEOIP8, geoip[7], + ENDP_COLUMN_GEOIP9, geoip[8], + ENDP_COLUMN_GEOIP10, geoip[9], + ENDP_COLUMN_GEOIP11, geoip[10], + ENDP_COLUMN_GEOIP12, geoip[11], + ENDP_COLUMN_GEOIP13, geoip[12], #endif - HOST_INDEX_COLUMN, idx, + ENDP_INDEX_COLUMN, idx, -1); #ifdef HAVE_GEOIP - for (j = 0; j < NUM_GEOIP_COLS; j++) + for (j = 0; j < ENDP_NUM_GEOIP_COLUMNS; j++) g_free(geoip[j]); #endif /* HAVE_GEOIP */ } else { gtk_list_store_set (store, &iter, - HOST_PACKETS_COLUMN, host->tx_frames+host->rx_frames, - HOST_BYTES_COLUMN, host->tx_bytes+host->rx_bytes, - HOST_PKT_AB_COLUMN, host->tx_frames, - HOST_BYTES_AB_COLUMN, host->tx_bytes, - HOST_PKT_BA_COLUMN, host->rx_frames, - HOST_BYTES_BA_COLUMN, host->rx_bytes, + ENDP_COLUMN_PACKETS, host->tx_frames+host->rx_frames, + ENDP_COLUMN_BYTES, host->tx_bytes+host->rx_bytes, + ENDP_COLUMN_PKT_AB, host->tx_frames, + ENDP_COLUMN_BYTES_AB, host->tx_bytes, + ENDP_COLUMN_PKT_BA, host->rx_frames, + ENDP_COLUMN_BYTES_BA, host->rx_bytes, -1); } @@ -668,7 +668,7 @@ draw_hostlist_table_data_cb(void *arg) typedef struct { int nb_cols; - gint columns_order[HOST_NUM_COLUMNS]; + gint columns_order[ENDP_NUM_COLUMNS+ENDP_NUM_GEOIP_COLUMNS]; GString *CSV_str; hostlist_table *talkers; } csv_t; @@ -684,27 +684,27 @@ csv_handle(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter, guint idx; guint64 value; - gtk_tree_model_get(model, iter, HOST_INDEX_COLUMN, &idx, -1); + gtk_tree_model_get(model, iter, ENDP_INDEX_COLUMN, &idx, -1); for (i=0; i< csv->nb_cols; i++) { if (i) g_string_append(csv->CSV_str, ","); switch(csv->columns_order[i]) { - case HOST_ADR_COLUMN: - case HOST_PORT_COLUMN: + case ENDP_COLUMN_ADDR: + case ENDP_COLUMN_PORT: gtk_tree_model_get(model, iter, csv->columns_order[i], &table_text, -1); if (table_text) { g_string_append_printf(csv->CSV_str, "\"%s\"", table_text); g_free(table_text); } break; - case HOST_PACKETS_COLUMN: - case HOST_BYTES_COLUMN: - case HOST_PKT_AB_COLUMN: - case HOST_BYTES_AB_COLUMN: - case HOST_PKT_BA_COLUMN: - case HOST_BYTES_BA_COLUMN: + case ENDP_COLUMN_PACKETS: + case ENDP_COLUMN_BYTES: + case ENDP_COLUMN_PKT_AB: + case ENDP_COLUMN_BYTES_AB: + case ENDP_COLUMN_PKT_BA: + case ENDP_COLUMN_BYTES_BA: gtk_tree_model_get(model, iter, csv->columns_order[i], &value, -1); g_string_append_printf(csv->CSV_str, "\"%" G_GINT64_MODIFIER "u\"", value); break; @@ -1023,34 +1023,34 @@ open_as_map_cb(GtkWindow *copy_bt, gpointer data _U_) } #endif /* HAVE_GEOIP */ -static gint default_col_size[HOST_NUM_COLUMNS]; +static gint default_col_size[ENDP_NUM_COLUMNS+ENDP_NUM_GEOIP_COLUMNS]; static void init_default_col_size(GtkWidget *view) { - default_col_size[HOST_ADR_COLUMN] = get_default_col_size(view, "00000000.000000000000"); - default_col_size[HOST_PORT_COLUMN] = get_default_col_size(view, "000000"); - default_col_size[HOST_PACKETS_COLUMN] = get_default_col_size(view, "00 000 000"); - default_col_size[HOST_BYTES_COLUMN] = get_default_col_size(view, "0 000 000 000"); - default_col_size[HOST_PKT_AB_COLUMN] = default_col_size[HOST_PACKETS_COLUMN]; - default_col_size[HOST_PKT_BA_COLUMN] = default_col_size[HOST_PACKETS_COLUMN]; - default_col_size[HOST_BYTES_AB_COLUMN] = default_col_size[HOST_BYTES_COLUMN]; - default_col_size[HOST_BYTES_BA_COLUMN] = default_col_size[HOST_BYTES_COLUMN]; + default_col_size[ENDP_COLUMN_ADDR] = get_default_col_size(view, "00000000.000000000000"); + default_col_size[ENDP_COLUMN_PORT] = get_default_col_size(view, "000000"); + default_col_size[ENDP_COLUMN_PACKETS] = get_default_col_size(view, "00 000 000"); + default_col_size[ENDP_COLUMN_BYTES] = get_default_col_size(view, "0 000 000 000"); + default_col_size[ENDP_COLUMN_PKT_AB] = default_col_size[ENDP_COLUMN_PACKETS]; + default_col_size[ENDP_COLUMN_PKT_BA] = default_col_size[ENDP_COLUMN_PACKETS]; + default_col_size[ENDP_COLUMN_BYTES_AB] = default_col_size[ENDP_COLUMN_BYTES]; + default_col_size[ENDP_COLUMN_BYTES_BA] = default_col_size[ENDP_COLUMN_BYTES]; #ifdef HAVE_GEOIP - default_col_size[HOST_GEOIP1_COLUMN] = default_col_size[HOST_ADR_COLUMN]; - default_col_size[HOST_GEOIP2_COLUMN] = default_col_size[HOST_GEOIP1_COLUMN]; - default_col_size[HOST_GEOIP3_COLUMN] = default_col_size[HOST_GEOIP1_COLUMN]; - default_col_size[HOST_GEOIP4_COLUMN] = default_col_size[HOST_GEOIP1_COLUMN]; - default_col_size[HOST_GEOIP5_COLUMN] = default_col_size[HOST_GEOIP1_COLUMN]; - default_col_size[HOST_GEOIP6_COLUMN] = default_col_size[HOST_GEOIP1_COLUMN]; - default_col_size[HOST_GEOIP7_COLUMN] = default_col_size[HOST_GEOIP1_COLUMN]; - default_col_size[HOST_GEOIP8_COLUMN] = default_col_size[HOST_GEOIP1_COLUMN]; - default_col_size[HOST_GEOIP9_COLUMN] = default_col_size[HOST_GEOIP1_COLUMN]; - default_col_size[HOST_GEOIP10_COLUMN] = default_col_size[HOST_GEOIP1_COLUMN]; - default_col_size[HOST_GEOIP11_COLUMN] = default_col_size[HOST_GEOIP1_COLUMN]; - default_col_size[HOST_GEOIP12_COLUMN] = default_col_size[HOST_GEOIP1_COLUMN]; - default_col_size[HOST_GEOIP13_COLUMN] = default_col_size[HOST_GEOIP1_COLUMN]; + default_col_size[ENDP_COLUMN_GEOIP1] = default_col_size[ENDP_COLUMN_ADDR]; + default_col_size[ENDP_COLUMN_GEOIP2] = default_col_size[ENDP_COLUMN_GEOIP1]; + default_col_size[ENDP_COLUMN_GEOIP3] = default_col_size[ENDP_COLUMN_GEOIP1]; + default_col_size[ENDP_COLUMN_GEOIP4] = default_col_size[ENDP_COLUMN_GEOIP1]; + default_col_size[ENDP_COLUMN_GEOIP5] = default_col_size[ENDP_COLUMN_GEOIP1]; + default_col_size[ENDP_COLUMN_GEOIP6] = default_col_size[ENDP_COLUMN_GEOIP1]; + default_col_size[ENDP_COLUMN_GEOIP7] = default_col_size[ENDP_COLUMN_GEOIP1]; + default_col_size[ENDP_COLUMN_GEOIP8] = default_col_size[ENDP_COLUMN_GEOIP1]; + default_col_size[ENDP_COLUMN_GEOIP9] = default_col_size[ENDP_COLUMN_GEOIP1]; + default_col_size[ENDP_COLUMN_GEOIP10] = default_col_size[ENDP_COLUMN_GEOIP1]; + default_col_size[ENDP_COLUMN_GEOIP11] = default_col_size[ENDP_COLUMN_GEOIP1]; + default_col_size[ENDP_COLUMN_GEOIP12] = default_col_size[ENDP_COLUMN_GEOIP1]; + default_col_size[ENDP_COLUMN_GEOIP13] = default_col_size[ENDP_COLUMN_GEOIP1]; #endif } @@ -1080,17 +1080,17 @@ init_hostlist_table_page(hostlist_table *hosttable, GtkWidget *vbox, gboolean hi hosttable->default_titles[7] = "Rx Bytes"; #ifdef HAVE_GEOIP - for (i = 0; i < NUM_GEOIP_COLS; i++) { + for (i = 0; i < ENDP_NUM_GEOIP_COLUMNS; i++) { if (i < geoip_db_num_dbs()) { - hosttable->default_titles[NUM_BUILTIN_COLS + i] = geoip_db_name(i); + hosttable->default_titles[ENDP_NUM_COLUMNS + i] = geoip_db_name(i); } else { - hosttable->default_titles[NUM_BUILTIN_COLS + i] = ""; + hosttable->default_titles[ENDP_NUM_COLUMNS + i] = ""; } } #endif /* HAVE_GEOIP */ if (strcmp(table_name, "NCP")==0) { - hosttable->default_titles[1] = "Connection"; + hosttable->default_titles[1] = endp_conn_title; } hosttable->has_ports=!hide_ports; @@ -1104,7 +1104,8 @@ init_hostlist_table_page(hostlist_table *hosttable, GtkWidget *vbox, gboolean hi gtk_box_pack_start(GTK_BOX(vbox), hosttable->name_lb, FALSE, FALSE, 0); /* Create the store */ - store = gtk_list_store_new (HOST_NUM_COLUMNS + 1, /* Total number of columns */ + + store = gtk_list_store_new (ENDP_INDEX_COLUMN + 1, /* Total number of columns */ G_TYPE_STRING, /* Address */ G_TYPE_STRING, /* Port */ G_TYPE_UINT64, /* Packets */ @@ -1146,26 +1147,26 @@ init_hostlist_table_page(hostlist_table *hosttable, GtkWidget *vbox, gboolean hi g_object_set_data(G_OBJECT(store), HOST_PTR_KEY, hosttable); g_object_set_data(G_OBJECT(hosttable->table), HOST_PTR_KEY, hosttable); - for (i = 0; i < HOST_NUM_COLUMNS; i++) { + for (i = 0; i < ENDP_NUM_COLUMNS+ENDP_NUM_GEOIP_COLUMNS; i++) { renderer = gtk_cell_renderer_text_new (); g_object_set(renderer, "ypad", 0, NULL); switch(i) { - case HOST_ADR_COLUMN: /* address and port */ - case HOST_PORT_COLUMN: + case ENDP_COLUMN_ADDR: /* address and port */ + case ENDP_COLUMN_PORT: column = gtk_tree_view_column_new_with_attributes (hosttable->default_titles[i], renderer, "text", i, NULL); - if(hide_ports && i == HOST_PORT_COLUMN){ + if(hide_ports && i == ENDP_COLUMN_PORT){ /* hide srcport and dstport if we don't use ports */ gtk_tree_view_column_set_visible(column, FALSE); } gtk_tree_sortable_set_sort_func(sortable, i, hostlist_sort_column, GINT_TO_POINTER(i), NULL); break; - case HOST_PACKETS_COLUMN: /* counts */ - case HOST_BYTES_COLUMN: - case HOST_PKT_AB_COLUMN: - case HOST_BYTES_AB_COLUMN: - case HOST_PKT_BA_COLUMN: - case HOST_BYTES_BA_COLUMN: /* right align numbers */ + case ENDP_COLUMN_PACKETS: /* counts */ + case ENDP_COLUMN_BYTES: + case ENDP_COLUMN_PKT_AB: + case ENDP_COLUMN_BYTES_AB: + case ENDP_COLUMN_PKT_BA: + case ENDP_COLUMN_BYTES_BA: /* right align numbers */ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL); column = gtk_tree_view_column_new_with_attributes (hosttable->default_titles[i], renderer, NULL); gtk_tree_view_column_set_cell_data_func(column, renderer, u64_data_func, GINT_TO_POINTER(i), NULL); @@ -1175,8 +1176,8 @@ init_hostlist_table_page(hostlist_table *hosttable, GtkWidget *vbox, gboolean hi i, NULL); gtk_tree_view_column_set_visible(column, FALSE); #ifdef HAVE_GEOIP - if (i >= NUM_BUILTIN_COLS && i - NUM_BUILTIN_COLS < geoip_db_num_dbs()) { - int goip_type = geoip_db_type(i - NUM_BUILTIN_COLS); + if (i >= ENDP_NUM_COLUMNS && i - ENDP_NUM_COLUMNS < geoip_db_num_dbs()) { + int goip_type = geoip_db_type(i - ENDP_NUM_COLUMNS); if (goip_type == WS_LON_FAKE_EDITION || goip_type == WS_LAT_FAKE_EDITION) { g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL); gtk_tree_sortable_set_sort_func(sortable, i, hostlist_sort_column, GINT_TO_POINTER(i), NULL); diff --git a/ui/gtk/hostlist_table.h b/ui/gtk/hostlist_table.h index e7742b8be1..78c71b7fbc 100644 --- a/ui/gtk/hostlist_table.h +++ b/ui/gtk/hostlist_table.h @@ -25,49 +25,33 @@ #define __HOSTLIST_TABLE_H__ #include +#include /** @file * Hostlist definitions. */ -typedef enum -{ - HOST_ADR_COLUMN, - HOST_PORT_COLUMN, - HOST_PACKETS_COLUMN, - HOST_BYTES_COLUMN, - HOST_PKT_AB_COLUMN, - HOST_BYTES_AB_COLUMN, - HOST_PKT_BA_COLUMN, - HOST_BYTES_BA_COLUMN, #ifdef HAVE_GEOIP - HOST_GEOIP1_COLUMN, - HOST_GEOIP2_COLUMN, - HOST_GEOIP3_COLUMN, - HOST_GEOIP4_COLUMN, - HOST_GEOIP5_COLUMN, - HOST_GEOIP6_COLUMN, - HOST_GEOIP7_COLUMN, - HOST_GEOIP8_COLUMN, - HOST_GEOIP9_COLUMN, - HOST_GEOIP10_COLUMN, - HOST_GEOIP11_COLUMN, - HOST_GEOIP12_COLUMN, - HOST_GEOIP13_COLUMN, -#endif - HOST_NUM_COLUMNS, - HOST_INDEX_COLUMN = HOST_NUM_COLUMNS -} hostlist_column_type_e; - - - -#define NUM_BUILTIN_COLS 8 -#ifdef HAVE_GEOIP -# define NUM_GEOIP_COLS 13 +typedef enum { + ENDP_COLUMN_GEOIP1 = ENDP_NUM_COLUMNS, + ENDP_COLUMN_GEOIP2, + ENDP_COLUMN_GEOIP3, + ENDP_COLUMN_GEOIP4, + ENDP_COLUMN_GEOIP5, + ENDP_COLUMN_GEOIP6, + ENDP_COLUMN_GEOIP7, + ENDP_COLUMN_GEOIP8, + ENDP_COLUMN_GEOIP9, + ENDP_COLUMN_GEOIP10, + ENDP_COLUMN_GEOIP11, + ENDP_COLUMN_GEOIP12, + ENDP_COLUMN_GEOIP13, +} geoip_column_type_e; +#define ENDP_NUM_GEOIP_COLUMNS 13 #else -# define NUM_GEOIP_COLS 0 +#define ENDP_NUM_GEOIP_COLUMNS 0 #endif -#define NUM_HOSTLIST_COLS (NUM_BUILTIN_COLS + NUM_GEOIP_COLS) +#define ENDP_INDEX_COLUMN (ENDP_NUM_COLUMNS+ENDP_NUM_GEOIP_COLUMNS) /** Hostlist widget */ typedef struct _hostlist_table { @@ -79,7 +63,7 @@ typedef struct _hostlist_table { GtkWidget *name_lb; /**< name label */ GtkWidget *scrolled_window; /**< the scrolled window */ GtkTreeView *table; /**< the GTK table */ - const char *default_titles[NUM_HOSTLIST_COLS]; /**< Column headers */ + const char *default_titles[ENDP_NUM_COLUMNS+ENDP_NUM_GEOIP_COLUMNS]; /**< Column headers */ GtkWidget *menu; /**< context menu */ gboolean has_ports; /**< table has ports */ conv_hash_t hash; /**< hostlist hash table */ diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt index ef24563d23..cbd9a63118 100644 --- a/ui/qt/CMakeLists.txt +++ b/ui/qt/CMakeLists.txt @@ -38,11 +38,11 @@ set(WIRESHARK_QT_HEADERS column_preferences_frame.h compiled_filter_output.h conversation_dialog.h - conversation_tree_widget.h decode_as_dialog.h display_filter_combo.h display_filter_edit.h elided_label.h + endpoint_dialog.h export_dissection_dialog.h export_object_dialog.h export_pdu_dialog.h @@ -97,6 +97,7 @@ set(WIRESHARK_QT_HEADERS syntax_line_edit.h tcp_stream_dialog.h time_shift_dialog.h + traffic_table_dialog.h uat_dialog.h wireshark_application.h ) @@ -124,7 +125,6 @@ set(WIRESHARK_QT_SRC capture_preferences_frame.cpp column_preferences_frame.cpp compiled_filter_output.cpp - conversation_tree_widget.cpp decode_as_dialog.cpp display_filter_combo.cpp display_filter_edit.cpp @@ -186,12 +186,14 @@ set(WIRESHARK_QT_SRC syntax_line_edit.cpp tcp_stream_dialog.cpp time_shift_dialog.cpp + traffic_table_dialog.cpp uat_dialog.cpp wireshark_application.cpp ) set(WIRESHARK_QT_TAP_SRC conversation_dialog.cpp + endpoint_dialog.cpp io_graph_dialog.cpp stats_tree_dialog.cpp ) @@ -211,7 +213,6 @@ set(WIRESHARK_QT_UI capture_interfaces_dialog.ui column_preferences_frame.ui compiled_filter_output.ui - conversation_dialog.ui decode_as_dialog.ui export_object_dialog.ui export_pdu_dialog.ui @@ -252,6 +253,7 @@ set(WIRESHARK_QT_UI summary_dialog.ui tcp_stream_dialog.ui time_shift_dialog.ui + traffic_table_dialog.ui uat_dialog.ui ) diff --git a/ui/qt/Makefile.am b/ui/qt/Makefile.am index 668bcb3e67..b325a31fcc 100644 --- a/ui/qt/Makefile.am +++ b/ui/qt/Makefile.am @@ -130,8 +130,6 @@ column_preferences_frame.cpp column_preferences_frame.h: ui_column_preferences_f compiled_filter_output.cpp compiled_filter_output.h: ui_compiled_filter_output.h -conversation_dialog.cpp conversation_dialog.h: ui_conversation_dialog.h - decode_as_dialog.cpp decode_as_dialog.h: ui_decode_as_dialog.h export_object_dialog.cpp export_object_dialog.h: ui_export_object_dialog.h @@ -212,6 +210,8 @@ tcp_stream_dialog.cpp: ui_tcp_stream_dialog.h time_shift_dialog.cpp time_shift_dialog.h: ui_time_shift_dialog.h +traffic_table_dialog.cpp traffic_table_dialog.h: ui_traffic_table_dialog.h + uat_dialog.cpp uat_dialog.h: ui_uat_dialog.h doxygen: diff --git a/ui/qt/Makefile.common b/ui/qt/Makefile.common index d5a3bfc712..dd34ebc240 100644 --- a/ui/qt/Makefile.common +++ b/ui/qt/Makefile.common @@ -34,7 +34,6 @@ NODIST_GENERATED_HEADER_FILES = \ ui_capture_preferences_frame.h \ ui_column_preferences_frame.h \ ui_compiled_filter_output.h \ - ui_conversation_dialog.h \ ui_decode_as_dialog.h \ ui_export_object_dialog.h \ ui_export_pdu_dialog.h \ @@ -75,6 +74,7 @@ NODIST_GENERATED_HEADER_FILES = \ ui_summary_dialog.h \ ui_tcp_stream_dialog.h \ ui_time_shift_dialog.h \ + ui_traffic_table_dialog.h \ ui_uat_dialog.h # Generated C source files that we want in the distribution. @@ -121,17 +121,17 @@ MOC_HDRS = \ capture_filter_syntax_worker.h \ capture_info_dialog.h \ capture_interfaces_dialog.h \ + capture_preferences_frame.h \ color_dialog.h \ color_utils.h \ - capture_preferences_frame.h \ column_preferences_frame.h \ compiled_filter_output.h \ conversation_dialog.h \ - conversation_tree_widget.h \ decode_as_dialog.h \ display_filter_combo.h \ display_filter_edit.h \ elided_label.h \ + endpoint_dialog.h \ export_dissection_dialog.h \ export_object_dialog.h \ export_pdu_dialog.h \ @@ -188,6 +188,7 @@ MOC_HDRS = \ syntax_line_edit.h \ tcp_stream_dialog.h \ time_shift_dialog.h \ + traffic_table_dialog.h \ uat_dialog.h \ wireshark_application.h @@ -201,7 +202,6 @@ UI_FILES = \ capture_preferences_frame.ui \ column_preferences_frame.ui \ compiled_filter_output.ui \ - conversation_dialog.ui \ decode_as_dialog.ui \ export_object_dialog.ui \ export_pdu_dialog.ui \ @@ -242,6 +242,7 @@ UI_FILES = \ summary_dialog.ui \ tcp_stream_dialog.ui \ time_shift_dialog.ui \ + traffic_table_dialog.ui \ uat_dialog.ui # @@ -308,17 +309,17 @@ WIRESHARK_QT_SRC = \ capture_filter_syntax_worker.cpp \ capture_info_dialog.cpp \ capture_interfaces_dialog.cpp \ + capture_preferences_frame.cpp \ color_dialog.cpp \ color_utils.cpp \ - capture_preferences_frame.cpp \ column_preferences_frame.cpp \ compiled_filter_output.cpp \ conversation_dialog.cpp \ - conversation_tree_widget.cpp \ decode_as_dialog.cpp \ display_filter_combo.cpp \ display_filter_edit.cpp \ elided_label.cpp \ + endpoint_dialog.cpp \ export_dissection_dialog.cpp \ export_object_dialog.cpp \ export_pdu_dialog.cpp \ @@ -379,6 +380,7 @@ WIRESHARK_QT_SRC = \ syntax_line_edit.cpp \ tcp_stream_dialog.cpp \ time_shift_dialog.cpp \ + traffic_table_dialog.cpp \ uat_dialog.cpp \ wireshark_application.cpp diff --git a/ui/qt/Wireshark.pro b/ui/qt/Wireshark.pro index 5e626c822e..2e01b2db3e 100644 --- a/ui/qt/Wireshark.pro +++ b/ui/qt/Wireshark.pro @@ -210,7 +210,6 @@ FORMS += \ capture_interfaces_dialog.ui \ column_preferences_frame.ui \ compiled_filter_output.ui \ - conversation_dialog.ui \ decode_as_dialog.ui \ export_object_dialog.ui \ export_pdu_dialog.ui \ @@ -251,6 +250,7 @@ FORMS += \ summary_dialog.ui \ tcp_stream_dialog.ui \ time_shift_dialog.ui \ + traffic_table_dialog.ui \ uat_dialog.ui @@ -262,9 +262,9 @@ HEADERS += $$HEADERS_WS_C \ column_preferences_frame.h \ compiled_filter_output.h \ conversation_dialog.h \ - conversation_tree_widget.h \ decode_as_dialog.h \ elided_label.h \ + endpoint_dialog.h \ export_dissection_dialog.h \ export_object_dialog.h \ export_pdu_dialog.h \ @@ -300,6 +300,7 @@ HEADERS += $$HEADERS_WS_C \ summary_dialog.h \ tango_colors.h \ tcp_stream_dialog.h \ + traffic_table_dialog.h \ uat_dialog.h win32 { @@ -595,11 +596,11 @@ SOURCES += \ column_preferences_frame.cpp \ compiled_filter_output.cpp \ conversation_dialog.cpp \ - conversation_tree_widget.cpp \ decode_as_dialog.cpp \ display_filter_combo.cpp \ display_filter_edit.cpp \ elided_label.cpp \ + endpoint_dialog.cpp \ export_dissection_dialog.cpp \ export_object_dialog.cpp \ export_pdu_dialog.cpp \ @@ -660,5 +661,6 @@ SOURCES += \ syntax_line_edit.cpp \ tcp_stream_dialog.cpp \ time_shift_dialog.cpp \ + traffic_table_dialog.cpp \ uat_dialog.cpp \ wireshark_application.cpp diff --git a/ui/qt/conversation_dialog.cpp b/ui/qt/conversation_dialog.cpp index bb76c8793f..898a02c657 100644 --- a/ui/qt/conversation_dialog.cpp +++ b/ui/qt/conversation_dialog.cpp @@ -20,30 +20,21 @@ */ #include "conversation_dialog.h" -#include "ui_conversation_dialog.h" - -#include -#include -#include #include #include "ui/recent.h" #include "ui/tap-tcp-stream.h" +#include "ui/traffic_table_ui.h" + +#include "wsutil/str_util.h" + +#include "qt_ui_utils.h" #include "wireshark_application.h" -#include -#include -#include -#include -#include -#include #include #include -#include -#include -#include // To do: // - https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=6727 @@ -65,36 +56,15 @@ // - Friendly unit displays https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9231 // - Misleading bps calculation https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=8703 -ConversationDialog::ConversationDialog(QWidget *parent, capture_file *cf, int proto_id, const char *filter) : - QDialog(parent), - ui(new Ui::ConversationDialog), - cap_file_(cf), - filter_(filter) +const QString table_name_ = QObject::tr("Conversation"); +ConversationDialog::ConversationDialog(QWidget *parent, capture_file *cf, int cli_proto_id, const char *filter) : + TrafficTableDialog(parent, cf, filter, table_name_) { - ui->setupUi(this); - setAttribute(Qt::WA_DeleteOnClose, true); - - // XXX Use recent settings instead - if (parent) { - resize(parent->width(), parent->height() * 3 / 4); - } - - QMenu *copy_menu = new QMenu(); - QAction *ca; - copy_bt_ = ui->buttonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole); - ca = copy_menu->addAction(tr("as CSV")); - ca->setToolTip(tr("Copy all values of this page to the clipboard in CSV (Comma Separated Values) format.")); - connect(ca, SIGNAL(triggered()), this, SLOT(copyAsCsv())); - ca = copy_menu->addAction(tr("as YAML")); - ca->setToolTip(tr("Copy all values of this page to the clipboard in the YAML data serialization format.")); - connect(ca, SIGNAL(triggered()), this, SLOT(copyAsYaml())); - copy_bt_->setMenu(copy_menu); - - follow_bt_ = ui->buttonBox->addButton(tr("Follow Stream..."), QDialogButtonBox::ActionRole); + follow_bt_ = buttonBox()->addButton(tr("Follow Stream..."), QDialogButtonBox::ActionRole); follow_bt_->setToolTip(tr("Follow a TCP or UDP stream.")); connect(follow_bt_, SIGNAL(clicked()), this, SLOT(followStream())); - graph_bt_ = ui->buttonBox->addButton(tr("Graph..."), QDialogButtonBox::ActionRole); + graph_bt_ = buttonBox()->addButton(tr("Graph..."), QDialogButtonBox::ActionRole); graph_bt_->setToolTip(tr("Graph a TCP conversation.")); connect(graph_bt_, SIGNAL(clicked()), this, SLOT(graphTcp())); @@ -106,51 +76,26 @@ ConversationDialog::ConversationDialog(QWidget *parent, capture_file *cf, int pr } } - // Reasonable defaults? if (conv_protos.isEmpty()) { - conv_protos << proto_get_id_by_filter_name( "tcp" ) << proto_get_id_by_filter_name( "eth" ) - << proto_get_id_by_filter_name( "ip" ) << proto_get_id_by_filter_name( "ipv6" ) - << proto_get_id_by_filter_name( "udp" ); + conv_protos = defaultProtos(); } // Bring the command-line specified type to the front. - if (get_conversation_by_proto_id(proto_id)) { - conv_protos.removeAll(proto_id); - conv_protos.prepend(proto_id); + if (get_conversation_by_proto_id(cli_proto_id)) { + conv_protos.removeAll(cli_proto_id); + conv_protos.prepend(cli_proto_id); } // QTabWidget selects the first item by default. foreach (int conv_proto, conv_protos) { - addConversationTable(get_conversation_by_proto_id(conv_proto)); + addTrafficTable(get_conversation_by_proto_id(conv_proto)); } - for (guint i = 0; i < conversation_table_get_num(); i++) { - int proto_id = get_conversation_proto_id(get_conversation_table_by_num(i)); - if (proto_id < 0) { - continue; - } - QString title = proto_get_protocol_short_name(find_protocol_by_id(proto_id)); - - QAction *conv_action = new QAction(title, this); - conv_action->setData(qVariantFromValue(proto_id)); - conv_action->setCheckable(true); - conv_action->setChecked(conv_protos.contains(proto_id)); - connect(conv_action, SIGNAL(triggered()), this, SLOT(toggleConversation())); - conv_type_menu_.addAction(conv_action); - } - - ui->conversationTypePushButton->setMenu(&conv_type_menu_); + fillTypeMenu(conv_protos); updateWidgets(); itemSelectionChanged(); - ui->nameResolutionCheckBox->setChecked(gbl_resolv_flags.network_name); - - ui->conversationTabWidget->currentWidget()->setFocus(); - - connect(ui->conversationTabWidget, SIGNAL(currentChanged(int)), - this, SLOT(itemSelectionChanged())); - if (cap_file_) { cf_retap_packets(cap_file_); } @@ -161,8 +106,8 @@ ConversationDialog::~ConversationDialog() prefs_clear_string_list(recent.conversation_tabs); recent.conversation_tabs = NULL; - ConversationTreeWidget *cur_tree = qobject_cast(ui->conversationTabWidget->currentWidget()); - foreach (QAction *ca, conv_type_menu_.actions()) { + ConversationTreeWidget *cur_tree = qobject_cast(trafficTableTabWidget()->currentWidget()); + foreach (QAction *ca, traffic_type_menu_.actions()) { int proto_id = ca->data().value(); if (proto_id_to_tree_.contains(proto_id) && ca->isChecked()) { char *title = g_strdup(proto_get_protocol_short_name(find_protocol_by_id(proto_id))); @@ -173,23 +118,26 @@ ConversationDialog::~ConversationDialog() } } } - delete ui; } void ConversationDialog::setCaptureFile(capture_file *cf) { if (!cf) { // We only want to know when the file closes. cap_file_ = NULL; - for (int i = 0; i < ui->conversationTabWidget->count(); i++) { - ConversationTreeWidget *cur_tree = qobject_cast(ui->conversationTabWidget->widget(i)); - remove_tap_listener(cur_tree->conversationHash()); + for (int i = 0; i < trafficTableTabWidget()->count(); i++) { + ConversationTreeWidget *cur_tree = qobject_cast(trafficTableTabWidget()->widget(i)); + remove_tap_listener(cur_tree->trafficTreeHash()); + disconnect(cur_tree, SIGNAL(filterAction(QString&,FilterAction::Action,FilterAction::ActionType)), + this, SIGNAL(filterAction(QString&,FilterAction::Action,FilterAction::ActionType))); } - ui->displayFilterCheckBox->setEnabled(false); - ui->conversationTypePushButton->setEnabled(false); + displayFilterCheckBox()->setEnabled(false); + enabledTypesPushButton()->setEnabled(false); + follow_bt_->setEnabled(false); + graph_bt_->setEnabled(false); } } -bool ConversationDialog::addConversationTable(register_ct_t* table) +bool ConversationDialog::addTrafficTable(register_ct_t* table) { int proto_id = get_conversation_proto_id(table); @@ -202,28 +150,28 @@ bool ConversationDialog::addConversationTable(register_ct_t* table) proto_id_to_tree_[proto_id] = conv_tree; const char* table_name = proto_get_protocol_short_name(find_protocol_by_id(proto_id)); - ui->conversationTabWidget->addTab(conv_tree, table_name); + trafficTableTabWidget()->addTab(conv_tree, table_name); connect(conv_tree, SIGNAL(itemSelectionChanged()), this, SLOT(itemSelectionChanged())); connect(conv_tree, SIGNAL(titleChanged(QWidget*,QString)), this, SLOT(setTabText(QWidget*,QString))); connect(conv_tree, SIGNAL(filterAction(QString&,FilterAction::Action,FilterAction::ActionType)), - this, SLOT(chainFilterAction(QString&,FilterAction::Action,FilterAction::ActionType))); - connect(ui->nameResolutionCheckBox, SIGNAL(toggled(bool)), + this, SIGNAL(filterAction(QString&,FilterAction::Action,FilterAction::ActionType))); + connect(nameResolutionCheckBox(), SIGNAL(toggled(bool)), conv_tree, SLOT(setNameResolutionEnabled(bool))); // XXX Move to ConversationTreeWidget ctor? const char *filter = NULL; - if (ui->displayFilterCheckBox->isChecked()) { + if (displayFilterCheckBox()->isChecked()) { filter = cap_file_->dfilter; } else if (!filter_.isEmpty()) { filter = filter_.toUtf8().constData(); } - conv_tree->conversationHash()->user_data = conv_tree; + conv_tree->trafficTreeHash()->user_data = conv_tree; - GString *error_string = register_tap_listener(proto_get_protocol_filter_name(proto_id), conv_tree->conversationHash(), filter, 0, + GString *error_string = register_tap_listener(proto_get_protocol_filter_name(proto_id), conv_tree->trafficTreeHash(), filter, 0, ConversationTreeWidget::tapReset, get_conversation_packet_func(table), ConversationTreeWidget::tapDraw); @@ -239,7 +187,7 @@ bool ConversationDialog::addConversationTable(register_ct_t* table) conv_item_t *ConversationDialog::currentConversation() { - ConversationTreeWidget *cur_tree = qobject_cast(ui->conversationTabWidget->currentWidget()); + ConversationTreeWidget *cur_tree = qobject_cast(trafficTableTabWidget()->currentWidget()); if (!cur_tree || cur_tree->selectedItems().count() < 1) { return NULL; @@ -250,6 +198,10 @@ conv_item_t *ConversationDialog::currentConversation() void ConversationDialog::followStream() { + if (!cap_file_) { + return; + } + conv_item_t *conv_item = currentConversation(); if (!conv_item) { return; @@ -273,56 +225,16 @@ void ConversationDialog::followStream() return; } - chainFilterAction(filter, FilterAction::ActionApply, FilterAction::ActionTypePlain); + emit filterAction(filter, FilterAction::ActionApply, FilterAction::ActionTypePlain); openFollowStreamDialog(ftype); } -void ConversationDialog::copyAsCsv() -{ - ConversationTreeWidget *cur_tree = qobject_cast(ui->conversationTabWidget->currentWidget()); - if (!cur_tree) { - return; - } - - QString csv; - QTextStream stream(&csv, QIODevice::Text); - for (int row = -1; row < cur_tree->topLevelItemCount(); row ++) { - QStringList rdsl; - foreach (QVariant v, cur_tree->rowData(row)) { - if (!v.isValid()) { - rdsl << "\"\""; - } else if ((int) v.type() == (int) QMetaType::QString) { - rdsl << QString("\"%1\"").arg(v.toString()); - } else { - rdsl << v.toString(); - } - } - stream << rdsl.join(",") << endl; - } - wsApp->clipboard()->setText(stream.readAll()); -} - -void ConversationDialog::copyAsYaml() -{ - ConversationTreeWidget *cur_tree = qobject_cast(ui->conversationTabWidget->currentWidget()); - if (!cur_tree) { - return; - } - - QString yaml; - QTextStream stream(&yaml, QIODevice::Text); - stream << "---" << endl; - for (int row = -1; row < cur_tree->topLevelItemCount(); row ++) { - stream << "-" << endl; - foreach (QVariant v, cur_tree->rowData(row)) { - stream << " - " << v.toString() << endl; - } - } - wsApp->clipboard()->setText(stream.readAll()); -} - void ConversationDialog::graphTcp() { + if (!cap_file_) { + return; + } + conv_item_t *conv_item = currentConversation(); if (!conv_item) { return; @@ -337,54 +249,13 @@ void ConversationDialog::graphTcp() return; } - chainFilterAction(filter, FilterAction::ActionApply, FilterAction::ActionTypePlain); + emit filterAction(filter, FilterAction::ActionApply, FilterAction::ActionTypePlain); openTcpStreamGraph(GRAPH_TSEQ_TCPTRACE); } -void ConversationDialog::updateWidgets() -{ - QWidget *cur_w = ui->conversationTabWidget->currentWidget(); - ui->conversationTabWidget->setUpdatesEnabled(false); - ui->conversationTabWidget->clear(); - foreach (QAction *ca, conv_type_menu_.actions()) { - int proto_id = ca->data().value(); - if (proto_id_to_tree_.contains(proto_id) && ca->isChecked()) { - ui->conversationTabWidget->addTab(proto_id_to_tree_[proto_id], - proto_id_to_tree_[proto_id]->conversationTitle()); - proto_id_to_tree_[proto_id]->setNameResolutionEnabled(ui->nameResolutionCheckBox->isChecked()); - } - } - ui->conversationTabWidget->setCurrentWidget(cur_w); - ui->conversationTabWidget->setUpdatesEnabled(true); -} - -void ConversationDialog::toggleConversation() -{ - QAction *ca = qobject_cast(QObject::sender()); - if (!ca) { - return; - } - - int proto_id = ca->data().value(); - register_ct_t* table = get_conversation_by_proto_id(proto_id); - - bool new_conv = addConversationTable(table); - updateWidgets(); - - if (ca->isChecked()) { - ui->conversationTabWidget->setCurrentWidget(proto_id_to_tree_[proto_id]); - } - - if (new_conv) { - if (cap_file_) { - cf_retap_packets(cap_file_); - } - } -} - void ConversationDialog::itemSelectionChanged() { - bool copy_enable = ui->conversationTabWidget->currentWidget() ? true : false; + bool copy_enable = trafficTableTabWidget()->currentWidget() ? true : false; bool follow_enable = false, graph_enable = false; conv_item_t *conv_item = currentConversation(); @@ -425,29 +296,13 @@ void ConversationDialog::on_displayFilterCheckBox_toggled(bool checked) filter = filter_.toUtf8().constData(); } - for (int i = 0; i < ui->conversationTabWidget->count(); i++) { - set_tap_dfilter(ui->conversationTabWidget->widget(i), filter); + for (int i = 0; i < trafficTableTabWidget()->count(); i++) { + set_tap_dfilter(trafficTableTabWidget()->widget(i), filter); } cf_retap_packets(cap_file_); } -void ConversationDialog::setTabText(QWidget *tree, const QString &text) -{ - // Could use QObject::sender as well - int index = ui->conversationTabWidget->indexOf(tree); - if (index >= 0) { - ui->conversationTabWidget->setTabText(index, text); - } -} - -void ConversationDialog::chainFilterAction(QString &filter, FilterAction::Action action, FilterAction::ActionType type) -{ - if (cap_file_) { // We probably shouldn't fail silently - emit filterAction(filter, action, type); - } -} - void ConversationDialog::on_buttonBox_helpRequested() { wsApp->helpTopicAction(HELP_STATS_CONVERSATIONS_DIALOG); @@ -456,7 +311,381 @@ void ConversationDialog::on_buttonBox_helpRequested() void init_conversation_table(struct register_ct* ct, const char *filter) { Q_UNUSED(ct) - wsApp->emitStatCommandSignal("Conversation", filter, GINT_TO_POINTER(get_conversation_proto_id(ct))); + wsApp->emitStatCommandSignal("Conversations", filter, GINT_TO_POINTER(get_conversation_proto_id(ct))); +} + + +// ConversationTreeWidgetItem +// TrafficTableTreeWidgetItem / QTreeWidgetItem subclass that allows sorting + +// Minimum bandwidth calculation duration +// https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=8703 +const double min_bw_calc_duration_ = 5 / 1000.0; // seconds +const QString bps_na_ = QObject::tr("N/A"); + +const int ci_col_ = 0; +const int pkts_col_ = 1; +class ConversationTreeWidgetItem : public TrafficTableTreeWidgetItem +{ +public: + ConversationTreeWidgetItem(QTreeWidget *tree) : TrafficTableTreeWidgetItem(tree) {} + ConversationTreeWidgetItem(QTreeWidget *parent, const QStringList &strings) + : TrafficTableTreeWidgetItem (parent, strings) {} + + // Set column text to its cooked representation. + void update(gboolean resolve_names) { + conv_item_t *conv_item = data(ci_col_, Qt::UserRole).value(); + bool ok; + quint64 cur_packets = data(pkts_col_, Qt::UserRole).toULongLong(&ok); + + if (!conv_item) { + return; + } + + quint64 packets = conv_item->tx_frames + conv_item->rx_frames; + if (ok && cur_packets == packets) { + return; + } + + setText(CONV_COLUMN_SRC_ADDR, get_conversation_address(&conv_item->src_address, resolve_names)); + setText(CONV_COLUMN_SRC_PORT, get_conversation_port(conv_item->src_port, conv_item->ptype, resolve_names)); + setText(CONV_COLUMN_DST_ADDR, get_conversation_address(&conv_item->dst_address, resolve_names)); + setText(CONV_COLUMN_DST_PORT, get_conversation_port(conv_item->dst_port, conv_item->ptype, resolve_names)); + + double duration = nstime_to_sec(&conv_item->stop_time) - nstime_to_sec(&conv_item->start_time); + QString col_str, bps_ab = bps_na_, bps_ba = bps_na_; + + col_str = QString("%L1").arg(packets); + setText(CONV_COLUMN_PACKETS, col_str); + col_str = gchar_free_to_qstring(format_size(conv_item->tx_bytes + conv_item->rx_bytes, format_size_unit_none|format_size_prefix_si)); + setText(CONV_COLUMN_BYTES, col_str); + col_str = QString("%L1").arg(conv_item->tx_frames); + setText(CONV_COLUMN_PKT_AB, QString::number(conv_item->tx_frames)); + col_str = gchar_free_to_qstring(format_size(conv_item->tx_bytes, format_size_unit_none|format_size_prefix_si)); + setText(CONV_COLUMN_BYTES_AB, col_str); + col_str = QString("%L1").arg(conv_item->rx_frames); + setText(CONV_COLUMN_PKT_BA, QString::number(conv_item->rx_frames)); + col_str = gchar_free_to_qstring(format_size(conv_item->rx_bytes, format_size_unit_none|format_size_prefix_si)); + setText(CONV_COLUMN_BYTES_BA, col_str); + setText(CONV_COLUMN_START, QString::number(nstime_to_sec(&conv_item->start_time), 'f', 9)); + setText(CONV_COLUMN_DURATION, QString::number(duration, 'f', 6)); + if (duration > min_bw_calc_duration_) { + bps_ab = gchar_free_to_qstring(format_size((gint64) conv_item->tx_bytes * 8 / duration, format_size_unit_none|format_size_prefix_si)); + bps_ba = gchar_free_to_qstring(format_size((gint64) conv_item->rx_bytes * 8 / duration, format_size_unit_none|format_size_prefix_si)); + } + setText(CONV_COLUMN_BPS_AB, bps_ab); + setText(CONV_COLUMN_BPS_BA, bps_ba); + setData(pkts_col_, Qt::UserRole, qVariantFromValue(packets)); + } + + // Return a string, qulonglong, double, or invalid QVariant representing the raw column data. + QVariant colData(int col, bool resolve_names) const { + conv_item_t *conv_item = data(ci_col_, Qt::UserRole).value(); + + if (!conv_item) { + return QVariant(); + } + + double duration = nstime_to_sec(&conv_item->stop_time) - nstime_to_sec(&conv_item->start_time); + double bps_ab = 0, bps_ba = 0; + if (duration > min_bw_calc_duration_) { + bps_ab = conv_item->tx_bytes * 8 / duration; + bps_ba = conv_item->rx_bytes * 8 / duration; + } + + switch (col) { + case CONV_COLUMN_SRC_ADDR: + return get_conversation_address(&conv_item->src_address, resolve_names); + case CONV_COLUMN_SRC_PORT: + if (resolve_names) { + return get_conversation_port(conv_item->src_port, conv_item->ptype, resolve_names); + } else { + return quint32(conv_item->src_port); + } + case CONV_COLUMN_DST_ADDR: + return get_conversation_address(&conv_item->dst_address, resolve_names); + case CONV_COLUMN_DST_PORT: + if (resolve_names) { + return get_conversation_port(conv_item->dst_port, conv_item->ptype, resolve_names); + } else { + return quint32(conv_item->dst_port); + } + case CONV_COLUMN_PACKETS: + return quint64(conv_item->tx_frames + conv_item->rx_frames); + case CONV_COLUMN_BYTES: + return quint64(conv_item->tx_bytes + conv_item->rx_bytes); + case CONV_COLUMN_PKT_AB: + return quint64(conv_item->tx_frames); + case CONV_COLUMN_BYTES_AB: + return quint64(conv_item->tx_bytes); + case CONV_COLUMN_PKT_BA: + return quint64(conv_item->rx_frames); + case CONV_COLUMN_BYTES_BA: + return quint64(conv_item->rx_bytes); + case CONV_COLUMN_START: + return nstime_to_sec(&conv_item->start_time); + case CONV_COLUMN_DURATION: + return duration; + case CONV_COLUMN_BPS_AB: + return bps_ab; + case CONV_COLUMN_BPS_BA: + return bps_ba; + default: + return QVariant(); + } + } + + bool operator< (const QTreeWidgetItem &other) const + { + conv_item_t *conv_item = data(ci_col_, Qt::UserRole).value(); + conv_item_t *other_item = other.data(ci_col_, Qt::UserRole).value(); + + if (!conv_item || !other_item) { + return false; + } + + int sort_col = treeWidget()->sortColumn(); + double conv_duration = nstime_to_sec(&conv_item->stop_time) - nstime_to_sec(&conv_item->start_time); + double other_duration = nstime_to_sec(&other_item->stop_time) - nstime_to_sec(&other_item->start_time); + + switch(sort_col) { + case CONV_COLUMN_SRC_ADDR: + return cmp_address(&conv_item->src_address, &other_item->src_address) < 0 ? true : false; + case CONV_COLUMN_SRC_PORT: + return conv_item->src_port < other_item->src_port; + case CONV_COLUMN_DST_ADDR: + return cmp_address(&conv_item->dst_address, &other_item->dst_address) < 0 ? true : false; + case CONV_COLUMN_DST_PORT: + return conv_item->dst_port < other_item->dst_port; + case CONV_COLUMN_PACKETS: + return (conv_item->tx_frames + conv_item->rx_frames) < (other_item->tx_frames + other_item->rx_frames); + case CONV_COLUMN_BYTES: + return (conv_item->tx_bytes + conv_item->rx_bytes) < (other_item->tx_bytes + other_item->rx_bytes); + case CONV_COLUMN_PKT_AB: + return conv_item->tx_frames < other_item->tx_frames; + case CONV_COLUMN_BYTES_AB: + return conv_item->tx_bytes < other_item->tx_bytes; + case CONV_COLUMN_PKT_BA: + return conv_item->rx_frames < other_item->rx_frames; + case CONV_COLUMN_BYTES_BA: + return conv_item->rx_bytes < other_item->rx_bytes; + case CONV_COLUMN_START: + return nstime_to_sec(&conv_item->start_time) < nstime_to_sec(&other_item->start_time); + case CONV_COLUMN_DURATION: + return conv_duration < other_duration; + case CONV_COLUMN_BPS_AB: + return conv_item->tx_bytes / conv_duration < other_item->tx_bytes / other_duration; + case CONV_COLUMN_BPS_BA: + return conv_item->rx_bytes / conv_duration < other_item->rx_bytes / other_duration; + default: + return false; + } + } +}; + +// ConversationTreeWidget +// TrafficTableTreeWidget / QTreeWidget subclass that allows tapping + +ConversationTreeWidget::ConversationTreeWidget(QWidget *parent, register_ct_t* table) : + TrafficTableTreeWidget(parent, table) +{ + setColumnCount(CONV_NUM_COLUMNS); + + for (int i = 0; i < CONV_NUM_COLUMNS; i++) { + headerItem()->setText(i, conv_column_titles[i]); + } + + if (get_conversation_hide_ports(table_)) { + hideColumn(CONV_COLUMN_SRC_PORT); + hideColumn(CONV_COLUMN_DST_PORT); + } else if (!strcmp(proto_get_protocol_filter_name(get_conversation_proto_id(table_)), "ncp")) { + headerItem()->setText(CONV_COLUMN_SRC_PORT, conv_conn_a_title); + headerItem()->setText(CONV_COLUMN_DST_PORT, conv_conn_b_title); + } + + int one_en = fontMetrics().height() / 2; + for (int i = 0; i < CONV_NUM_COLUMNS; i++) { + switch (i) { + case CONV_COLUMN_SRC_ADDR: + case CONV_COLUMN_DST_ADDR: + setColumnWidth(i, one_en * strlen("000.000.000.000")); + break; + case CONV_COLUMN_SRC_PORT: + case CONV_COLUMN_DST_PORT: + setColumnWidth(i, one_en * strlen("000000")); + break; + case CONV_COLUMN_PACKETS: + case CONV_COLUMN_PKT_AB: + case CONV_COLUMN_PKT_BA: + setColumnWidth(i, one_en * strlen("00,000")); + break; + case CONV_COLUMN_BYTES: + case CONV_COLUMN_BYTES_AB: + case CONV_COLUMN_BYTES_BA: + setColumnWidth(i, one_en * strlen("000,000")); + break; + case CONV_COLUMN_START: + setColumnWidth(i, one_en * strlen("00.000")); + break; + case CONV_COLUMN_DURATION: + setColumnWidth(i, one_en * strlen("00.000000")); + break; + case CONV_COLUMN_BPS_AB: + case CONV_COLUMN_BPS_BA: + setColumnWidth(i, one_en * strlen("000 k")); + break; + default: + setColumnWidth(i, one_en * 5); + } + } + + QMenu *submenu; + + initDirectionMap(); + + FilterAction::Action cur_action = FilterAction::ActionApply; + submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); + foreach (FilterAction::ActionType at, FilterAction::actionTypes()) { + QMenu *subsubmenu = submenu->addMenu(FilterAction::actionTypeName(at)); + foreach (FilterAction::ActionDirection ad, FilterAction::actionDirections()) { + FilterAction *fa = new FilterAction(subsubmenu, cur_action, at, ad); + subsubmenu->addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + } + } + + cur_action = FilterAction::ActionPrepare; + submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); + foreach (FilterAction::ActionType at, FilterAction::actionTypes()) { + QMenu *subsubmenu = submenu->addMenu(FilterAction::actionTypeName(at)); + foreach (FilterAction::ActionDirection ad, FilterAction::actionDirections()) { + FilterAction *fa = new FilterAction(subsubmenu, cur_action, at, ad); + subsubmenu->addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + } + } + + cur_action = FilterAction::ActionFind; + submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); + foreach (FilterAction::ActionDirection ad, FilterAction::actionDirections()) { + FilterAction *fa = new FilterAction(submenu, cur_action, FilterAction::ActionTypePlain, ad); + submenu->addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + } + + cur_action = FilterAction::ActionColorize; + submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); + foreach (FilterAction::ActionDirection ad, FilterAction::actionDirections()) { + FilterAction *fa = new FilterAction(submenu, cur_action, FilterAction::ActionTypePlain, ad); + submenu->addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + } + + updateItems(); +} + +ConversationTreeWidget::~ConversationTreeWidget() { + reset_conversation_table_data(&hash_); +} + +// Callbacks for register_tap_listener +void ConversationTreeWidget::tapReset(void *conv_hash_ptr) +{ + conv_hash_t *hash = (conv_hash_t*)conv_hash_ptr; + ConversationTreeWidget *conv_tree = static_cast(hash->user_data); + if (!conv_tree) return; + + conv_tree->clear(); + reset_conversation_table_data(&conv_tree->hash_); +} + +void ConversationTreeWidget::tapDraw(void *conv_hash_ptr) +{ + conv_hash_t *hash = (conv_hash_t*)conv_hash_ptr; + ConversationTreeWidget *conv_tree = static_cast(hash->user_data); + if (!conv_tree) return; + + conv_tree->updateItems(); +} + +QMap fad_to_cd_; + +void ConversationTreeWidget::initDirectionMap() +{ + if (fad_to_cd_.size() > 0) { + return; + } + + fad_to_cd_[FilterAction::ActionDirectionAToFromB] = CONV_DIR_A_TO_FROM_B; + fad_to_cd_[FilterAction::ActionDirectionAToB] = CONV_DIR_A_TO_B; + fad_to_cd_[FilterAction::ActionDirectionAFromB] = CONV_DIR_A_FROM_B; + fad_to_cd_[FilterAction::ActionDirectionAToFromAny] = CONV_DIR_A_TO_FROM_ANY; + fad_to_cd_[FilterAction::ActionDirectionAToAny] = CONV_DIR_A_TO_ANY; + fad_to_cd_[FilterAction::ActionDirectionAFromAny] = CONV_DIR_A_FROM_ANY; + fad_to_cd_[FilterAction::ActionDirectionAnyToFromB] = CONV_DIR_ANY_TO_FROM_B; + fad_to_cd_[FilterAction::ActionDirectionAnyToB] = CONV_DIR_ANY_TO_B; + fad_to_cd_[FilterAction::ActionDirectionAnyFromB] = CONV_DIR_ANY_FROM_B; +} + +void ConversationTreeWidget::updateItems() { + title_ = proto_get_protocol_short_name(find_protocol_by_id(get_conversation_proto_id(table_))); + + if (hash_.conv_array && hash_.conv_array->len > 0) { + title_.append(QString(" %1 %2").arg(UTF8_MIDDLE_DOT).arg(hash_.conv_array->len)); + } + emit titleChanged(this, title_); + + if (!hash_.conv_array) { + return; + } + + setSortingEnabled(false); + for (int i = topLevelItemCount(); i < (int) hash_.conv_array->len; i++) { + ConversationTreeWidgetItem *ctwi = new ConversationTreeWidgetItem(this); + conv_item_t *conv_item = &g_array_index(hash_.conv_array, conv_item_t, i); + ctwi->setData(ci_col_, Qt::UserRole, qVariantFromValue(conv_item)); + addTopLevelItem(ctwi); + + for (int col = 0; col < columnCount(); col++) { + switch (col) { + case CONV_COLUMN_SRC_ADDR: + case CONV_COLUMN_DST_ADDR: + break; + default: + ctwi->setTextAlignment(col, Qt::AlignRight); + break; + } + } + } + QTreeWidgetItemIterator iter(this); + while (*iter) { + ConversationTreeWidgetItem *ci = static_cast(*iter); + ci->update(resolve_names_); + ++iter; + } + setSortingEnabled(true); + + for (int col = 0; col < columnCount(); col++) { + resizeColumnToContents(col); + } +} + +void ConversationTreeWidget::filterActionTriggered() +{ + ConversationTreeWidgetItem *ctwi = static_cast(currentItem()); + FilterAction *fa = qobject_cast(QObject::sender()); + + if (!fa || !ctwi) { + return; + } + + conv_item_t *conv_item = ctwi->data(ci_col_, Qt::UserRole).value(); + if (!conv_item) { + return; + } + + QString filter = get_conversation_filter(conv_item, fad_to_cd_[fa->actionDirection()]); + emit filterAction(filter, fa->action(), fa->actionType()); } /* diff --git a/ui/qt/conversation_dialog.h b/ui/qt/conversation_dialog.h index 59c183d22c..cfff90e9ee 100644 --- a/ui/qt/conversation_dialog.h +++ b/ui/qt/conversation_dialog.h @@ -22,23 +22,29 @@ #ifndef CONVERSATION_DIALOG_H #define CONVERSATION_DIALOG_H -#include +#include "traffic_table_dialog.h" -#include +Q_DECLARE_METATYPE(conv_item_t *) -#include "ui/follow.h" -#include "epan/conversation_table.h" +class ConversationTreeWidget : public TrafficTableTreeWidget +{ + Q_OBJECT +public: + explicit ConversationTreeWidget(QWidget *parent, register_ct_t* table); + ~ConversationTreeWidget(); -#include -#include + static void tapReset(void *conv_hash_ptr); + static void tapDraw(void *conv_hash_ptr); -#include +private: + void initDirectionMap(); -namespace Ui { -class ConversationDialog; -} +private slots: + void updateItems(); + void filterActionTriggered(); +}; -class ConversationDialog : public QDialog +class ConversationDialog : public TrafficTableDialog { Q_OBJECT @@ -50,7 +56,7 @@ public: * @param proto_id If valid, add this protocol and bring it to the front. * @param filter Display filter to apply. */ - explicit ConversationDialog(QWidget *parent = 0, capture_file *cf = NULL, int proto_id = -1, const char *filter = NULL); + explicit ConversationDialog(QWidget *parent = 0, capture_file *cf = NULL, int cli_proto_id = -1, const char *filter = NULL); ~ConversationDialog(); public slots: @@ -62,32 +68,18 @@ signals: void openTcpStreamGraph(int graph_type); private: - Ui::ConversationDialog *ui; - - capture_file *cap_file_; - QString filter_; - QMenu conv_type_menu_; - QMap proto_id_to_tree_; QList conv_actions_; - QPushButton *copy_bt_; QPushButton *follow_bt_; QPushButton *graph_bt_; - // Adds a conversation tree. Returns true if the tree was freshly created, false if it was cached. - bool addConversationTable(register_ct_t* table); + bool addTrafficTable(register_ct_t* table); conv_item_t *currentConversation(); private slots: - void updateWidgets(); - void toggleConversation(); void itemSelectionChanged(); void on_nameResolutionCheckBox_toggled(bool checked); void on_displayFilterCheckBox_toggled(bool checked); - void setTabText(QWidget *tree, const QString &text); - void chainFilterAction(QString& filter, FilterAction::Action action, FilterAction::ActionType type); void followStream(); - void copyAsCsv(); - void copyAsYaml(); void graphTcp(); void on_buttonBox_helpRequested(); }; diff --git a/ui/qt/conversation_tree_widget.cpp b/ui/qt/conversation_tree_widget.cpp deleted file mode 100644 index 00d88bb65a..0000000000 --- a/ui/qt/conversation_tree_widget.cpp +++ /dev/null @@ -1,487 +0,0 @@ -/* conversation_tree_widget.cpp - * - * Wireshark - Network traffic analyzer - * By Gerald Combs - * Copyright 1998 Gerald Combs - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "conversation_tree_widget.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include "wireshark_application.h" - -#include "qt_ui_utils.h" - -#include -#include - -// QTreeWidget subclass that allows tapping - -// Minimum bandwidth calculation duration -// https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=8703 -const double min_bw_calc_duration_ = 5 / 1000.0; // seconds -const QString bps_na_ = QObject::tr("N/A"); - -QMap fad_to_cd_; - -// QTreeWidgetItem subclass that allows sorting -const int ci_col_ = 0; -const int pkts_col_ = 1; -class ConversationTreeWidgetItem : public QTreeWidgetItem -{ -public: - ConversationTreeWidgetItem(QTreeWidget *tree) : QTreeWidgetItem(tree) {} - ConversationTreeWidgetItem(QTreeWidget * parent, const QStringList & strings) - : QTreeWidgetItem (parent,strings) {} - - // Set column text to its cooked representation. - void update(gboolean resolve_names) { - conv_item_t *conv_item = data(ci_col_, Qt::UserRole).value(); - bool ok; - quint64 cur_packets = data(pkts_col_, Qt::UserRole).toULongLong(&ok); - - if (!conv_item) { - return; - } - - quint64 packets = conv_item->tx_frames + conv_item->rx_frames; - if (ok && cur_packets == packets) { - return; - } - - setText(CONV_COLUMN_SRC_ADDR, get_conversation_address(&conv_item->src_address, resolve_names)); - setText(CONV_COLUMN_SRC_PORT, get_conversation_port(conv_item->src_port, conv_item->ptype, resolve_names)); - setText(CONV_COLUMN_DST_ADDR, get_conversation_address(&conv_item->dst_address, resolve_names)); - setText(CONV_COLUMN_DST_PORT, get_conversation_port(conv_item->dst_port, conv_item->ptype, resolve_names)); - - double duration = nstime_to_sec(&conv_item->stop_time) - nstime_to_sec(&conv_item->start_time); - QString col_str, bps_ab = bps_na_, bps_ba = bps_na_; - - col_str = QString("%L1").arg(packets); - setText(CONV_COLUMN_PACKETS, col_str); - col_str = gchar_free_to_qstring(format_size(conv_item->tx_bytes + conv_item->rx_bytes, format_size_unit_none|format_size_prefix_si)); - setText(CONV_COLUMN_BYTES, col_str); - col_str = QString("%L1").arg(conv_item->tx_frames); - setText(CONV_COLUMN_PKT_AB, QString::number(conv_item->tx_frames)); - col_str = gchar_free_to_qstring(format_size(conv_item->tx_bytes, format_size_unit_none|format_size_prefix_si)); - setText(CONV_COLUMN_BYTES_AB, col_str); - col_str = QString("%L1").arg(conv_item->rx_frames); - setText(CONV_COLUMN_PKT_BA, QString::number(conv_item->rx_frames)); - col_str = gchar_free_to_qstring(format_size(conv_item->rx_bytes, format_size_unit_none|format_size_prefix_si)); - setText(CONV_COLUMN_BYTES_BA, col_str); - setText(CONV_COLUMN_START, QString::number(nstime_to_sec(&conv_item->start_time), 'f', 9)); - setText(CONV_COLUMN_DURATION, QString::number(duration, 'f', 6)); - if (duration > min_bw_calc_duration_) { - bps_ab = gchar_free_to_qstring(format_size((gint64) conv_item->tx_bytes * 8 / duration, format_size_unit_none|format_size_prefix_si)); - bps_ba = gchar_free_to_qstring(format_size((gint64) conv_item->rx_bytes * 8 / duration, format_size_unit_none|format_size_prefix_si)); - } - setText(CONV_COLUMN_BPS_AB, bps_ab); - setText(CONV_COLUMN_BPS_BA, bps_ba); - setData(pkts_col_, Qt::UserRole, qVariantFromValue(packets)); - } - - // Return a string, qulonglong, double, or invalid QVariant representing the raw column data. - QVariant colData(int col, bool resolve_names) { - conv_item_t *conv_item = data(ci_col_, Qt::UserRole).value(); - - if (!conv_item) { - return QVariant(); - } - - double duration = nstime_to_sec(&conv_item->stop_time) - nstime_to_sec(&conv_item->start_time); - double bps_ab = 0, bps_ba = 0; - if (duration > min_bw_calc_duration_) { - bps_ab = conv_item->tx_bytes * 8 / duration; - bps_ba = conv_item->rx_bytes * 8 / duration; - } - - switch (col) { - case CONV_COLUMN_SRC_ADDR: - return get_conversation_address(&conv_item->src_address, resolve_names); - case CONV_COLUMN_SRC_PORT: - if (resolve_names) { - return get_conversation_port(conv_item->src_port, conv_item->ptype, resolve_names); - } else { - return quint32(conv_item->src_port); - } - case CONV_COLUMN_DST_ADDR: - return get_conversation_address(&conv_item->dst_address, resolve_names); - case CONV_COLUMN_DST_PORT: - if (resolve_names) { - return get_conversation_port(conv_item->dst_port, conv_item->ptype, resolve_names); - } else { - return quint32(conv_item->dst_port); - } - case CONV_COLUMN_PACKETS: - return quint64(conv_item->tx_frames + conv_item->rx_frames); - case CONV_COLUMN_BYTES: - return quint64(conv_item->tx_bytes + conv_item->rx_bytes); - case CONV_COLUMN_PKT_AB: - return quint64(conv_item->tx_frames); - case CONV_COLUMN_BYTES_AB: - return quint64(conv_item->tx_bytes); - case CONV_COLUMN_PKT_BA: - return quint64(conv_item->rx_frames); - case CONV_COLUMN_BYTES_BA: - return quint64(conv_item->rx_bytes); - case CONV_COLUMN_START: - return nstime_to_sec(&conv_item->start_time); - case CONV_COLUMN_DURATION: - return duration; - case CONV_COLUMN_BPS_AB: - return bps_ab; - case CONV_COLUMN_BPS_BA: - return bps_ba; - default: - return QVariant(); - } - } - - bool operator< (const QTreeWidgetItem &other) const - { - conv_item_t *conv_item = data(ci_col_, Qt::UserRole).value(); - conv_item_t *other_item = other.data(ci_col_, Qt::UserRole).value(); - - if (!conv_item || !other_item) { - return false; - } - - int sort_col = treeWidget()->sortColumn(); - double conv_duration = nstime_to_sec(&conv_item->stop_time) - nstime_to_sec(&conv_item->start_time); - double other_duration = nstime_to_sec(&other_item->stop_time) - nstime_to_sec(&other_item->start_time); - - switch(sort_col) { - case CONV_COLUMN_SRC_ADDR: - return cmp_address(&conv_item->src_address, &other_item->src_address) < 0 ? true : false; - case CONV_COLUMN_SRC_PORT: - return conv_item->src_port < other_item->src_port; - case CONV_COLUMN_DST_ADDR: - return cmp_address(&conv_item->dst_address, &other_item->dst_address) < 0 ? true : false; - case CONV_COLUMN_DST_PORT: - return conv_item->dst_port < other_item->dst_port; - case CONV_COLUMN_PACKETS: - return (conv_item->tx_frames + conv_item->rx_frames) < (other_item->tx_frames + other_item->rx_frames); - case CONV_COLUMN_BYTES: - return (conv_item->tx_bytes + conv_item->rx_bytes) < (other_item->tx_bytes + other_item->rx_bytes); - case CONV_COLUMN_PKT_AB: - return conv_item->tx_frames < other_item->tx_frames; - case CONV_COLUMN_BYTES_AB: - return conv_item->tx_bytes < other_item->tx_bytes; - case CONV_COLUMN_PKT_BA: - return conv_item->rx_frames < other_item->rx_frames; - case CONV_COLUMN_BYTES_BA: - return conv_item->rx_bytes < other_item->rx_bytes; - case CONV_COLUMN_START: - return nstime_to_sec(&conv_item->start_time) < nstime_to_sec(&other_item->start_time); - case CONV_COLUMN_DURATION: - return conv_duration < other_duration; - case CONV_COLUMN_BPS_AB: - return conv_item->tx_bytes / conv_duration < other_item->tx_bytes / other_duration; - case CONV_COLUMN_BPS_BA: - return conv_item->rx_bytes / conv_duration < other_item->rx_bytes / other_duration; - default: - return false; - } - } - -private: -}; - -ConversationTreeWidget::ConversationTreeWidget(QWidget *parent, register_ct_t* table) : - QTreeWidget(parent), - table_(table), - hash_(), - resolve_names_(false) -{ - setRootIsDecorated(false); - sortByColumn(0, Qt::AscendingOrder); - - setColumnCount(CONV_NUM_COLUMNS); - - for (int i = 0; i < CONV_NUM_COLUMNS; i++) { - headerItem()->setText(i, column_titles[i]); - } - - if (get_conversation_hide_ports(table_)) { - hideColumn(CONV_COLUMN_SRC_PORT); - hideColumn(CONV_COLUMN_DST_PORT); - } else if (!strcmp(proto_get_protocol_filter_name(get_conversation_proto_id(table_)), "ncp")) { - headerItem()->setText(CONV_COLUMN_SRC_PORT, conn_a_title); - headerItem()->setText(CONV_COLUMN_DST_PORT, conn_b_title); - } - - int one_en = fontMetrics().height() / 2; - for (int i = 0; i < CONV_NUM_COLUMNS; i++) { - switch (i) { - case CONV_COLUMN_SRC_ADDR: - case CONV_COLUMN_DST_ADDR: - setColumnWidth(i, one_en * strlen("000.000.000.000")); - break; - case CONV_COLUMN_SRC_PORT: - case CONV_COLUMN_DST_PORT: - setColumnWidth(i, one_en * strlen("000000")); - break; - case CONV_COLUMN_PACKETS: - case CONV_COLUMN_PKT_AB: - case CONV_COLUMN_PKT_BA: - setColumnWidth(i, one_en * strlen("00,000")); - break; - case CONV_COLUMN_BYTES: - case CONV_COLUMN_BYTES_AB: - case CONV_COLUMN_BYTES_BA: - setColumnWidth(i, one_en * strlen("000,000")); - break; - case CONV_COLUMN_START: - setColumnWidth(i, one_en * strlen("00.000")); - break; - case CONV_COLUMN_DURATION: - setColumnWidth(i, one_en * strlen("00.000000")); - break; - case CONV_COLUMN_BPS_AB: - case CONV_COLUMN_BPS_BA: - setColumnWidth(i, one_en * strlen("000 k")); - break; - default: - setColumnWidth(i, one_en * 5); - } - } - - QMenu *submenu; - - initDirectionMap(); - - FilterAction::Action cur_action = FilterAction::ActionApply; - submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); - foreach (FilterAction::ActionType at, FilterAction::actionTypes()) { - QMenu *subsubmenu = submenu->addMenu(FilterAction::actionTypeName(at)); - foreach (FilterAction::ActionDirection ad, FilterAction::actionDirections()) { - FilterAction *fa = new FilterAction(subsubmenu, cur_action, at, ad); - subsubmenu->addAction(fa); - connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); - } - } - - cur_action = FilterAction::ActionPrepare; - submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); - foreach (FilterAction::ActionType at, FilterAction::actionTypes()) { - QMenu *subsubmenu = submenu->addMenu(FilterAction::actionTypeName(at)); - foreach (FilterAction::ActionDirection ad, FilterAction::actionDirections()) { - FilterAction *fa = new FilterAction(subsubmenu, cur_action, at, ad); - subsubmenu->addAction(fa); - connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); - } - } - - cur_action = FilterAction::ActionFind; - submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); - foreach (FilterAction::ActionDirection ad, FilterAction::actionDirections()) { - FilterAction *fa = new FilterAction(submenu, cur_action, FilterAction::ActionTypePlain, ad); - submenu->addAction(fa); - connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); - } - - cur_action = FilterAction::ActionColorize; - submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); - foreach (FilterAction::ActionDirection ad, FilterAction::actionDirections()) { - FilterAction *fa = new FilterAction(submenu, cur_action, FilterAction::ActionTypePlain, ad); - submenu->addAction(fa); - connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); - } - - updateItems(); - - connect(wsApp, SIGNAL(addressResolutionChanged()), this, SLOT(updateItems())); -} - -ConversationTreeWidget::~ConversationTreeWidget() { - remove_tap_listener(&hash_); - reset_conversation_table_data(&hash_); -} - -// Callbacks for register_tap_listener -void ConversationTreeWidget::tapReset(void *conv_tree_ptr) -{ - conv_hash_t *hash = (conv_hash_t*)conv_tree_ptr; - ConversationTreeWidget *conv_tree = static_cast(hash->user_data); - if (!conv_tree) return; - - conv_tree->clear(); - reset_conversation_table_data(&conv_tree->hash_); -} - -void ConversationTreeWidget::tapDraw(void *conv_tree_ptr) -{ - conv_hash_t *hash = (conv_hash_t*)conv_tree_ptr; - ConversationTreeWidget *conv_tree = static_cast(hash->user_data); - if (!conv_tree) return; - - conv_tree->updateItems(); -} - -QList ConversationTreeWidget::rowData(int row) -{ - QList row_data; - - for (int col = 0; col < columnCount(); col++) { - if (isColumnHidden(col) || row >= topLevelItemCount()) { - continue; - } - if (row < 0) { - row_data << headerItem()->text(col); - } else { - ConversationTreeWidgetItem *ci = static_cast(topLevelItem(row)); - if (ci) { - row_data << ci->colData(col, resolve_names_); - } - } - } - return row_data; -} - -void ConversationTreeWidget::setNameResolutionEnabled(bool enable) -{ - if (resolve_names_ != enable) { - resolve_names_ = enable; - updateItems(); - } -} - -void ConversationTreeWidget::contextMenuEvent(QContextMenuEvent *event) -{ - bool enable = selectedItems().count() > 0 ? true : false; - - foreach (QMenu *submenu, ctx_menu_.findChildren()) { - submenu->setEnabled(enable); - } - - ctx_menu_.exec(event->globalPos()); -} - -void ConversationTreeWidget::initDirectionMap() -{ - if (fad_to_cd_.size() > 0) { - return; - } - - fad_to_cd_[FilterAction::ActionDirectionAToFromB] = CONV_DIR_A_TO_FROM_B; - fad_to_cd_[FilterAction::ActionDirectionAToB] = CONV_DIR_A_TO_B; - fad_to_cd_[FilterAction::ActionDirectionAFromB] = CONV_DIR_A_FROM_B; - fad_to_cd_[FilterAction::ActionDirectionAToFromAny] = CONV_DIR_A_TO_FROM_ANY; - fad_to_cd_[FilterAction::ActionDirectionAToAny] = CONV_DIR_A_TO_ANY; - fad_to_cd_[FilterAction::ActionDirectionAFromAny] = CONV_DIR_A_FROM_ANY; - fad_to_cd_[FilterAction::ActionDirectionAnyToFromB] = CONV_DIR_ANY_TO_FROM_B; - fad_to_cd_[FilterAction::ActionDirectionAnyToB] = CONV_DIR_ANY_TO_B; - fad_to_cd_[FilterAction::ActionDirectionAnyFromB] = CONV_DIR_ANY_FROM_B; -} - -void ConversationTreeWidget::updateItems() { - title_ = proto_get_protocol_short_name(find_protocol_by_id(get_conversation_proto_id(table_))); - - if (hash_.conv_array && hash_.conv_array->len > 0) { - title_.append(QString(" %1 %2").arg(UTF8_MIDDLE_DOT).arg(hash_.conv_array->len)); - } - emit titleChanged(this, title_); - - if (!hash_.conv_array) { - return; - } - - setSortingEnabled(false); - for (int i = topLevelItemCount(); i < (int) hash_.conv_array->len; i++) { - ConversationTreeWidgetItem *ctwi = new ConversationTreeWidgetItem(this); - conv_item_t *conv_item = &g_array_index(hash_.conv_array, conv_item_t, i); - ctwi->setData(ci_col_, Qt::UserRole, qVariantFromValue(conv_item)); - addTopLevelItem(ctwi); - - for (int col = 0; col < columnCount(); col++) { - switch (col) { - case CONV_COLUMN_SRC_ADDR: - case CONV_COLUMN_DST_ADDR: - break; - default: - ctwi->setTextAlignment(col, Qt::AlignRight); - break; - } - } - } - QTreeWidgetItemIterator iter(this); - while (*iter) { - ConversationTreeWidgetItem *ci = static_cast(*iter); - ci->update(resolve_names_); - ++iter; - } - setSortingEnabled(true); - - for (int col = 0; col < columnCount(); col++) { - resizeColumnToContents(col); - } -} - -void ConversationTreeWidget::filterActionTriggered() -{ - if (selectedItems().count() < 1) { - return; - } - - FilterAction *fa = qobject_cast(QObject::sender()); - ConversationTreeWidgetItem *ctwi = static_cast(selectedItems()[0]); - if (!fa || !ctwi) { - return; - } - - conv_item_t *conv_item = ctwi->data(ci_col_, Qt::UserRole).value(); - if (!conv_item) { - return; - } - - QString filter = get_conversation_filter(conv_item, fad_to_cd_[fa->actionDirection()]); - emit filterAction(filter, fa->action(), fa->actionType()); -} - -/* - * Editor modelines - * - * Local Variables: - * c-basic-offset: 4 - * tab-width: 8 - * indent-tabs-mode: nil - * End: - * - * ex: set shiftwidth=4 tabstop=8 expandtab: - * :indentSize=4:tabSize=8:noTabs=true: - */ diff --git a/ui/qt/conversation_tree_widget.h b/ui/qt/conversation_tree_widget.h deleted file mode 100644 index b170f83ad4..0000000000 --- a/ui/qt/conversation_tree_widget.h +++ /dev/null @@ -1,112 +0,0 @@ -/* conversation_tree_widget.h - * - * Wireshark - Network traffic analyzer - * By Gerald Combs - * Copyright 1998 Gerald Combs - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef CONVERSATION_TREE_WIDGET_H -#define CONVERSATION_TREE_WIDGET_H - -#include "config.h" - -#include - -#include -#include -#include "epan/conversation_table.h" - -#include "ui/conversation_ui.h" - -#include "filter_action.h" - -#include -#include - -Q_DECLARE_METATYPE(conv_item_t *) - -class ConversationTreeWidget : public QTreeWidget -{ - Q_OBJECT -public: - explicit ConversationTreeWidget(QWidget *parent, register_ct_t* table); - ~ConversationTreeWidget(); - - static void tapReset(void *conv_tree_ptr); - static void tapDraw(void *conv_tree_ptr); - - // String, int, or double data for each column in a row. - // Passing -1 returns titles. - QList rowData(int row); - - // Title string plus optional count - const QString &conversationTitle() { return title_; } - conv_hash_t* conversationHash() {return &hash_;} - -signals: - void titleChanged(QWidget *tree, const QString &text); - void filterAction(QString& filter, FilterAction::Action action, FilterAction::ActionType type); - -public slots: - void setNameResolutionEnabled(bool enable); - -protected: - void contextMenuEvent(QContextMenuEvent *event); - -private: - register_ct_t* table_; - QString title_; - conv_hash_t hash_; - bool resolve_names_; - QMenu ctx_menu_; - - void initDirectionMap(); - int tapEthernetPacket(packet_info *pinfo, const void *vip); - int tapFibreChannelPacket(packet_info *pinfo, const void *vip); - int tapFddiPacket(packet_info *pinfo, const void *vip); - int tapIPv4Packet(packet_info *pinfo, const void *vip); - int tapIPv6Packet(packet_info *pinfo, const void *vip); - int tapIpxPacket(packet_info *pinfo, const void *vip); - int tapJxtaPacket(packet_info *pinfo, const void *vip); - int tapNcpPacket(packet_info *pinfo, const void *vip); - int tapRsvpPacket(packet_info *pinfo, const void *vip); - int tapSctpPacket(packet_info *pinfo, const void *vip); - int tapTcpPacket(packet_info *pinfo, const void *vip); - int tapTokenRingPacket(packet_info *pinfo, const void *vip); - int tapUdpPacket(packet_info *pinfo, const void *vip); - int tapUsbPacket(packet_info *pinfo, const void *vip); - int tapWlanPacket(packet_info *pinfo, const void *vip); - -private slots: - void updateItems(); - void filterActionTriggered(); -}; - -#endif // CONVERSATION_TREE_WIDGET_H - -/* - * Editor modelines - * - * Local Variables: - * c-basic-offset: 4 - * tab-width: 8 - * indent-tabs-mode: nil - * End: - * - * ex: set shiftwidth=4 tabstop=8 expandtab: - * :indentSize=4:tabSize=8:noTabs=true: - */ diff --git a/ui/qt/endpoint_dialog.cpp b/ui/qt/endpoint_dialog.cpp new file mode 100644 index 0000000000..1523819d6d --- /dev/null +++ b/ui/qt/endpoint_dialog.cpp @@ -0,0 +1,545 @@ +/* endpoint_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "endpoint_dialog.h" + +#ifdef HAVE_GEOIP +#include +#include +#include +#endif + +#include "ui/recent.h" +#include "ui/traffic_table_ui.h" + +#include "wsutil/str_util.h" + +#include "qt_ui_utils.h" + +#include "wireshark_application.h" + +#include + + +const QString table_name_ = QObject::tr("Endpoint"); +EndpointDialog::EndpointDialog(QWidget *parent, capture_file *cf, int cli_proto_id, const char *filter) : + TrafficTableDialog(parent, cf, filter, table_name_) +{ + QList endp_protos; + for (GList *endp_tab = recent.endpoint_tabs; endp_tab; endp_tab = endp_tab->next) { + int proto_id = proto_get_id_by_short_name((const char *)endp_tab->data); + if (proto_id > -1 && !endp_protos.contains(proto_id)) { + endp_protos.append(proto_id); + } + } + + if (endp_protos.isEmpty()) { + endp_protos = defaultProtos(); + } + + // Bring the command-line specified type to the front. + if (get_conversation_by_proto_id(cli_proto_id)) { + endp_protos.removeAll(cli_proto_id); + endp_protos.prepend(cli_proto_id); + } + + // QTabWidget selects the first item by default. + foreach (int endp_proto, endp_protos) { + addTrafficTable(get_conversation_by_proto_id(endp_proto)); + } + + fillTypeMenu(endp_protos); + + updateWidgets(); + itemSelectionChanged(); + + if (cap_file_) { + cf_retap_packets(cap_file_); + } +} + +EndpointDialog::~EndpointDialog() +{ + prefs_clear_string_list(recent.endpoint_tabs); + recent.endpoint_tabs = NULL; + + EndpointTreeWidget *cur_tree = qobject_cast(trafficTableTabWidget()->currentWidget()); + foreach (QAction *ea, traffic_type_menu_.actions()) { + int proto_id = ea->data().value(); + if (proto_id_to_tree_.contains(proto_id) && ea->isChecked()) { + char *title = g_strdup(proto_get_protocol_short_name(find_protocol_by_id(proto_id))); + if (proto_id_to_tree_[proto_id] == cur_tree) { + recent.endpoint_tabs = g_list_prepend(recent.endpoint_tabs, title); + } else { + recent.endpoint_tabs = g_list_append(recent.endpoint_tabs, title); + } + } + } +} + +void EndpointDialog::setCaptureFile(capture_file *cf) +{ + if (!cf) { // We only want to know when the file closes. + cap_file_ = NULL; + for (int i = 0; i < trafficTableTabWidget()->count(); i++) { + EndpointTreeWidget *cur_tree = qobject_cast(trafficTableTabWidget()->widget(i)); + remove_tap_listener(cur_tree->trafficTreeHash()); + disconnect(cur_tree, SIGNAL(filterAction(QString&,FilterAction::Action,FilterAction::ActionType)), + this, SIGNAL(filterAction(QString&,FilterAction::Action,FilterAction::ActionType))); + } + displayFilterCheckBox()->setEnabled(false); + enabledTypesPushButton()->setEnabled(false); + } +} + +bool EndpointDialog::addTrafficTable(register_ct_t *table) +{ + int proto_id = get_conversation_proto_id(table); + + if (!table || proto_id_to_tree_.contains(proto_id)) { + return false; + } + + EndpointTreeWidget *endp_tree = new EndpointTreeWidget(this, table); + + proto_id_to_tree_[proto_id] = endp_tree; + const char* table_name = proto_get_protocol_short_name(find_protocol_by_id(proto_id)); + + trafficTableTabWidget()->addTab(endp_tree, table_name); + + connect(endp_tree, SIGNAL(itemSelectionChanged()), + this, SLOT(itemSelectionChanged())); + connect(endp_tree, SIGNAL(titleChanged(QWidget*,QString)), + this, SLOT(setTabText(QWidget*,QString))); + connect(endp_tree, SIGNAL(filterAction(QString&,FilterAction::Action,FilterAction::ActionType)), + this, SIGNAL(filterAction(QString&,FilterAction::Action,FilterAction::ActionType))); + connect(nameResolutionCheckBox(), SIGNAL(toggled(bool)), + endp_tree, SLOT(setNameResolutionEnabled(bool))); + + // XXX Move to ConversationTreeWidget ctor? + const char *filter = NULL; + if (displayFilterCheckBox()->isChecked()) { + filter = cap_file_->dfilter; + } else if (!filter_.isEmpty()) { + filter = filter_.toUtf8().constData(); + } + + endp_tree->trafficTreeHash()->user_data = endp_tree; + + GString *error_string = register_tap_listener(proto_get_protocol_filter_name(proto_id), endp_tree->trafficTreeHash(), filter, 0, + EndpointTreeWidget::tapReset, + get_hostlist_packet_func(table), + EndpointTreeWidget::tapDraw); + + if (error_string) { + QMessageBox::warning(this, tr("Endpoint %1 failed to register tap listener").arg(table_name), + error_string->str); + g_string_free(error_string, TRUE); + } + + return true; +} + +void EndpointDialog::on_buttonBox_helpRequested() +{ + wsApp->helpTopicAction(HELP_STATS_ENDPOINTS_DIALOG); +} + +void init_endpoint_table(struct register_ct* ct, const char *filter) +{ + Q_UNUSED(ct) + wsApp->emitStatCommandSignal("Endpoints", filter, GINT_TO_POINTER(get_conversation_proto_id(ct))); +} + +// EndpointTreeWidgetItem +// TrafficTableTreeWidgetItem / QTreeWidgetItem subclass that allows sorting + +const int ei_col_ = 0; +const int pkts_col_ = 1; + +const char *geoip_none_ = "-"; + +class EndpointTreeWidgetItem : public TrafficTableTreeWidgetItem +{ +public: + EndpointTreeWidgetItem(QTreeWidget *tree) : TrafficTableTreeWidgetItem(tree) {} + EndpointTreeWidgetItem(QTreeWidget *parent, const QStringList &strings) + : TrafficTableTreeWidgetItem (parent, strings) {} + + // Set column text to its cooked representation. + void update(gboolean resolve_names) { + hostlist_talker_t *endp_item = data(ei_col_, Qt::UserRole).value(); + bool ok; + quint64 cur_packets = data(pkts_col_, Qt::UserRole).toULongLong(&ok); + + if (!endp_item) { + return; + } + + quint64 packets = endp_item->tx_frames + endp_item->rx_frames; + if (ok && cur_packets == packets) { + return; + } + + setText(ENDP_COLUMN_ADDR, get_conversation_address(&endp_item->myaddress, resolve_names)); + setText(ENDP_COLUMN_PORT, get_conversation_port(endp_item->port, endp_item->ptype, resolve_names)); + + QString col_str; + + col_str = QString("%L1").arg(packets); + setText(ENDP_COLUMN_PACKETS, col_str); + col_str = gchar_free_to_qstring(format_size(endp_item->tx_bytes + endp_item->rx_bytes, format_size_unit_none|format_size_prefix_si)); + setText(ENDP_COLUMN_BYTES, col_str); + col_str = QString("%L1").arg(endp_item->tx_frames); + setText(ENDP_COLUMN_PKT_AB, QString::number(endp_item->tx_frames)); + col_str = gchar_free_to_qstring(format_size(endp_item->tx_bytes, format_size_unit_none|format_size_prefix_si)); + setText(ENDP_COLUMN_BYTES_AB, col_str); + col_str = QString("%L1").arg(endp_item->rx_frames); + setText(ENDP_COLUMN_PKT_BA, QString::number(endp_item->rx_frames)); + col_str = gchar_free_to_qstring(format_size(endp_item->rx_bytes, format_size_unit_none|format_size_prefix_si)); + setText(ENDP_COLUMN_BYTES_BA, col_str); + setData(pkts_col_, Qt::UserRole, qVariantFromValue(packets)); + +#ifdef HAVE_GEOIP + /* Filled in from the GeoIP config, if any */ + for (unsigned i = 0; i < geoip_db_num_dbs(); i++) { + if (endp_item->myaddress.type == AT_IPv4) { + setText(ENDP_NUM_COLUMNS+i, geoip_db_lookup_ipv4(i, pntoh32(endp_item->myaddress.data), geoip_none_)); + } else if (endp_item->myaddress.type == AT_IPv6) { + const struct e_in6_addr *addr = (const struct e_in6_addr *) endp_item->myaddress.data; + setText(ENDP_NUM_COLUMNS+i, geoip_db_lookup_ipv6(i, *addr, geoip_none_)); + } else { + setText(ENDP_NUM_COLUMNS+i, geoip_none_); + } + } +#endif + } + + // Return a string, qulonglong, double, or invalid QVariant representing the raw column data. + QVariant colData(int col, bool resolve_names) const { + hostlist_talker_t *endp_item = data(ei_col_, Qt::UserRole).value(); + + if (!endp_item) { + return QVariant(); + } + + switch (col) { + case ENDP_COLUMN_ADDR: + return get_conversation_address(&endp_item->myaddress, resolve_names); + case ENDP_COLUMN_PORT: + if (resolve_names) { + return get_conversation_port(endp_item->port, endp_item->ptype, resolve_names); + } else { + return quint32(endp_item->port); + } + case ENDP_COLUMN_PACKETS: + return quint64(endp_item->tx_frames + endp_item->rx_frames); + case ENDP_COLUMN_BYTES: + return quint64(endp_item->tx_bytes + endp_item->rx_bytes); + case ENDP_COLUMN_PKT_AB: + return quint64(endp_item->tx_frames); + case ENDP_COLUMN_BYTES_AB: + return quint64(endp_item->tx_bytes); + case ENDP_COLUMN_PKT_BA: + return quint64(endp_item->rx_frames); + case ENDP_COLUMN_BYTES_BA: + return quint64(endp_item->rx_bytes); +#ifdef HAVE_GEOIP + default: + { + bool ok; + + double dval = text(col).toDouble(&ok); + if (ok) { // Assume lat / lon + return dval; + } + + qulonglong ullval = text(col).toULongLong(&ok); + if (ok) { // Assume lat / lon + return ullval; + } + + qlonglong llval = text(col).toLongLong(&ok); + if (ok) { // Assume lat / lon + return llval; + } + + return text(col); + break; + } +#else + default: + return QVariant(); +#endif + } + } + + bool operator< (const QTreeWidgetItem &other) const + { + hostlist_talker_t *endp_item = data(ei_col_, Qt::UserRole).value(); + hostlist_talker_t *other_item = other.data(ei_col_, Qt::UserRole).value(); + + if (!endp_item || !other_item) { + return false; + } + + int sort_col = treeWidget()->sortColumn(); + + switch(sort_col) { + case ENDP_COLUMN_ADDR: + return cmp_address(&endp_item->myaddress, &other_item->myaddress) < 0 ? true : false; + case ENDP_COLUMN_PORT: + return endp_item->port < other_item->port; + case ENDP_COLUMN_PACKETS: + return (endp_item->tx_frames + endp_item->rx_frames) < (other_item->tx_frames + other_item->rx_frames); + case ENDP_COLUMN_BYTES: + return (endp_item->tx_bytes + endp_item->rx_bytes) < (other_item->tx_bytes + other_item->rx_bytes); + case ENDP_COLUMN_PKT_AB: + return endp_item->tx_frames < other_item->tx_frames; + case ENDP_COLUMN_BYTES_AB: + return endp_item->tx_bytes < other_item->tx_bytes; + case ENDP_COLUMN_PKT_BA: + return endp_item->rx_frames < other_item->rx_frames; + case ENDP_COLUMN_BYTES_BA: + return endp_item->rx_bytes < other_item->rx_bytes; +#ifdef HAVE_GEOIP + default: + { + double ei_val, oi_val; + bool ei_ok, oi_ok; + ei_val = text(sort_col).toDouble(&ei_ok); + oi_val = other.text(sort_col).toDouble(&oi_ok); + + if (ei_ok && oi_ok) { // Assume lat / lon + return ei_val < oi_val; + } else { + // XXX Fall back to string comparison. We might want to try sorting naturally + // using QCollator instead. + return text(sort_col) < other.text(sort_col); + } + break; + } +#else + default: + return false; +#endif + } + } + +}; + +// +// EndpointTreeWidget +// TrafficTableTreeWidget / QTreeWidget subclass that allows tapping +// + +EndpointTreeWidget::EndpointTreeWidget(QWidget *parent, register_ct_t *table) : + TrafficTableTreeWidget(parent, table) +{ +#ifdef HAVE_GEOIP + setColumnCount(ENDP_NUM_COLUMNS + geoip_db_num_dbs()); +#else + setColumnCount(ENDP_NUM_BUILTIN_COLUMNS); +#endif + + for (int i = 0; i < ENDP_NUM_COLUMNS; i++) { + headerItem()->setText(i, endp_column_titles[i]); + } + + if (get_conversation_hide_ports(table_)) { + hideColumn(ENDP_COLUMN_PORT); + } else if (!strcmp(proto_get_protocol_filter_name(get_conversation_proto_id(table_)), "ncp")) { + headerItem()->setText(ENDP_COLUMN_PORT, endp_conn_title); + } + +#ifdef HAVE_GEOIP + for (unsigned i = 0; i < geoip_db_num_dbs(); i++) { + headerItem()->setText(ENDP_NUM_COLUMNS + i, geoip_db_name(i)); + hideColumn(ENDP_NUM_COLUMNS + i); + } +#endif + + int one_en = fontMetrics().height() / 2; + for (int i = 0; i < columnCount(); i++) { + switch (i) { + case ENDP_COLUMN_ADDR: + setColumnWidth(i, one_en * strlen("000.000.000.000")); + break; + case ENDP_COLUMN_PORT: + setColumnWidth(i, one_en * strlen("000000")); + break; + case ENDP_COLUMN_PACKETS: + case ENDP_COLUMN_PKT_AB: + case ENDP_COLUMN_PKT_BA: + setColumnWidth(i, one_en * strlen("00,000")); + break; + case ENDP_COLUMN_BYTES: + case ENDP_COLUMN_BYTES_AB: + case ENDP_COLUMN_BYTES_BA: + setColumnWidth(i, one_en * strlen("000,000")); + break; + default: + setColumnWidth(i, one_en * strlen("-00.000000")); // GeoIP + } + } + + QMenu *submenu; + + FilterAction::Action cur_action = FilterAction::ActionApply; + submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); + foreach (FilterAction::ActionType at, FilterAction::actionTypes()) { + FilterAction *fa = new FilterAction(submenu, cur_action, at); + submenu->addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + } + + cur_action = FilterAction::ActionPrepare; + submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); + foreach (FilterAction::ActionType at, FilterAction::actionTypes()) { + FilterAction *fa = new FilterAction(submenu, cur_action, at); + submenu->addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + } + + cur_action = FilterAction::ActionFind; + submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); + foreach (FilterAction::ActionType at, FilterAction::actionTypes()) { + FilterAction *fa = new FilterAction(submenu, cur_action, at); + submenu->addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + } + + cur_action = FilterAction::ActionColorize; + submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action)); + foreach (FilterAction::ActionType at, FilterAction::actionTypes()) { + FilterAction *fa = new FilterAction(submenu, cur_action, at); + submenu->addAction(fa); + connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered())); + } + + updateItems(); + +} + +EndpointTreeWidget::~EndpointTreeWidget() +{ + reset_hostlist_table_data(&hash_); +} + +void EndpointTreeWidget::tapReset(void *conv_hash_ptr) +{ + conv_hash_t *hash = (conv_hash_t*)conv_hash_ptr; + EndpointTreeWidget *endp_tree = static_cast(hash->user_data); + if (!endp_tree) return; + + endp_tree->clear(); + reset_hostlist_table_data(&endp_tree->hash_); +} + +void EndpointTreeWidget::tapDraw(void *conv_hash_ptr) +{ + conv_hash_t *hash = (conv_hash_t*)conv_hash_ptr; + EndpointTreeWidget *endp_tree = static_cast(hash->user_data); + if (!endp_tree) return; + + endp_tree->updateItems(); +} + +void EndpointTreeWidget::updateItems() +{ + title_ = proto_get_protocol_short_name(find_protocol_by_id(get_conversation_proto_id(table_))); + + if (hash_.conv_array && hash_.conv_array->len > 0) { + title_.append(QString(" %1 %2").arg(UTF8_MIDDLE_DOT).arg(hash_.conv_array->len)); + } + emit titleChanged(this, title_); + + if (!hash_.conv_array) { + return; + } + +#ifdef HAVE_GEOIP + if (topLevelItemCount() < 1 && hash_.conv_array->len > 0) { + hostlist_talker_t *endp_item = &g_array_index(hash_.conv_array, hostlist_talker_t, 0); + if (endp_item->myaddress.type == AT_IPv4 || endp_item->myaddress.type == AT_IPv6) { + for (unsigned i = 0; i < geoip_db_num_dbs(); i++) { + showColumn(ENDP_NUM_COLUMNS + i); + } + } + } +#endif + + setSortingEnabled(false); + for (int i = topLevelItemCount(); i < (int) hash_.conv_array->len; i++) { + EndpointTreeWidgetItem *etwi = new EndpointTreeWidgetItem(this); + hostlist_talker_t *endp_item = &g_array_index(hash_.conv_array, hostlist_talker_t, i); + etwi->setData(ei_col_, Qt::UserRole, qVariantFromValue(endp_item)); + addTopLevelItem(etwi); + + for (int col = 0; col < columnCount(); col++) { + if (col != ENDP_COLUMN_ADDR && col < ENDP_NUM_COLUMNS) { + etwi->setTextAlignment(col, Qt::AlignRight); + } + } + } + QTreeWidgetItemIterator iter(this); + while (*iter) { + EndpointTreeWidgetItem *ei = static_cast(*iter); + ei->update(resolve_names_); + ++iter; + } + setSortingEnabled(true); + + for (int col = 0; col < columnCount(); col++) { + resizeColumnToContents(col); + } +} + +void EndpointTreeWidget::filterActionTriggered() +{ + EndpointTreeWidgetItem *etwi = static_cast(currentItem()); + FilterAction *fa = qobject_cast(QObject::sender()); + + if (!fa || !etwi) { + return; + } + + hostlist_talker_t *endp_item = etwi->data(ei_col_, Qt::UserRole).value(); + if (!endp_item) { + return; + } + + QString filter = get_hostlist_filter(endp_item); + emit filterAction(filter, fa->action(), fa->actionType()); +} + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/qt/endpoint_dialog.h b/ui/qt/endpoint_dialog.h new file mode 100644 index 0000000000..bc9744ad9c --- /dev/null +++ b/ui/qt/endpoint_dialog.h @@ -0,0 +1,88 @@ +/* endpoint_dialog.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef ENDPOINT_DIALOG_H +#define ENDPOINT_DIALOG_H + +#include "traffic_table_dialog.h" + +Q_DECLARE_METATYPE(hostlist_talker_t *) + +class EndpointTreeWidget : public TrafficTableTreeWidget +{ + Q_OBJECT +public: + explicit EndpointTreeWidget(QWidget *parent, register_ct_t* table); + ~EndpointTreeWidget(); + + static void tapReset(void *conv_hash_ptr); + static void tapDraw(void *conv_hash_ptr); + +private slots: + void updateItems(); + void filterActionTriggered(); + +}; + +class EndpointDialog : public TrafficTableDialog +{ + Q_OBJECT +public: + /** Create a new endpoint window. + * + * @param parent Parent widget. + * @param cf Capture file. No statistics will be calculated if this is NULL. + * @param proto_id If valid, add this protocol and bring it to the front. + * @param filter Display filter to apply. + */ + explicit EndpointDialog(QWidget *parent = 0, capture_file *cf = NULL, int cli_proto_id = -1, const char *filter = NULL); + ~EndpointDialog(); + +signals: + +public slots: + void setCaptureFile(capture_file *cf); + +private: + QList endp_actions_; + + bool addTrafficTable(register_ct_t* table); + +private slots: + void on_buttonBox_helpRequested(); +}; + +void init_endpoint_table(struct register_ct* ct, const char *filter); + +#endif // ENDPOINT_DIALOG_H + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/qt/filter_action.cpp b/ui/qt/filter_action.cpp index e91642eba6..5911911603 100644 --- a/ui/qt/filter_action.cpp +++ b/ui/qt/filter_action.cpp @@ -21,7 +21,7 @@ #include "filter_action.h" -FilterAction::FilterAction(QObject *parent, Action action, ActionType type, ActionDirection direction) : +FilterAction::FilterAction(QObject *parent, FilterAction::Action action, FilterAction::ActionType type, FilterAction::ActionDirection direction) : QAction(parent), action_(action), type_(type), @@ -30,6 +30,15 @@ FilterAction::FilterAction(QObject *parent, Action action, ActionType type, Acti setText(actionDirectionName(direction)); } +FilterAction::FilterAction(QObject *parent, FilterAction::Action action, FilterAction::ActionType type) : + QAction(parent), + action_(action), + type_(type), + direction_(ActionDirectionAToAny) +{ + setText(actionTypeName(type)); +} + const QList FilterAction::actions() { static const QList actions_ = QList() diff --git a/ui/qt/filter_action.h b/ui/qt/filter_action.h index b1ccff0ec6..ae7afd2341 100644 --- a/ui/qt/filter_action.h +++ b/ui/qt/filter_action.h @@ -65,7 +65,8 @@ public: ActionDirectionAnyFromB }; - explicit FilterAction(QObject *parent = 0, Action action = ActionApply, ActionType type = ActionTypePlain, ActionDirection direction = ActionDirectionAToFromB); + explicit FilterAction(QObject *parent, Action action, ActionType type, ActionDirection direction); + explicit FilterAction(QObject *parent, Action action, ActionType type); Action action() { return action_; } static const QList actions(); diff --git a/ui/qt/main.cpp b/ui/qt/main.cpp index 4507920fc2..f3db3654df 100644 --- a/ui/qt/main.cpp +++ b/ui/qt/main.cpp @@ -143,6 +143,7 @@ #include #include #include "conversation_dialog.h" +#include "endpoint_dialog.h" #ifdef HAVE_LIBPCAP capture_options global_capture_opts; @@ -911,7 +912,7 @@ int main(int argc, char *argv[]) register_all_tap_listeners(); conversation_table_set_gui_info(init_conversation_table); - hostlist_table_set_gui_info(NULL); /* XXX - TODO: Provide "GUI" function for Qt */ + hostlist_table_set_gui_info(init_endpoint_table); if (ex_opt_count("read_format") > 0) { in_file_type = open_info_name_to_type(ex_opt_get_next("read_format")); diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h index f7122d1ad7..df20da5078 100644 --- a/ui/qt/main_window.h +++ b/ui/qt/main_window.h @@ -374,8 +374,10 @@ private slots: void on_actionStatisticsBACappObjectId_triggered(); void on_actionStatisticsBACappService_triggered(); void on_actionStatisticsCollectd_triggered(); - void statCommandConversation(const char *arg = NULL, void *userdata = NULL); + void statCommandConversations(const char *arg = NULL, void *userdata = NULL); void on_actionStatisticsConversations_triggered(); + void statCommandEndpoints(const char *arg = NULL, void *userdata = NULL); + void on_actionStatisticsEndpoints_triggered(); void on_actionStatisticsHART_IP_triggered(); void on_actionStatisticsHTTPPacketCounter_triggered(); void on_actionStatisticsHTTPRequests_triggered(); diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui index fd9c48cc93..5e42d75660 100644 --- a/ui/qt/main_window.ui +++ b/ui/qt/main_window.ui @@ -109,7 +109,7 @@ 0 0 960 - 21 + 22 @@ -370,6 +370,7 @@ + @@ -1739,6 +1740,14 @@ Conversations at different protocol levels + + + Endpoints + + + Endpoints at different protocol levels + + diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp index c3c200a470..714e8b5ee8 100644 --- a/ui/qt/main_window_slots.cpp +++ b/ui/qt/main_window_slots.cpp @@ -72,6 +72,7 @@ #include "capture_file_dialog.h" #include "conversation_dialog.h" #include "decode_as_dialog.h" +#include "endpoint_dialog.h" #include "export_object_dialog.h" #include "export_pdu_dialog.h" #include "io_graph_dialog.h" @@ -2074,7 +2075,7 @@ void MainWindow::on_actionStatisticsCollectd_triggered() openStatisticsTreeDialog("collectd"); } -void MainWindow::statCommandConversation(const char *arg, void *userdata) +void MainWindow::statCommandConversations(const char *arg, void *userdata) { ConversationDialog *conv_dialog = new ConversationDialog(this, cap_file_, GPOINTER_TO_INT(userdata), arg); connect(conv_dialog, SIGNAL(filterAction(QString&,FilterAction::Action,FilterAction::ActionType)), @@ -2090,7 +2091,26 @@ void MainWindow::statCommandConversation(const char *arg, void *userdata) void MainWindow::on_actionStatisticsConversations_triggered() { - statCommandConversation(NULL, NULL); + statCommandConversations(NULL, NULL); +} + +void MainWindow::statCommandEndpoints(const char *arg, void *userdata) +{ + EndpointDialog *endp_dialog = new EndpointDialog(this, cap_file_, GPOINTER_TO_INT(userdata), arg); + connect(endp_dialog, SIGNAL(filterAction(QString&,FilterAction::Action,FilterAction::ActionType)), + this, SLOT(filterAction(QString&,FilterAction::Action,FilterAction::ActionType))); + connect(endp_dialog, SIGNAL(openFollowStreamDialog(follow_type_t)), + this, SLOT(openFollowStreamDialog(follow_type_t))); + connect(endp_dialog, SIGNAL(openTcpStreamGraph(int)), + this, SLOT(openTcpStreamDialog(int))); + connect(this, SIGNAL(setCaptureFile(capture_file*)), + endp_dialog, SLOT(setCaptureFile(capture_file*))); + endp_dialog->show(); +} + +void MainWindow::on_actionStatisticsEndpoints_triggered() +{ + statCommandEndpoints(NULL, NULL); } void MainWindow::on_actionStatisticsHART_IP_triggered() diff --git a/ui/qt/traffic_table_dialog.cpp b/ui/qt/traffic_table_dialog.cpp new file mode 100644 index 0000000000..ac59560b8e --- /dev/null +++ b/ui/qt/traffic_table_dialog.cpp @@ -0,0 +1,347 @@ +/* traffic_table_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "traffic_table_dialog.h" +#include "ui_traffic_table_dialog.h" + +#include +#include +#include + +//#include + +#include "ui/recent.h" +//#include "ui/tap-tcp-stream.h" + +#include "wireshark_application.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Bugs: +// - Name resolution doesn't do anything if its preference is disabled. +// - Columns don't resize correctly. +// - Closing the capture file clears conversation data. + +TrafficTableDialog::TrafficTableDialog(QWidget *parent, capture_file *cf, const char *filter, const QString &table_name) : + QDialog(parent), + ui(new Ui::TrafficTableDialog), + cap_file_(cf), + filter_(filter) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, true); + + setWindowTitle(tr("Wireshark: %1s").arg(table_name)); + ui->enabledTypesPushButton->setText(tr("%1 Types").arg(table_name)); + + // XXX Use recent settings instead + if (parent) { + resize(parent->width(), parent->height() * 3 / 4); + } + + QMenu *copy_menu = new QMenu(); + QAction *ca; + copy_bt_ = ui->buttonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole); + ca = copy_menu->addAction(tr("as CSV")); + ca->setToolTip(tr("Copy all values of this page to the clipboard in CSV (Comma Separated Values) format.")); + connect(ca, SIGNAL(triggered()), this, SLOT(copyAsCsv())); + ca = copy_menu->addAction(tr("as YAML")); + ca->setToolTip(tr("Copy all values of this page to the clipboard in the YAML data serialization format.")); + connect(ca, SIGNAL(triggered()), this, SLOT(copyAsYaml())); + copy_bt_->setMenu(copy_menu); + + ui->enabledTypesPushButton->setMenu(&traffic_type_menu_); + ui->nameResolutionCheckBox->setChecked(gbl_resolv_flags.network_name); + ui->trafficTableTabWidget->setFocus(); + + connect(ui->trafficTableTabWidget, SIGNAL(currentChanged(int)), + this, SLOT(itemSelectionChanged())); + +} + +TrafficTableDialog::~TrafficTableDialog() +{ + delete ui; +} + +const QList TrafficTableDialog::defaultProtos() const +{ + // Reasonable defaults? + return QList() << proto_get_id_by_filter_name( "tcp" ) << proto_get_id_by_filter_name( "eth" ) + << proto_get_id_by_filter_name( "ip" ) << proto_get_id_by_filter_name( "ipv6" ) + << proto_get_id_by_filter_name( "udp" ); +} + +void TrafficTableDialog::fillTypeMenu(QList &enabled_protos) +{ + for (guint i = 0; i < conversation_table_get_num(); i++) { + int proto_id = get_conversation_proto_id(get_conversation_table_by_num(i)); + if (proto_id < 0) { + continue; + } + QString title = proto_get_protocol_short_name(find_protocol_by_id(proto_id)); + + QAction *endp_action = new QAction(title, this); + endp_action->setData(qVariantFromValue(proto_id)); + endp_action->setCheckable(true); + endp_action->setChecked(enabled_protos.contains(proto_id)); + connect(endp_action, SIGNAL(triggered()), this, SLOT(toggleConversation())); + traffic_type_menu_.addAction(endp_action); + } +} + +QDialogButtonBox *TrafficTableDialog::buttonBox() const +{ + return ui->buttonBox; +} + +QTabWidget *TrafficTableDialog::trafficTableTabWidget() const +{ + return ui->trafficTableTabWidget; +} + +QCheckBox *TrafficTableDialog::displayFilterCheckBox() const +{ + return ui->displayFilterCheckBox; +} + +QCheckBox *TrafficTableDialog::nameResolutionCheckBox() const +{ + return ui->nameResolutionCheckBox; +} + +QPushButton *TrafficTableDialog::enabledTypesPushButton() const +{ + return ui->enabledTypesPushButton; +} + +void TrafficTableDialog::on_nameResolutionCheckBox_toggled(bool checked) +{ + Q_UNUSED(checked); + updateWidgets(); +} + +void TrafficTableDialog::on_displayFilterCheckBox_toggled(bool checked) +{ + if (!cap_file_) { + return; + } + + const char *filter = NULL; + if (checked) { + filter = cap_file_->dfilter; + } else if (!filter_.isEmpty()) { + filter = filter_.toUtf8().constData(); + } + + for (int i = 0; i < ui->trafficTableTabWidget->count(); i++) { + set_tap_dfilter(ui->trafficTableTabWidget->widget(i), filter); + } + + cf_retap_packets(cap_file_); +} + +void TrafficTableDialog::setTabText(QWidget *tree, const QString &text) +{ + // Could use QObject::sender as well + int index = ui->trafficTableTabWidget->indexOf(tree); + if (index >= 0) { + ui->trafficTableTabWidget->setTabText(index, text); + } +} + +void TrafficTableDialog::toggleConversation() +{ + QAction *ca = qobject_cast(QObject::sender()); + if (!ca) { + return; + } + + int proto_id = ca->data().value(); + register_ct_t* table = get_conversation_by_proto_id(proto_id); + + bool new_table = addTrafficTable(table); + updateWidgets(); + + if (ca->isChecked()) { + ui->trafficTableTabWidget->setCurrentWidget(proto_id_to_tree_[proto_id]); + } + + if (new_table) { + if (cap_file_) { + cf_retap_packets(cap_file_); + } + } +} + +void TrafficTableDialog::updateWidgets() +{ + QWidget *cur_w = ui->trafficTableTabWidget->currentWidget(); + ui->trafficTableTabWidget->setUpdatesEnabled(false); + ui->trafficTableTabWidget->clear(); + foreach (QAction *ca, traffic_type_menu_.actions()) { + int proto_id = ca->data().value(); + if (proto_id_to_tree_.contains(proto_id) && ca->isChecked()) { + ui->trafficTableTabWidget->addTab(proto_id_to_tree_[proto_id], + proto_id_to_tree_[proto_id]->trafficTreeTitle()); + proto_id_to_tree_[proto_id]->setNameResolutionEnabled(ui->nameResolutionCheckBox->isChecked()); + } + } + ui->trafficTableTabWidget->setCurrentWidget(cur_w); + ui->trafficTableTabWidget->setUpdatesEnabled(true); +} + +QList TrafficTableDialog::curTreeRowData(int row) const +{ + TrafficTableTreeWidget *cur_tree = qobject_cast(ui->trafficTableTabWidget->currentWidget()); + if (!cur_tree) { + return QList(); + } + + return cur_tree->rowData(row); +} + +void TrafficTableDialog::copyAsCsv() +{ + QTreeWidget *cur_tree = qobject_cast(ui->trafficTableTabWidget->currentWidget()); + if (!cur_tree) { + return; + } + + QString csv; + QTextStream stream(&csv, QIODevice::Text); + for (int row = -1; row < cur_tree->topLevelItemCount(); row ++) { + QStringList rdsl; + foreach (QVariant v, curTreeRowData(row)) { + if (!v.isValid()) { + rdsl << "\"\""; + } else if ((int) v.type() == (int) QMetaType::QString) { + rdsl << QString("\"%1\"").arg(v.toString()); + } else { + rdsl << v.toString(); + } + } + stream << rdsl.join(",") << endl; + } + wsApp->clipboard()->setText(stream.readAll()); +} + +void TrafficTableDialog::copyAsYaml() +{ + QTreeWidget *cur_tree = qobject_cast(ui->trafficTableTabWidget->currentWidget()); + if (!cur_tree) { + return; + } + + QString yaml; + QTextStream stream(&yaml, QIODevice::Text); + stream << "---" << endl; + for (int row = -1; row < cur_tree->topLevelItemCount(); row ++) { + stream << "-" << endl; + foreach (QVariant v, curTreeRowData(row)) { + stream << " - " << v.toString() << endl; + } + } + wsApp->clipboard()->setText(stream.readAll()); +} + + +TrafficTableTreeWidget::TrafficTableTreeWidget(QWidget *parent, register_ct_t *table) : + QTreeWidget(parent), + table_(table), + hash_(), + resolve_names_(false) +{ + setRootIsDecorated(false); + sortByColumn(0, Qt::AscendingOrder); + + connect(wsApp, SIGNAL(addressResolutionChanged()), this, SLOT(updateItems())); +} + +TrafficTableTreeWidget::~TrafficTableTreeWidget() +{ + remove_tap_listener(&hash_); +} + +QList TrafficTableTreeWidget::rowData(int row) const +{ + QList row_data; + + if (row >= topLevelItemCount()) { + return row_data; + } + + for (int col = 0; col < columnCount(); col++) { + if (isColumnHidden(col)) { + continue; + } + if (row < 0) { + row_data << headerItem()->text(col); + } else { + TrafficTableTreeWidgetItem *ti = static_cast(topLevelItem(row)); + if (ti) { + row_data << ti->colData(col, resolve_names_); + } + } + } + return row_data; +} + +void TrafficTableTreeWidget::setNameResolutionEnabled(bool enable) +{ + if (resolve_names_ != enable) { + resolve_names_ = enable; + updateItems(); + } +} + +void TrafficTableTreeWidget::contextMenuEvent(QContextMenuEvent *event) +{ + bool enable = currentItem() != NULL ? true : false; + + foreach (QMenu *submenu, ctx_menu_.findChildren()) { + submenu->setEnabled(enable); + } + + ctx_menu_.exec(event->globalPos()); +} + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/qt/traffic_table_dialog.h b/ui/qt/traffic_table_dialog.h new file mode 100644 index 0000000000..9c1bde51cf --- /dev/null +++ b/ui/qt/traffic_table_dialog.h @@ -0,0 +1,168 @@ +/* traffic_table_dialog.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef TRAFFIC_TABLE_DIALOG_H +#define TRAFFIC_TABLE_DIALOG_H + +#include "config.h" + +#include "file.h" + +#include "epan/conversation_table.h" + +#include "ui/follow.h" + +#include "filter_action.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace Ui { +class TrafficTableDialog; +} + +class TrafficTableTreeWidgetItem : public QTreeWidgetItem +{ +public: + TrafficTableTreeWidgetItem(QTreeWidget *tree) : QTreeWidgetItem(tree) {} + TrafficTableTreeWidgetItem(QTreeWidget *parent, const QStringList &strings) + : QTreeWidgetItem (parent, strings) {} + virtual QVariant colData(int col, bool resolve_names) const = 0; +}; + +class TrafficTableTreeWidget : public QTreeWidget +{ + Q_OBJECT +public: + explicit TrafficTableTreeWidget(QWidget *parent, register_ct_t* table); + ~TrafficTableTreeWidget(); + + // String, int, or double data for each column in a row. + // Passing -1 returns titles. + QList rowData(int row) const; + +public slots: + void setNameResolutionEnabled(bool enable); + + // Title string plus optional count + const QString &trafficTreeTitle() { return title_; } + conv_hash_t* trafficTreeHash() {return &hash_;} + +protected: + register_ct_t* table_; + QString title_; + conv_hash_t hash_; + bool resolve_names_; + QMenu ctx_menu_; + + void contextMenuEvent(QContextMenuEvent *event); + +private: + +private slots: + virtual void updateItems() {} + +signals: + void titleChanged(QWidget *tree, const QString &text); + void filterAction(QString& filter, FilterAction::Action action, FilterAction::ActionType type); +}; + +class TrafficTableDialog : public QDialog +{ + Q_OBJECT + +public: + /** Create a new conversation window. + * + * @param parent Parent widget. + * @param cf Capture file. No statistics will be calculated if this is NULL. + * @param proto_id If valid, add this protocol and bring it to the front. + * @param filter Display filter to apply. + */ + explicit TrafficTableDialog(QWidget *parent = 0, capture_file *cf = NULL, const char *filter = NULL, const QString &table_name = tr("Unknown")); + ~TrafficTableDialog(); + +public slots: + virtual void setCaptureFile(capture_file *cf) { Q_UNUSED(cf) } + +signals: + void filterAction(QString& filter, FilterAction::Action action, FilterAction::ActionType type); + void openFollowStreamDialog(follow_type_t type); + void openTcpStreamGraph(int graph_type); + +protected: + Ui::TrafficTableDialog *ui; + + capture_file *cap_file_; + QString filter_; + QMenu traffic_type_menu_; + QPushButton *copy_bt_; + QMap proto_id_to_tree_; + + const QList defaultProtos() const; + void fillTypeMenu(QList &enabled_protos); + // Adds a conversation tree. Returns true if the tree was freshly created, false if it was cached. + virtual bool addTrafficTable(register_ct_t* table) { Q_UNUSED(table) return false; } + + // UI getters + QDialogButtonBox *buttonBox() const; + QTabWidget *trafficTableTabWidget() const; + QCheckBox *displayFilterCheckBox() const; + QCheckBox *nameResolutionCheckBox() const; + QPushButton *enabledTypesPushButton() const; + +protected slots: + virtual void itemSelectionChanged() {} + void updateWidgets(); + +private: + QList curTreeRowData(int row) const; + +private slots: + void on_nameResolutionCheckBox_toggled(bool checked); + void on_displayFilterCheckBox_toggled(bool checked); + void setTabText(QWidget *tree, const QString &text); + void toggleConversation(); + + void copyAsCsv(); + void copyAsYaml(); + virtual void on_buttonBox_helpRequested() = 0; +}; + +#endif // TRAFFIC_TABLE_DIALOG_H + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/qt/conversation_dialog.ui b/ui/qt/traffic_table_dialog.ui similarity index 86% rename from ui/qt/conversation_dialog.ui rename to ui/qt/traffic_table_dialog.ui index 247fee1a31..9e75019076 100644 --- a/ui/qt/conversation_dialog.ui +++ b/ui/qt/traffic_table_dialog.ui @@ -1,7 +1,7 @@ - ConversationDialog - + TrafficTableDialog + 0 @@ -10,12 +10,9 @@ 475 - - Wireshark: Conversations - - + @@ -66,13 +63,10 @@ - + <html><head/><body><p>Add and remove conversation types.</p></body></html> - - Conversation Types - @@ -94,7 +88,7 @@ buttonBox accepted() - ConversationDialog + TrafficTableDialog accept() @@ -110,7 +104,7 @@ buttonBox rejected() - ConversationDialog + TrafficTableDialog reject() diff --git a/ui/recent.c b/ui/recent.c index 7a67047463..a115ee19e6 100644 --- a/ui/recent.c +++ b/ui/recent.c @@ -74,7 +74,8 @@ #define RECENT_GUI_GEOMETRY_WLAN_STATS_PANE "gui.geometry_status_wlan_stats_pane" #define RECENT_LAST_USED_PROFILE "gui.last_used_profile" #define RECENT_GUI_FILEOPEN_REMEMBERED_DIR "gui.fileopen_remembered_dir" -#define RECENT_GUI_CONVERSATION_TABS "gui.conversation_tabs" +#define RECENT_GUI_CONVERSATION_TABS "gui.conversation_tabs" +#define RECENT_GUI_ENDPOINT_TABS "gui.endpoint_tabs" #define RECENT_GUI_GEOMETRY "gui.geom." @@ -800,6 +801,12 @@ write_profile_recent(void) fprintf(rf, RECENT_GUI_CONVERSATION_TABS ": %s\n", string_list); g_free(string_list); + fprintf(rf, "\n# Open endpoint dialog tabs.\n"); + fprintf(rf, "# List of endpoint names, e.g. \"TCP\", \"IPv6\".\n"); + string_list = join_string_list(recent.endpoint_tabs); + fprintf(rf, RECENT_GUI_ENDPOINT_TABS ": %s\n", string_list); + g_free(string_list); + if (get_last_open_dir() != NULL) { fprintf(rf, "\n# Last directory navigated to in File Open dialog.\n"); @@ -1036,6 +1043,8 @@ read_set_recent_pair_static(gchar *key, const gchar *value, recent.has_gui_geometry_main_lower_pane = TRUE; } else if (strcmp(key, RECENT_GUI_CONVERSATION_TABS) == 0) { recent.conversation_tabs = prefs_get_string_list(value); + } else if (strcmp(key, RECENT_GUI_ENDPOINT_TABS) == 0) { + recent.endpoint_tabs = prefs_get_string_list(value); } else if (strcmp(key, RECENT_KEY_COL_WIDTH) == 0) { col_l = prefs_get_string_list(value); if (col_l == NULL) diff --git a/ui/recent.h b/ui/recent.h index 70707d5fd2..a87ec19f4d 100644 --- a/ui/recent.h +++ b/ui/recent.h @@ -99,6 +99,7 @@ typedef struct recent_settings_tag { gboolean privs_warn_if_no_npf; GList *col_width_list; /* column widths */ GList *conversation_tabs; /* enabled conversation dialog tabs */ + GList *endpoint_tabs; /* enabled endpoint dialog tabs */ gchar *gui_fileopen_remembered_dir; /* folder of last capture loaded in File Open dialog */ } recent_settings_t; diff --git a/ui/conversation_ui.c b/ui/traffic_table_ui.c similarity index 75% rename from ui/conversation_ui.c rename to ui/traffic_table_ui.c index 6269f054fd..464ec621f5 100644 --- a/ui/conversation_ui.c +++ b/ui/traffic_table_ui.c @@ -1,4 +1,4 @@ -/* conversation_ui.c +/* traffic_table_ui.c * Copied from gtk/conversations_table.c 2003 Ronnie Sahlberg * Helper routines common to all conversations taps. * @@ -22,10 +22,10 @@ */ #include "config.h" -#include "conversation_ui.h" +#include "traffic_table_ui.h" #include "utf8_entities.h" -const char *column_titles[CONV_NUM_COLUMNS] = { +const char *conv_column_titles[CONV_NUM_COLUMNS] = { "Address A", "Port A", "Address B", @@ -42,10 +42,21 @@ const char *column_titles[CONV_NUM_COLUMNS] = { "bps B " UTF8_RIGHTWARDS_ARROW " A" }; -const char *conn_a_title = "Connection A"; -const char *conn_b_title = "Connection B"; +const char *conv_conn_a_title = "Connection A"; +const char *conv_conn_b_title = "Connection B"; +const char *endp_column_titles[ENDP_NUM_COLUMNS] = { + "Address", + "Port", + "Packets", + "Bytes", + "Packets A " UTF8_RIGHTWARDS_ARROW " B", + "Bytes A " UTF8_RIGHTWARDS_ARROW " B", + "Packets B " UTF8_RIGHTWARDS_ARROW " A", + "Bytes B " UTF8_RIGHTWARDS_ARROW " A" +}; +const char *endp_conn_title = "Connection"; /* * Editor modelines diff --git a/ui/conversation_ui.h b/ui/traffic_table_ui.h similarity index 77% rename from ui/conversation_ui.h rename to ui/traffic_table_ui.h index fff938cf75..0b7de72752 100644 --- a/ui/conversation_ui.h +++ b/ui/traffic_table_ui.h @@ -1,4 +1,4 @@ -/* conversation_ui.h +/* traffic_table_ui.h * Copied from gtk/conversations_table.h 2003 Ronnie Sahlberg * Helper routines common to all conversations taps. * @@ -29,7 +29,7 @@ extern "C" { #endif /* __cplusplus */ /** @file - * Conversation lists. + * Conversation and endpoint lists. */ typedef enum { @@ -51,10 +51,26 @@ typedef enum { CONV_INDEX_COLUMN = CONV_NUM_COLUMNS } conversation_column_type_e; +extern const char *conv_column_titles[CONV_NUM_COLUMNS]; +extern const char *conv_conn_a_title; +extern const char *conv_conn_b_title; -extern const char *column_titles[CONV_NUM_COLUMNS]; -extern const char *conn_a_title; -extern const char *conn_b_title; +typedef enum +{ + ENDP_COLUMN_ADDR, + ENDP_COLUMN_PORT, + ENDP_COLUMN_PACKETS, + ENDP_COLUMN_BYTES, + ENDP_COLUMN_PKT_AB, + ENDP_COLUMN_BYTES_AB, + ENDP_COLUMN_PKT_BA, + ENDP_COLUMN_BYTES_BA, + ENDP_NUM_COLUMNS, +} endpoint_column_type_e; + +extern const char *endp_column_titles[ENDP_NUM_COLUMNS]; + +extern const char *endp_conn_title; #ifdef __cplusplus }