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 <gerald@wireshark.org>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Gerald Combs <gerald@wireshark.org>
This commit is contained in:
Gerald Combs 2014-08-06 10:07:42 -07:00
parent 382815d6bd
commit 30f3d52441
33 changed files with 1866 additions and 1041 deletions

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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
)

View File

@ -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

View File

@ -31,7 +31,7 @@
#include <epan/tap.h>
#include <epan/timestamp.h>
#include <epan/stat_cmd_args.h>
#include <ui/conversation_ui.h>
#include <ui/traffic_table_ui.h>
#include <epan/conversation_table.h>
#include <ui/cli/tshark-tap.h>

View File

@ -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);

View File

@ -26,7 +26,7 @@
#include <epan/conv_id.h>
#include <epan/conversation_table.h>
#include <ui/conversation_ui.h>
#include <ui/traffic_table_ui.h>
/** @file
* Conversation definitions.

View File

@ -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);

View File

@ -25,49 +25,33 @@
#define __HOSTLIST_TABLE_H__
#include <epan/conversation_table.h>
#include <ui/traffic_table_ui.h>
/** @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 */

View File

@ -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
)

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -20,30 +20,21 @@
*/
#include "conversation_dialog.h"
#include "ui_conversation_dialog.h"
#include <epan/addr_resolv.h>
#include <epan/prefs.h>
#include <epan/stat_cmd_args.h>
#include <epan/dissectors/packet-tcp.h>
#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 <QByteArray>
#include <QCheckBox>
#include <QClipboard>
#include <QContextMenuEvent>
#include <QList>
#include <QMap>
#include <QMessageBox>
#include <QPushButton>
#include <QTabWidget>
#include <QTextStream>
#include <QToolButton>
// 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<ConversationTreeWidget *>(ui->conversationTabWidget->currentWidget());
foreach (QAction *ca, conv_type_menu_.actions()) {
ConversationTreeWidget *cur_tree = qobject_cast<ConversationTreeWidget *>(trafficTableTabWidget()->currentWidget());
foreach (QAction *ca, traffic_type_menu_.actions()) {
int proto_id = ca->data().value<int>();
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<ConversationTreeWidget *>(ui->conversationTabWidget->widget(i));
remove_tap_listener(cur_tree->conversationHash());
for (int i = 0; i < trafficTableTabWidget()->count(); i++) {
ConversationTreeWidget *cur_tree = qobject_cast<ConversationTreeWidget *>(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<ConversationTreeWidget *>(ui->conversationTabWidget->currentWidget());
ConversationTreeWidget *cur_tree = qobject_cast<ConversationTreeWidget *>(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<ConversationTreeWidget *>(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<ConversationTreeWidget *>(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<int>();
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<QAction *>(QObject::sender());
if (!ca) {
return;
}
int proto_id = ca->data().value<int>();
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<conv_item_t *>();
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<conv_item_t *>();
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 *>();
conv_item_t *other_item = other.data(ci_col_, Qt::UserRole).value<conv_item_t *>();
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<ConversationTreeWidget *>(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<ConversationTreeWidget *>(hash->user_data);
if (!conv_tree) return;
conv_tree->updateItems();
}
QMap<FilterAction::ActionDirection, conv_direction_e> 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<ConversationTreeWidgetItem *>(*iter);
ci->update(resolve_names_);
++iter;
}
setSortingEnabled(true);
for (int col = 0; col < columnCount(); col++) {
resizeColumnToContents(col);
}
}
void ConversationTreeWidget::filterActionTriggered()
{
ConversationTreeWidgetItem *ctwi = static_cast<ConversationTreeWidgetItem *>(currentItem());
FilterAction *fa = qobject_cast<FilterAction *>(QObject::sender());
if (!fa || !ctwi) {
return;
}
conv_item_t *conv_item = ctwi->data(ci_col_, Qt::UserRole).value<conv_item_t *>();
if (!conv_item) {
return;
}
QString filter = get_conversation_filter(conv_item, fad_to_cd_[fa->actionDirection()]);
emit filterAction(filter, fa->action(), fa->actionType());
}
/*

View File

@ -22,23 +22,29 @@
#ifndef CONVERSATION_DIALOG_H
#define CONVERSATION_DIALOG_H
#include <conversation_tree_widget.h>
#include "traffic_table_dialog.h"
#include <file.h>
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 <QAction>
#include <QMenu>
static void tapReset(void *conv_hash_ptr);
static void tapDraw(void *conv_hash_ptr);
#include <QDialog>
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<int, ConversationTreeWidget *> proto_id_to_tree_;
QList<QAction> 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();
};

View File

@ -1,487 +0,0 @@
/* conversation_tree_widget.cpp
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* 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 <epan/addr_resolv.h>
#include <epan/to_str.h>
#include <epan/dissectors/packet-eth.h>
#include <epan/dissectors/packet-fc.h>
#include <epan/dissectors/packet-fddi.h>
#include <epan/dissectors/packet-ip.h>
#include <epan/dissectors/packet-ipv6.h>
#include <epan/dissectors/packet-ipx.h>
#include <epan/dissectors/packet-jxta.h>
#include <epan/dissectors/packet-ncp-int.h>
#include <epan/dissectors/packet-rsvp.h>
#include <epan/dissectors/packet-sctp.h>
#include <epan/dissectors/packet-tcp.h>
#include <epan/dissectors/packet-tr.h>
#include <epan/dissectors/packet-udp.h>
#include <epan/dissectors/packet-ieee80211.h>
#include <ui/utf8_entities.h>
#include <wsutil/str_util.h>
#include "wireshark_application.h"
#include "qt_ui_utils.h"
#include <QContextMenuEvent>
#include <QTreeWidgetItemIterator>
// 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<FilterAction::ActionDirection, conv_direction_e> 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<conv_item_t *>();
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<conv_item_t *>();
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 *>();
conv_item_t *other_item = other.data(ci_col_, Qt::UserRole).value<conv_item_t *>();
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<ConversationTreeWidget *>(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<ConversationTreeWidget *>(hash->user_data);
if (!conv_tree) return;
conv_tree->updateItems();
}
QList<QVariant> ConversationTreeWidget::rowData(int row)
{
QList<QVariant> 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<ConversationTreeWidgetItem *>(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<QMenu*>()) {
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<ConversationTreeWidgetItem *>(*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<FilterAction *>(QObject::sender());
ConversationTreeWidgetItem *ctwi = static_cast<ConversationTreeWidgetItem *>(selectedItems()[0]);
if (!fa || !ctwi) {
return;
}
conv_item_t *conv_item = ctwi->data(ci_col_, Qt::UserRole).value<conv_item_t *>();
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:
*/

View File

@ -1,112 +0,0 @@
/* conversation_tree_widget.h
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* 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 <glib.h>
#include <epan/packet.h>
#include <epan/tap.h>
#include "epan/conversation_table.h"
#include "ui/conversation_ui.h"
#include "filter_action.h"
#include <QMenu>
#include <QTreeWidget>
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<QVariant> 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:
*/

545
ui/qt/endpoint_dialog.cpp Normal file
View File

@ -0,0 +1,545 @@
/* endpoint_dialog.cpp
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* 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 <GeoIP.h>
#include <epan/geoip_db.h>
#include <wsutil/pint.h>
#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 <QMessageBox>
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<int> 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<EndpointTreeWidget *>(trafficTableTabWidget()->currentWidget());
foreach (QAction *ea, traffic_type_menu_.actions()) {
int proto_id = ea->data().value<int>();
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<EndpointTreeWidget *>(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<hostlist_talker_t *>();
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<hostlist_talker_t *>();
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 *>();
hostlist_talker_t *other_item = other.data(ei_col_, Qt::UserRole).value<hostlist_talker_t *>();
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<EndpointTreeWidget *>(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<EndpointTreeWidget *>(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<EndpointTreeWidgetItem *>(*iter);
ei->update(resolve_names_);
++iter;
}
setSortingEnabled(true);
for (int col = 0; col < columnCount(); col++) {
resizeColumnToContents(col);
}
}
void EndpointTreeWidget::filterActionTriggered()
{
EndpointTreeWidgetItem *etwi = static_cast<EndpointTreeWidgetItem *>(currentItem());
FilterAction *fa = qobject_cast<FilterAction *>(QObject::sender());
if (!fa || !etwi) {
return;
}
hostlist_talker_t *endp_item = etwi->data(ei_col_, Qt::UserRole).value<hostlist_talker_t *>();
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:
*/

88
ui/qt/endpoint_dialog.h Normal file
View File

@ -0,0 +1,88 @@
/* endpoint_dialog.h
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* 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<QAction> 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:
*/

View File

@ -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::Action> FilterAction::actions() {
static const QList<Action> actions_ = QList<Action>()

View File

@ -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<Action> actions();

View File

@ -143,6 +143,7 @@
#include <qlocale.h>
#include <qlibraryinfo.h>
#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"));

View File

@ -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();

View File

@ -109,7 +109,7 @@
<x>0</x>
<y>0</y>
<width>960</width>
<height>21</height>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@ -370,6 +370,7 @@
<addaction name="actionSummary"/>
<addaction name="actionProtocol_Hierarchy"/>
<addaction name="actionStatisticsConversations"/>
<addaction name="actionStatisticsEndpoints"/>
<addaction name="actionStatisticsPacketLen"/>
<addaction name="actionStatisticsIOGraph"/>
<addaction name="separator"/>
@ -1739,6 +1740,14 @@
<string>Conversations at different protocol levels</string>
</property>
</action>
<action name="actionStatisticsEndpoints">
<property name="text">
<string>Endpoints</string>
</property>
<property name="toolTip">
<string>Endpoints at different protocol levels</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

View File

@ -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()

View File

@ -0,0 +1,347 @@
/* traffic_table_dialog.cpp
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* 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 <epan/addr_resolv.h>
#include <epan/prefs.h>
#include <epan/stat_cmd_args.h>
//#include <epan/dissectors/packet-tcp.h>
#include "ui/recent.h"
//#include "ui/tap-tcp-stream.h"
#include "wireshark_application.h"
#include <QCheckBox>
#include <QClipboard>
#include <QContextMenuEvent>
#include <QList>
#include <QMap>
#include <QMessageBox>
#include <QPushButton>
#include <QTreeWidget>
#include <QTextStream>
#include <QToolButton>
// 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<int> TrafficTableDialog::defaultProtos() const
{
// Reasonable defaults?
return QList<int>() << 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<int> &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<QAction *>(QObject::sender());
if (!ca) {
return;
}
int proto_id = ca->data().value<int>();
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<int>();
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<QVariant> TrafficTableDialog::curTreeRowData(int row) const
{
TrafficTableTreeWidget *cur_tree = qobject_cast<TrafficTableTreeWidget *>(ui->trafficTableTabWidget->currentWidget());
if (!cur_tree) {
return QList<QVariant>();
}
return cur_tree->rowData(row);
}
void TrafficTableDialog::copyAsCsv()
{
QTreeWidget *cur_tree = qobject_cast<QTreeWidget *>(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<QTreeWidget *>(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<QVariant> TrafficTableTreeWidget::rowData(int row) const
{
QList<QVariant> 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<TrafficTableTreeWidgetItem *>(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<QMenu*>()) {
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:
*/

View File

@ -0,0 +1,168 @@
/* traffic_table_dialog.h
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* 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 <QPushButton>
#include <QCheckBox>
#include <QDialog>
#include <QDialogButtonBox>
#include <QMenu>
#include <QTabWidget>
#include <QTreeWidget>
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<QVariant> 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<int, TrafficTableTreeWidget *> proto_id_to_tree_;
const QList<int> defaultProtos() const;
void fillTypeMenu(QList<int> &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<QVariant> 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:
*/

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConversationDialog</class>
<widget class="QDialog" name="ConversationDialog">
<class>TrafficTableDialog</class>
<widget class="QDialog" name="TrafficTableDialog">
<property name="geometry">
<rect>
<x>0</x>
@ -10,12 +10,9 @@
<height>475</height>
</rect>
</property>
<property name="windowTitle">
<string>Wireshark: Conversations</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTabWidget" name="conversationTabWidget"/>
<widget class="QTabWidget" name="trafficTableTabWidget"/>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,1,0">
@ -66,13 +63,10 @@
</spacer>
</item>
<item>
<widget class="QPushButton" name="conversationTypePushButton">
<widget class="QPushButton" name="enabledTypesPushButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Add and remove conversation types.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Conversation Types</string>
</property>
</widget>
</item>
</layout>
@ -94,7 +88,7 @@
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ConversationDialog</receiver>
<receiver>TrafficTableDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
@ -110,7 +104,7 @@
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ConversationDialog</receiver>
<receiver>TrafficTableDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">

View File

@ -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)

View File

@ -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;

View File

@ -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

View File

@ -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
}