summaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
authorJörg Mayer <jmayer@loplof.de>2012-01-15 21:59:11 +0000
committerJörg Mayer <jmayer@loplof.de>2012-01-15 21:59:11 +0000
commitbe706c63801fb98d42fb743b27b16cc36273651e (patch)
tree62ed0b552191eb0753d26a3edcbab73459a15f7f /ui
parent6d69ef093cd6868ab51f8b52477a510172033353 (diff)
downloadwireshark-be706c63801fb98d42fb743b27b16cc36273651e.tar.gz
Move gtk to ui/gtk.
This looses the last checkin to gtk, will add this manually back. svn path=/trunk/; revision=40518
Diffstat (limited to 'ui')
-rw-r--r--ui/gtk/CMakeLists.txt287
-rw-r--r--ui/gtk/Makefile.am123
-rw-r--r--ui/gtk/Makefile.common350
-rw-r--r--ui/gtk/Makefile.nmake187
-rw-r--r--ui/gtk/Makefile_custom.common13
-rw-r--r--ui/gtk/STATUS.gtk334
-rw-r--r--ui/gtk/about_dlg.c759
-rw-r--r--ui/gtk/about_dlg.h63
-rw-r--r--ui/gtk/afp_stat.c224
-rw-r--r--ui/gtk/airpcap_dlg.c2911
-rw-r--r--ui/gtk/airpcap_dlg.h65
-rw-r--r--ui/gtk/airpcap_gui_utils.c1090
-rw-r--r--ui/gtk/airpcap_gui_utils.h221
-rw-r--r--ui/gtk/ansi_a_stat.c373
-rw-r--r--ui/gtk/ansi_map_stat.c375
-rw-r--r--ui/gtk/bootp_stat.c300
-rw-r--r--ui/gtk/camel_counter.c242
-rw-r--r--ui/gtk/camel_srt.c258
-rw-r--r--ui/gtk/capture_dlg.c4363
-rw-r--r--ui/gtk/capture_dlg.h210
-rw-r--r--ui/gtk/capture_file_dlg.c1778
-rw-r--r--ui/gtk/capture_file_dlg.h121
-rw-r--r--ui/gtk/capture_globals.h30
-rw-r--r--ui/gtk/capture_if_details_dlg_win32.c2530
-rw-r--r--ui/gtk/capture_if_details_dlg_win32.h45
-rw-r--r--ui/gtk/capture_if_dlg.c1059
-rw-r--r--ui/gtk/capture_if_dlg.h72
-rw-r--r--ui/gtk/capture_info_dlg.c371
-rw-r--r--ui/gtk/cfilter_combo_utils.c95
-rw-r--r--ui/gtk/cfilter_combo_utils.h39
-rw-r--r--ui/gtk/color_dlg.c1064
-rw-r--r--ui/gtk/color_dlg.h55
-rw-r--r--ui/gtk/color_edit_dlg.c636
-rw-r--r--ui/gtk/color_edit_dlg.h42
-rw-r--r--ui/gtk/color_utils.c153
-rw-r--r--ui/gtk/color_utils.h70
-rw-r--r--ui/gtk/compare_stat.c1072
-rw-r--r--ui/gtk/conversations_eth.c86
-rw-r--r--ui/gtk/conversations_fc.c87
-rw-r--r--ui/gtk/conversations_fddi.c84
-rw-r--r--ui/gtk/conversations_ip.c82
-rw-r--r--ui/gtk/conversations_ipv6.c91
-rw-r--r--ui/gtk/conversations_ipx.c85
-rw-r--r--ui/gtk/conversations_jxta.c93
-rw-r--r--ui/gtk/conversations_ncp.c85
-rw-r--r--ui/gtk/conversations_rsvp.c86
-rw-r--r--ui/gtk/conversations_sctp.c94
-rw-r--r--ui/gtk/conversations_table.c3016
-rw-r--r--ui/gtk/conversations_table.h122
-rw-r--r--ui/gtk/conversations_tcpip.c84
-rw-r--r--ui/gtk/conversations_tr.c84
-rw-r--r--ui/gtk/conversations_udpip.c84
-rw-r--r--ui/gtk/conversations_usb.c81
-rw-r--r--ui/gtk/conversations_wlan.c84
-rw-r--r--ui/gtk/dcerpc_stat.c728
-rw-r--r--ui/gtk/decode_as_ber.c141
-rw-r--r--ui/gtk/decode_as_ber.h39
-rw-r--r--ui/gtk/decode_as_dcerpc.c426
-rw-r--r--ui/gtk/decode_as_dcerpc.h156
-rw-r--r--ui/gtk/decode_as_dlg.c2038
-rw-r--r--ui/gtk/decode_as_dlg.h71
-rw-r--r--ui/gtk/dfilter_expr_dlg.c1206
-rw-r--r--ui/gtk/dfilter_expr_dlg.h41
-rw-r--r--ui/gtk/diameter_stat.c246
-rw-r--r--ui/gtk/dissector_tables_dlg.c402
-rw-r--r--ui/gtk/dissector_tables_dlg.h25
-rw-r--r--ui/gtk/dlg_utils.c514
-rw-r--r--ui/gtk/dlg_utils.h119
-rw-r--r--ui/gtk/doxygen.cfg.in65
-rw-r--r--ui/gtk/drag_and_drop.c417
-rw-r--r--ui/gtk/drag_and_drop.h40
-rw-r--r--ui/gtk/expert_comp_dlg.c811
-rw-r--r--ui/gtk/expert_comp_dlg.h30
-rw-r--r--ui/gtk/expert_comp_table.c854
-rw-r--r--ui/gtk/expert_comp_table.h93
-rw-r--r--ui/gtk/expert_indicators.h316
-rw-r--r--ui/gtk/export_object.c567
-rw-r--r--ui/gtk/export_object.h68
-rw-r--r--ui/gtk/export_object_dicom.c78
-rw-r--r--ui/gtk/export_object_http.c75
-rw-r--r--ui/gtk/export_object_smb.c423
-rw-r--r--ui/gtk/export_sslkeys.c276
-rw-r--r--ui/gtk/export_sslkeys.h43
-rw-r--r--ui/gtk/fc_stat.c231
-rw-r--r--ui/gtk/file_dlg.c258
-rw-r--r--ui/gtk/file_dlg.h115
-rw-r--r--ui/gtk/file_import_dlg.c1202
-rw-r--r--ui/gtk/file_import_dlg.h31
-rw-r--r--ui/gtk/fileset_dlg.c401
-rw-r--r--ui/gtk/fileset_dlg.h54
-rw-r--r--ui/gtk/filter_autocomplete.c885
-rw-r--r--ui/gtk/filter_autocomplete.h55
-rw-r--r--ui/gtk/filter_dlg.c1395
-rw-r--r--ui/gtk/filter_dlg.h131
-rw-r--r--ui/gtk/filter_expression_save_dlg.c363
-rw-r--r--ui/gtk/filter_expression_save_dlg.h47
-rw-r--r--ui/gtk/filter_utils.c107
-rw-r--r--ui/gtk/filter_utils.h67
-rw-r--r--ui/gtk/find_dlg.c787
-rw-r--r--ui/gtk/find_dlg.h61
-rw-r--r--ui/gtk/firewall_dlg.c794
-rw-r--r--ui/gtk/firewall_dlg.h32
-rw-r--r--ui/gtk/flow_graph.c673
-rw-r--r--ui/gtk/follow_ssl.c348
-rw-r--r--ui/gtk/follow_ssl.h34
-rw-r--r--ui/gtk/follow_stream.c1109
-rw-r--r--ui/gtk/follow_stream.h115
-rw-r--r--ui/gtk/follow_tcp.c432
-rw-r--r--ui/gtk/follow_tcp.h44
-rw-r--r--ui/gtk/follow_udp.c304
-rw-r--r--ui/gtk/follow_udp.h34
-rw-r--r--ui/gtk/font_utils.c431
-rw-r--r--ui/gtk/font_utils.h75
-rw-r--r--ui/gtk/funnel_stat.c668
-rw-r--r--ui/gtk/goto_dlg.c171
-rw-r--r--ui/gtk/goto_dlg.h76
-rw-r--r--ui/gtk/graph_analysis.c2109
-rw-r--r--ui/gtk/graph_analysis.h136
-rw-r--r--ui/gtk/gsm_a_stat.c671
-rw-r--r--ui/gtk/gsm_map_stat.c477
-rw-r--r--ui/gtk/gsm_map_stat.h46
-rw-r--r--ui/gtk/gsm_map_summary.c300
-rw-r--r--ui/gtk/gtkglobals.h57
-rw-r--r--ui/gtk/gtp_stat.c249
-rw-r--r--ui/gtk/gui_stat_menu.h186
-rw-r--r--ui/gtk/gui_stat_util.c159
-rw-r--r--ui/gtk/gui_stat_util.h65
-rw-r--r--ui/gtk/gui_utils.c1881
-rw-r--r--ui/gtk/gui_utils.h538
-rw-r--r--ui/gtk/h225_counter.c583
-rw-r--r--ui/gtk/h225_ras_srt.c351
-rw-r--r--ui/gtk/help_dlg.c375
-rw-r--r--ui/gtk/help_dlg.h136
-rw-r--r--ui/gtk/hostlist_eth.c91
-rw-r--r--ui/gtk/hostlist_fc.c92
-rw-r--r--ui/gtk/hostlist_fddi.c89
-rw-r--r--ui/gtk/hostlist_ip.c88
-rw-r--r--ui/gtk/hostlist_ipv6.c95
-rw-r--r--ui/gtk/hostlist_ipx.c89
-rw-r--r--ui/gtk/hostlist_jxta.c86
-rw-r--r--ui/gtk/hostlist_ncp.c87
-rw-r--r--ui/gtk/hostlist_rsvp.c94
-rw-r--r--ui/gtk/hostlist_sctp.c87
-rw-r--r--ui/gtk/hostlist_table.c1800
-rw-r--r--ui/gtk/hostlist_table.h123
-rw-r--r--ui/gtk/hostlist_tcpip.c89
-rw-r--r--ui/gtk/hostlist_tr.c89
-rw-r--r--ui/gtk/hostlist_udpip.c89
-rw-r--r--ui/gtk/hostlist_usb.c86
-rw-r--r--ui/gtk/hostlist_wlan.c87
-rw-r--r--ui/gtk/iax2_analysis.c3745
-rw-r--r--ui/gtk/iax2_analysis.h119
-rw-r--r--ui/gtk/io_stat.c2431
-rw-r--r--ui/gtk/keys.h126
-rw-r--r--ui/gtk/ldap_stat.c262
-rw-r--r--ui/gtk/libui.vcproj1095
-rw-r--r--ui/gtk/mac_lte_stat_dlg.c1346
-rw-r--r--ui/gtk/macros_dlg.c56
-rw-r--r--ui/gtk/macros_dlg.h31
-rw-r--r--ui/gtk/main.c3855
-rw-r--r--ui/gtk/main.h366
-rw-r--r--ui/gtk/main_airpcap_toolbar.c430
-rw-r--r--ui/gtk/main_airpcap_toolbar.h33
-rw-r--r--ui/gtk/main_filter_toolbar.c414
-rw-r--r--ui/gtk/main_filter_toolbar.h35
-rw-r--r--ui/gtk/main_menubar.c5495
-rw-r--r--ui/gtk/main_proto_draw.c2250
-rw-r--r--ui/gtk/main_proto_draw.h252
-rw-r--r--ui/gtk/main_statusbar.c934
-rw-r--r--ui/gtk/main_statusbar_private.h42
-rw-r--r--ui/gtk/main_toolbar.c426
-rw-r--r--ui/gtk/main_toolbar.h93
-rw-r--r--ui/gtk/main_welcome.c1469
-rw-r--r--ui/gtk/main_welcome.h94
-rw-r--r--ui/gtk/manual_addr_resolv.c178
-rw-r--r--ui/gtk/manual_addr_resolv.h31
-rw-r--r--ui/gtk/mcast_stream.c469
-rw-r--r--ui/gtk/mcast_stream.h136
-rw-r--r--ui/gtk/mcast_stream_dlg.c801
-rw-r--r--ui/gtk/mcast_stream_dlg.h54
-rw-r--r--ui/gtk/megaco_stat.c243
-rw-r--r--ui/gtk/menus.h161
-rw-r--r--ui/gtk/mgcp_stat.c329
-rw-r--r--ui/gtk/mtp3_stat.c445
-rw-r--r--ui/gtk/mtp3_stat.h55
-rw-r--r--ui/gtk/mtp3_summary.c419
-rw-r--r--ui/gtk/ncp_stat.c746
-rw-r--r--ui/gtk/network_icons.h287
-rw-r--r--ui/gtk/new_packet_list.c1747
-rw-r--r--ui/gtk/new_packet_list.h155
-rw-r--r--ui/gtk/old-gtk-compat.h98
-rw-r--r--ui/gtk/packet_history.c188
-rw-r--r--ui/gtk/packet_history.h36
-rw-r--r--ui/gtk/packet_list_store.c1347
-rw-r--r--ui/gtk/packet_list_store.h130
-rw-r--r--ui/gtk/packet_win.c1113
-rw-r--r--ui/gtk/packet_win.h47
-rw-r--r--ui/gtk/pixmap_save.c214
-rw-r--r--ui/gtk/pixmap_save.h38
-rw-r--r--ui/gtk/plugins_dlg.c122
-rw-r--r--ui/gtk/plugins_dlg.h38
-rw-r--r--ui/gtk/prefs_capture.c1430
-rw-r--r--ui/gtk/prefs_capture.h57
-rw-r--r--ui/gtk/prefs_column.c808
-rw-r--r--ui/gtk/prefs_column.h74
-rw-r--r--ui/gtk/prefs_dlg.c1834
-rw-r--r--ui/gtk/prefs_dlg.h191
-rw-r--r--ui/gtk/prefs_filter_expressions.c386
-rw-r--r--ui/gtk/prefs_filter_expressions.h45
-rw-r--r--ui/gtk/prefs_gui.c699
-rw-r--r--ui/gtk/prefs_gui.h64
-rw-r--r--ui/gtk/prefs_layout.c494
-rw-r--r--ui/gtk/prefs_layout.h57
-rw-r--r--ui/gtk/prefs_nameres.c307
-rw-r--r--ui/gtk/prefs_nameres.h57
-rw-r--r--ui/gtk/prefs_print.c183
-rw-r--r--ui/gtk/prefs_print.h58
-rw-r--r--ui/gtk/prefs_protocols.c88
-rw-r--r--ui/gtk/prefs_protocols.h57
-rw-r--r--ui/gtk/prefs_stream.c228
-rw-r--r--ui/gtk/prefs_stream.h59
-rw-r--r--ui/gtk/prefs_taps.c133
-rw-r--r--ui/gtk/prefs_taps.h57
-rw-r--r--ui/gtk/print_dlg.c1139
-rw-r--r--ui/gtk/profile_dlg.c1372
-rw-r--r--ui/gtk/profile_dlg.h69
-rw-r--r--ui/gtk/progress_dlg.c414
-rw-r--r--ui/gtk/proto_dlg.c593
-rw-r--r--ui/gtk/proto_dlg.h49
-rw-r--r--ui/gtk/proto_help.c696
-rw-r--r--ui/gtk/proto_help.h51
-rw-r--r--ui/gtk/proto_hier_stats_dlg.c588
-rw-r--r--ui/gtk/proto_hier_stats_dlg.h39
-rw-r--r--ui/gtk/radius_stat.c398
-rw-r--r--ui/gtk/range_utils.c659
-rw-r--r--ui/gtk/range_utils.h55
-rw-r--r--ui/gtk/recent.c1105
-rw-r--r--ui/gtk/recent.h182
-rw-r--r--ui/gtk/remote_icons.h224
-rw-r--r--ui/gtk/rlc_lte_stat_dlg.c1578
-rw-r--r--ui/gtk/rpc_progs.c424
-rw-r--r--ui/gtk/rpc_stat.c537
-rw-r--r--ui/gtk/rtp_analysis.c3911
-rw-r--r--ui/gtk/rtp_analysis.h131
-rw-r--r--ui/gtk/rtp_player.c2530
-rw-r--r--ui/gtk/rtp_player.h47
-rw-r--r--ui/gtk/rtp_stream.c202
-rw-r--r--ui/gtk/rtp_stream.h155
-rw-r--r--ui/gtk/rtp_stream_dlg.c1140
-rw-r--r--ui/gtk/rtp_stream_dlg.h52
-rw-r--r--ui/gtk/sat.h43
-rw-r--r--ui/gtk/scsi_stat.c318
-rw-r--r--ui/gtk/sctp_assoc_analyse.c984
-rw-r--r--ui/gtk/sctp_byte_graph_dlg.c1542
-rw-r--r--ui/gtk/sctp_chunk_stat.c339
-rw-r--r--ui/gtk/sctp_chunk_stat_dlg.c812
-rw-r--r--ui/gtk/sctp_error_dlg.c304
-rw-r--r--ui/gtk/sctp_graph_dlg.c1867
-rw-r--r--ui/gtk/sctp_stat.c1337
-rw-r--r--ui/gtk/sctp_stat.h408
-rw-r--r--ui/gtk/sctp_stat_dlg.c681
-rw-r--r--ui/gtk/service_response_time_table.c615
-rw-r--r--ui/gtk/service_response_time_table.h102
-rw-r--r--ui/gtk/simple_dialog.c414
-rw-r--r--ui/gtk/sip_stat.c748
-rw-r--r--ui/gtk/smb2_stat.c227
-rw-r--r--ui/gtk/smb_stat.c265
-rw-r--r--ui/gtk/stats_tree_stat.c402
-rw-r--r--ui/gtk/stock_icons.c267
-rw-r--r--ui/gtk/stock_icons.h97
-rw-r--r--ui/gtk/summary_dlg.c506
-rw-r--r--ui/gtk/summary_dlg.h41
-rw-r--r--ui/gtk/supported_protos_dlg.c360
-rw-r--r--ui/gtk/supported_protos_dlg.h45
-rw-r--r--ui/gtk/tap_param_dlg.c322
-rw-r--r--ui/gtk/tap_param_dlg.h102
-rw-r--r--ui/gtk/tcp_graph.c4608
-rw-r--r--ui/gtk/text_import.c974
-rw-r--r--ui/gtk/text_import.h87
-rw-r--r--ui/gtk/text_import_scanner.h50
-rw-r--r--ui/gtk/text_import_scanner.l102
-rw-r--r--ui/gtk/text_page_utils.c155
-rw-r--r--ui/gtk/text_page_utils.h46
-rw-r--r--ui/gtk/time_shift_dlg.c1002
-rw-r--r--ui/gtk/time_shift_dlg.h38
-rw-r--r--ui/gtk/uat_gui.c1078
-rw-r--r--ui/gtk/uat_gui.h35
-rw-r--r--ui/gtk/ui/main-menubar-ui.xml386
-rw-r--r--ui/gtk/utf8_entities.h55
-rw-r--r--ui/gtk/voip_calls.c4083
-rw-r--r--ui/gtk/voip_calls.h272
-rw-r--r--ui/gtk/voip_calls_dlg.c888
-rw-r--r--ui/gtk/voip_calls_dlg.h52
-rw-r--r--ui/gtk/webbrowser.c520
-rw-r--r--ui/gtk/webbrowser.h44
-rw-r--r--ui/gtk/wlan_stat_dlg.c1970
-rw-r--r--ui/gtk/wsp_stat.c447
-rw-r--r--ui/qt/QtShark.pro10
-rw-r--r--ui/qt/capture_interface_dialog.cpp2
-rw-r--r--ui/qt/display_filter_combo.cpp2
-rw-r--r--ui/qt/display_filter_edit.cpp2
-rw-r--r--ui/qt/interface_tree.cpp2
-rw-r--r--ui/qt/main.cpp6
-rw-r--r--ui/qt/main_window.cpp2
-rw-r--r--ui/qt/packet_list.cpp4
-rw-r--r--ui/qt/qt_ui_utils.cpp4
-rw-r--r--ui/qt/qt_ui_utils.h2
-rw-r--r--ui/qt/wireshark_application.cpp2
308 files changed, 150317 insertions, 19 deletions
diff --git a/ui/gtk/CMakeLists.txt b/ui/gtk/CMakeLists.txt
new file mode 100644
index 0000000000..69df1c121a
--- /dev/null
+++ b/ui/gtk/CMakeLists.txt
@@ -0,0 +1,287 @@
+# CMakeLists.txt
+#
+# $Id$
+#
+# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+
+
+set(WIRESHARK_GTK_SRC
+ about_dlg.c
+ airpcap_dlg.c
+ airpcap_gui_utils.c
+ capture_dlg.c
+ capture_file_dlg.c
+ capture_if_dlg.c
+ capture_info_dlg.c
+ cfilter_combo_utils.c
+ color_dlg.c
+ color_edit_dlg.c
+ color_utils.c
+ conversations_table.c
+ decode_as_ber.c
+ decode_as_dcerpc.c
+ decode_as_dlg.c
+ dfilter_expr_dlg.c
+ dissector_tables_dlg.c
+ dlg_utils.c
+ drag_and_drop.c
+ expert_comp_table.c
+ export_object.c
+ export_object_dicom.c
+ export_object_http.c
+ export_object_smb.c
+ export_sslkeys.c
+ filter_autocomplete.c
+ file_dlg.c
+ file_import_dlg.c
+ fileset_dlg.c
+ filter_dlg.c
+ filter_expression_save_dlg.c
+ filter_utils.c
+ find_dlg.c
+ firewall_dlg.c
+ follow_ssl.c
+ follow_stream.c
+ follow_tcp.c
+ follow_udp.c
+ font_utils.c
+ goto_dlg.c
+ graph_analysis.c
+ gui_stat_util.c
+ gui_utils.c
+ help_dlg.c
+ hostlist_table.c
+ macros_dlg.c
+ main.c
+ main_airpcap_toolbar.c
+ main_filter_toolbar.c
+ main_menubar.c
+ main_proto_draw.c
+ main_statusbar.c
+ main_toolbar.c
+ main_welcome.c
+ manual_addr_resolv.c
+ mcast_stream.c
+ new_packet_list.c
+ packet_history.c
+ packet_list_store.c
+ packet_win.c
+ pixmap_save.c
+ plugins_dlg.c
+ prefs_capture.c
+ prefs_column.c
+ prefs_dlg.c
+ prefs_filter_expressions.c
+ prefs_gui.c
+ prefs_layout.c
+ prefs_nameres.c
+ prefs_print.c
+ prefs_protocols.c
+ prefs_taps.c
+ prefs_stream.c
+ print_dlg.c
+ profile_dlg.c
+ progress_dlg.c
+ proto_dlg.c
+ proto_help.c
+ proto_hier_stats_dlg.c
+ range_utils.c
+ recent.c
+ rtp_player.c
+ rtp_stream.c
+ sctp_byte_graph_dlg.c
+ sctp_error_dlg.c
+ sctp_graph_dlg.c
+ sctp_stat.c
+ service_response_time_table.c
+ simple_dialog.c
+ stock_icons.c
+ summary_dlg.c
+ supported_protos_dlg.c
+ tap_param_dlg.c
+ text_import.c
+ text_page_utils.c
+ time_shift_dlg.c
+ uat_gui.c
+ voip_calls.c
+ webbrowser.c
+)
+
+set(WIRESHARK_TAP_SRC
+ afp_stat.c
+ ansi_a_stat.c
+ ansi_map_stat.c
+ bootp_stat.c
+ camel_counter.c
+ camel_srt.c
+ compare_stat.c
+ conversations_eth.c
+ conversations_fc.c
+ conversations_fddi.c
+ conversations_ip.c
+ conversations_ipv6.c
+ conversations_ipx.c
+ conversations_jxta.c
+ conversations_ncp.c
+ conversations_rsvp.c
+ conversations_sctp.c
+ conversations_tcpip.c
+ conversations_tr.c
+ conversations_udpip.c
+ conversations_usb.c
+ conversations_wlan.c
+ dcerpc_stat.c
+ diameter_stat.c
+ expert_comp_dlg.c
+ fc_stat.c
+ flow_graph.c
+ funnel_stat.c
+ gsm_a_stat.c
+ gsm_map_stat.c
+ gsm_map_summary.c
+ gtp_stat.c
+ h225_counter.c
+ h225_ras_srt.c
+ hostlist_eth.c
+ hostlist_fc.c
+ hostlist_fddi.c
+ hostlist_ip.c
+ hostlist_ipv6.c
+ hostlist_ipx.c
+ hostlist_jxta.c
+ hostlist_ncp.c
+ hostlist_rsvp.c
+ hostlist_sctp.c
+ hostlist_tcpip.c
+ hostlist_tr.c
+ hostlist_udpip.c
+ hostlist_usb.c
+ hostlist_wlan.c
+ iax2_analysis.c
+ io_stat.c
+ ldap_stat.c
+ mac_lte_stat_dlg.c
+ mcast_stream_dlg.c
+ megaco_stat.c
+ mgcp_stat.c
+ mtp3_stat.c
+ mtp3_summary.c
+ ncp_stat.c
+ radius_stat.c
+ rlc_lte_stat_dlg.c
+ rpc_progs.c
+ rpc_stat.c
+ rtp_analysis.c
+ rtp_stream_dlg.c
+ scsi_stat.c
+ sctp_assoc_analyse.c
+ sctp_chunk_stat.c
+ sctp_chunk_stat_dlg.c
+ sctp_stat_dlg.c
+ sip_stat.c
+ smb_stat.c
+ smb2_stat.c
+ stats_tree_stat.c
+ tcp_graph.c
+ voip_calls_dlg.c
+ wlan_stat_dlg.c
+ wsp_stat.c
+)
+
+set(CLEAN_FILES
+ ${WIRESHARK_GTK_SRC}
+ ${WIRESHARK_TAP_SRC}
+)
+
+add_lex_files(WIRESHARK_GTK_SRC
+ text_import_scanner.l
+)
+
+if (WERROR)
+ set_source_files_properties(
+ ${CLEAN_FILES}
+ PROPERTIES
+ COMPILE_FLAGS -Werror
+ )
+endif()
+
+if (NOT ENABLE_GTK3)
+ add_definitions(
+ # We are only allowed to include gtk/gtk.h, no other files. When
+ # violating this with gtk3 the compiler will complain anyway.
+ -DGTK_DISABLE_SINGLE_INCLUDES
+
+ # GTK+ 3 removes many implementation details and struct members from its
+ # public headers. Make the compiler catch all uses of direct access to
+ # struct fields so that you can go through them one by one and replace
+ # them with a call to an accessor function instead.
+ -DGSEAL_ENABLE
+
+ # To verify that your program does not use any deprecated symbols,
+ # you can use defines to remove deprecated symbols from the header files
+ -DGDK_DISABLE_DEPRECATED
+ -DGTK_DISABLE_DEPRECATED
+ )
+endif()
+
+if (WANT_PACKET_EDITOR)
+ add_definitions(
+ -DWANT_PACKET_EDITOR
+ )
+endif()
+
+# add_definitions(-DNEW_MENU_CODE)
+
+if(WIN32)
+ set(PLATFORM_UI_SRC
+ capture_if_details_dlg_win32.c
+ )
+endif()
+
+register_tap_files(wireshark-tap-register.c
+ ${WIRESHARK_TAP_SRC}
+)
+
+add_library(ui STATIC
+ ${WIRESHARK_GTK_SRC}
+ ${WIRESHARK_TAP_SRC}
+ ${PLATFORM_UI_SRC}
+ wireshark-tap-register.c
+)
+set_target_properties(ui PROPERTIES LINK_FLAGS "${WS_LINK_FLAGS}")
+
+install(
+ DIRECTORY
+ ui
+ DIRECTORY_PERMISSIONS
+ OWNER_EXECUTE OWNER_WRITE OWNER_READ
+ GROUP_EXECUTE GROUP_READ
+ WORLD_EXECUTE WORLD_READ
+ DESTINATION
+ ${CMAKE_INSTALL_DATADIR}/${CPACK_PACKAGE_NAME}
+ PATTERN ".svn" EXCLUDE
+)
+
+#install(TARGETS ui
+# LIBRARY DESTINATION lib
+# RUNTIME DESTINATION lib
+# ARCHIVE DESTINATION lib
+#)
+
diff --git a/ui/gtk/Makefile.am b/ui/gtk/Makefile.am
new file mode 100644
index 0000000000..be3a0e5276
--- /dev/null
+++ b/ui/gtk/Makefile.am
@@ -0,0 +1,123 @@
+# Makefile.am
+# Automake file for the GTK interface routines for Wireshark
+#
+# $Id$
+#
+# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+include Makefile.common
+include ../Makefile.am.inc
+
+if HAVE_WARNINGS_AS_ERRORS
+AM_CLEAN_CFLAGS = -Werror
+endif
+
+noinst_LIBRARIES = libui.a libui_dirty.a
+
+CLEANFILES = \
+ libui.a \
+ libui_dirty.a \
+ *~
+
+MAINTAINERCLEANFILES = \
+ $(GENERATED_FILES) \
+ Makefile.in
+
+RUNLEX=$(top_srcdir)/tools/runlex.sh
+
+text_import_scanner_lex.h: text_import_scanner.c
+
+wireshark-tap-register.c: $(WIRESHARK_TAP_SRC) $(top_srcdir)/tools/make-tapreg-dotc
+ @echo Making wireshark-tap-register.c
+ @$(top_srcdir)/tools/make-tapreg-dotc wireshark-tap-register.c $(srcdir) $(WIRESHARK_TAP_SRC)
+
+libui_a_SOURCES = \
+ $(WIRESHARK_GTK_SRC) \
+ $(GENERATED_C_FILES) \
+ $(GENERATED_H_FILES) \
+ $(noinst_HEADERS) \
+ $(WIRESHARK_TAP_SRC)
+
+libui_a_CFLAGS = $(AM_CLEAN_CFLAGS)
+
+libui_a_DEPENDENCIES =
+
+
+libui_dirty_a_SOURCES = \
+ $(DIRTY_GENERATED_C_FILES)
+
+libui_dirty_a_DEPENDENCIES =
+
+# Common headers
+AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/wiretap $(LIBGCRYPT_CFLAGS) $(LIBGNUTLS_CFLAGS) $(PORTAUDIO_INCLUDES)
+
+
+doxygen:
+if HAVE_DOXYGEN
+ $(DOXYGEN) doxygen.cfg
+endif # HAVE_DOXYGEN
+
+checkapi: checkapi-base checkapi-todo
+
+checkapi-base:
+ $(PERL) ../tools/checkAPIs.pl -g deprecated-gtk \
+ $(WIRESHARK_GTK_SRC) \
+ $(WIRESHARK_TAP_SRC) \
+ capture_if_details_dlg_win32.c
+
+checkapi-todo:
+ $(PERL) ../tools/checkAPIs.pl -M -g deprecated-gtk-todo \
+ $(WIRESHARK_GTK_SRC) \
+ $(WIRESHARK_TAP_SRC) \
+ capture_if_details_dlg_win32.c
+
+expert_indicators.h:
+ echo "/* This file was automatically generated. DO NOT EDIT. */" > $@
+ echo >> $@
+ for elevel in chat error none note warn ; do \
+ gdk-pixbuf-csource --raw --name=expert_$${elevel}_pb_data ../image/expert_$${elevel}.png >> $@ ;\
+ done
+
+network_icons.h:
+ echo "/* This file was automatically generated. DO NOT EDIT. */" > $@
+ echo >> $@
+ for icon in bluetooth usb wired wireless ; do \
+ gdk-pixbuf-csource --raw --name=network_$${icon}_pb_data ../image/toolbar/network_$${icon}_16.png >> $@ ;\
+ done
+
+remote_icons.h:
+ echo "/* This file was automatically generated. DO NOT EDIT. */" > $@
+ echo >> $@
+ for icon in arrow globe sat ; do \
+ gdk-pixbuf-csource --raw --name=remote_$${icon}_pb_data ../image/toolbar/remote_$${icon}_16.png >> $@ ;\
+ done
+
+EXTRA_DIST = \
+ $(GENERATOR_FILES) \
+ airpcap_dlg.c \
+ airpcap_gui_utils.c \
+ capture_if_details_dlg_win32.c \
+ capture_if_details_dlg_win32.h \
+ doxygen.cfg.in \
+ libui.vcproj \
+ main_airpcap_toolbar.c \
+ Makefile.common \
+ Makefile.nmake \
+ CMakeLists.txt \
+ Makefile_custom.common
diff --git a/ui/gtk/Makefile.common b/ui/gtk/Makefile.common
new file mode 100644
index 0000000000..af92171155
--- /dev/null
+++ b/ui/gtk/Makefile.common
@@ -0,0 +1,350 @@
+# Makefile.common
+# Contains the stuff from Makefile.am and Makefile.nmake that is
+# a) common to both files and
+# b) portable between both files
+#
+# $Id$
+#
+# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#
+include Makefile_custom.common
+
+# Generated header files that we want in the distribution.
+GENERATED_HEADER_FILES =
+
+# Generated C source files that we want in the distribution.
+GENERATED_C_FILES = \
+ wireshark-tap-register.c
+
+GENERATED_H_FILES = \
+ text_import_scanner_lex.h
+
+DIRTY_GENERATED_C_FILES = \
+ text_import_scanner.c
+
+# All the generated files we want in the distribution.
+GENERATED_FILES = \
+ $(GENERATED_HEADER_FILES) \
+ $(GENERATED_C_FILES) \
+ $(GENERATED_H_FILES) \
+ $(DIRTY_GENERATED_C_FILES)
+
+# Files that generate compileable files
+GENERATOR_FILES = \
+ text_import_scanner.l
+
+WIRESHARK_GTK_SRC = \
+ about_dlg.c \
+ airpcap_dlg.c \
+ airpcap_gui_utils.c \
+ capture_dlg.c \
+ capture_file_dlg.c \
+ capture_if_dlg.c \
+ capture_info_dlg.c \
+ cfilter_combo_utils.c \
+ color_dlg.c \
+ color_edit_dlg.c \
+ color_utils.c \
+ conversations_table.c \
+ decode_as_ber.c \
+ decode_as_dcerpc.c \
+ decode_as_dlg.c \
+ dfilter_expr_dlg.c \
+ dissector_tables_dlg.c \
+ dlg_utils.c \
+ drag_and_drop.c \
+ expert_comp_table.c \
+ export_object.c \
+ export_object_dicom.c \
+ export_object_http.c \
+ export_object_smb.c \
+ export_sslkeys.c \
+ filter_autocomplete.c \
+ file_dlg.c \
+ file_import_dlg.c \
+ fileset_dlg.c \
+ filter_dlg.c \
+ filter_expression_save_dlg.c \
+ filter_utils.c \
+ find_dlg.c \
+ firewall_dlg.c \
+ follow_ssl.c \
+ follow_stream.c \
+ follow_tcp.c \
+ follow_udp.c \
+ font_utils.c \
+ goto_dlg.c \
+ graph_analysis.c \
+ gui_stat_util.c \
+ gui_utils.c \
+ help_dlg.c \
+ hostlist_table.c \
+ macros_dlg.c \
+ main.c \
+ main_airpcap_toolbar.c \
+ main_filter_toolbar.c \
+ main_menubar.c \
+ manual_addr_resolv.c \
+ main_proto_draw.c \
+ main_statusbar.c \
+ main_toolbar.c \
+ main_welcome.c \
+ mcast_stream.c \
+ new_packet_list.c \
+ packet_history.c \
+ packet_list_store.c \
+ packet_win.c \
+ pixmap_save.c \
+ plugins_dlg.c \
+ prefs_capture.c \
+ prefs_column.c \
+ prefs_dlg.c \
+ prefs_filter_expressions.c \
+ prefs_gui.c \
+ prefs_layout.c \
+ prefs_nameres.c \
+ prefs_print.c \
+ prefs_protocols.c \
+ prefs_taps.c \
+ prefs_stream.c \
+ print_dlg.c \
+ profile_dlg.c \
+ progress_dlg.c \
+ proto_dlg.c \
+ proto_help.c \
+ proto_hier_stats_dlg.c \
+ range_utils.c \
+ recent.c \
+ rtp_player.c \
+ rtp_stream.c \
+ sctp_byte_graph_dlg.c \
+ sctp_error_dlg.c \
+ sctp_graph_dlg.c \
+ sctp_stat.c \
+ service_response_time_table.c \
+ simple_dialog.c \
+ stock_icons.c \
+ summary_dlg.c \
+ supported_protos_dlg.c \
+ tap_param_dlg.c \
+ text_import.c \
+ text_page_utils.c \
+ time_shift_dlg.c \
+ uat_gui.c \
+ voip_calls.c \
+ webbrowser.c \
+ $(WIRESHARK_CUSTOM_GTK_SRC)
+
+about_dlg.c main_welcome.c: ../image/wssplash.xpm ../image/wssplash-dev.xpm remote_icons.h
+main_statusbar.c: expert_indicators.h
+capture_if_dlg.c: network_icons.h remote_icons.h
+
+WIRESHARK_TAP_SRC = \
+ afp_stat.c \
+ ansi_a_stat.c \
+ ansi_map_stat.c \
+ bootp_stat.c \
+ camel_counter.c \
+ camel_srt.c \
+ compare_stat.c \
+ conversations_eth.c \
+ conversations_fc.c \
+ conversations_fddi.c \
+ conversations_ip.c \
+ conversations_ipv6.c \
+ conversations_ipx.c \
+ conversations_jxta.c \
+ conversations_ncp.c \
+ conversations_rsvp.c \
+ conversations_sctp.c \
+ conversations_tcpip.c \
+ conversations_tr.c \
+ conversations_udpip.c \
+ conversations_usb.c \
+ conversations_wlan.c \
+ dcerpc_stat.c \
+ diameter_stat.c \
+ expert_comp_dlg.c \
+ fc_stat.c \
+ flow_graph.c \
+ funnel_stat.c \
+ gsm_a_stat.c \
+ gsm_map_stat.c \
+ gsm_map_summary.c \
+ gtp_stat.c \
+ h225_counter.c \
+ h225_ras_srt.c \
+ hostlist_eth.c \
+ hostlist_fc.c \
+ hostlist_fddi.c \
+ hostlist_ip.c \
+ hostlist_ipv6.c \
+ hostlist_ipx.c \
+ hostlist_jxta.c \
+ hostlist_ncp.c \
+ hostlist_rsvp.c \
+ hostlist_sctp.c \
+ hostlist_tcpip.c \
+ hostlist_tr.c \
+ hostlist_udpip.c \
+ hostlist_usb.c \
+ hostlist_wlan.c \
+ iax2_analysis.c \
+ io_stat.c \
+ ldap_stat.c \
+ mac_lte_stat_dlg.c \
+ mcast_stream_dlg.c \
+ megaco_stat.c \
+ mgcp_stat.c \
+ mtp3_stat.c \
+ mtp3_summary.c \
+ ncp_stat.c \
+ radius_stat.c \
+ rlc_lte_stat_dlg.c \
+ rpc_progs.c \
+ rpc_stat.c \
+ rtp_analysis.c \
+ rtp_stream_dlg.c \
+ scsi_stat.c \
+ sctp_assoc_analyse.c \
+ sctp_chunk_stat.c \
+ sctp_chunk_stat_dlg.c \
+ sctp_stat_dlg.c \
+ sip_stat.c \
+ smb_stat.c \
+ smb2_stat.c \
+ stats_tree_stat.c \
+ tcp_graph.c \
+ voip_calls_dlg.c \
+ wlan_stat_dlg.c \
+ wsp_stat.c \
+ $(WIRESHARK_CUSTOM_TAP_SRC)
+
+noinst_HEADERS = \
+ about_dlg.h \
+ airpcap_dlg.h \
+ airpcap_gui_utils.h \
+ capture_dlg.h \
+ capture_file_dlg.h \
+ capture_globals.h \
+ capture_if_dlg.h \
+ cfilter_combo_utils.h \
+ color_dlg.h \
+ color_edit_dlg.h \
+ color_utils.h \
+ conversations_table.h \
+ decode_as_ber.h \
+ decode_as_dlg.h \
+ decode_as_dcerpc.h \
+ dfilter_expr_dlg.h \
+ dissector_tables_dlg.h \
+ dlg_utils.h \
+ drag_and_drop.h \
+ expert_comp_dlg.h \
+ expert_comp_table.h \
+ expert_indicators.h \
+ export_object.h \
+ export_sslkeys.h \
+ file_dlg.h \
+ file_import_dlg.h \
+ fileset_dlg.h \
+ filter_autocomplete.h \
+ filter_dlg.h \
+ filter_expression_save_dlg.h \
+ filter_utils.h \
+ find_dlg.h \
+ firewall_dlg.h \
+ follow_ssl.h \
+ follow_stream.h \
+ follow_tcp.h \
+ follow_udp.h \
+ font_utils.h \
+ goto_dlg.h \
+ graph_analysis.h \
+ gsm_map_stat.h \
+ gtkglobals.h \
+ gui_stat_menu.h \
+ gui_stat_util.h \
+ gui_utils.h \
+ help_dlg.h \
+ hostlist_table.h \
+ iax2_analysis.h \
+ keys.h \
+ macros_dlg.h \
+ main.h \
+ main_airpcap_toolbar.h \
+ main_filter_toolbar.h \
+ menus.h \
+ main_proto_draw.h \
+ main_statusbar_private.h \
+ main_toolbar.h \
+ main_welcome.h \
+ manual_addr_resolv.h \
+ mcast_stream.h \
+ mcast_stream_dlg.h \
+ mtp3_stat.h \
+ network_icons.h \
+ new_packet_list.h \
+ old-gtk-compat.h \
+ packet_history.h \
+ packet_list_store.h \
+ packet_win.h \
+ pixmap_save.h \
+ plugins_dlg.h \
+ prefs_capture.h \
+ prefs_column.h \
+ prefs_dlg.h \
+ prefs_filter_expressions.h \
+ prefs_gui.h \
+ prefs_layout.h \
+ prefs_nameres.h \
+ prefs_print.h \
+ prefs_protocols.h \
+ prefs_taps.h \
+ prefs_stream.h \
+ profile_dlg.h \
+ proto_dlg.h \
+ proto_help.h \
+ proto_hier_stats_dlg.h \
+ range_utils.h \
+ recent.h \
+ remote_icons.h \
+ rtp_analysis.h \
+ rtp_player.h \
+ rtp_stream.h \
+ rtp_stream_dlg.h \
+ sat.h \
+ sctp_stat.h \
+ service_response_time_table.h \
+ time_shift_dlg.h \
+ stock_icons.h \
+ summary_dlg.h \
+ supported_protos_dlg.h \
+ tap_param_dlg.h \
+ text_import.h \
+ text_import_scanner.h \
+ text_page_utils.h \
+ uat_gui.h \
+ utf8_entities.h \
+ voip_calls.h \
+ voip_calls_dlg.h \
+ webbrowser.h \
+ $(WIRESHARK_CUSTOM_HDRS)
+
diff --git a/ui/gtk/Makefile.nmake b/ui/gtk/Makefile.nmake
new file mode 100644
index 0000000000..b8639f87e0
--- /dev/null
+++ b/ui/gtk/Makefile.nmake
@@ -0,0 +1,187 @@
+## Makefile for building wireshark.exe with Microsoft C and nmake
+## Use: $(MAKE) /$(MAKEFLAGS) -f makefile.nmake
+#
+# $Id$
+
+include ..\config.nmake
+include ..\Makefile.nmake.inc
+
+############### no need to modify below this line #########
+
+# We use GENERATED_CFLAGS to get around flex's non-LLP64-compliant output
+GENERATED_CFLAGS=\
+ $(STANDARD_CFLAGS) \
+ -D_NEED_VAR_IMPORT_ /Zm800 \
+ /I.. /I../wiretap $(GTK_CFLAGS) $(GNUTLS_CFLAGS) \
+ /I$(PCAP_DIR)\WPCAP\LIBPCAP /I$(PCAP_DIR)\WPCAP\LIBPCAP\bpf \
+ /I$(PCAP_DIR)\WPCAP\LIBPCAP\lbl \
+ /I$(PCAP_DIR)\include $(AIRPCAP_CFLAGS) \
+ $(PORTAUDIO_CFLAGS) $(GEOIP_CFLAGS) \
+ $(HHC_CFLAGS)
+
+CFLAGS=$(WARNINGS_ARE_ERRORS) $(GENERATED_CFLAGS)
+
+.c.obj::
+ $(CC) $(CVARSDLL) $(CFLAGS) $(WSUG_CFLAGS) -Fd.\ -c $<
+
+include Makefile.common
+
+
+# if you add files here, be sure to include them also in Makefile.am EXTRA_DIST
+WIRESHARK_GTK_SRC = \
+ $(WIRESHARK_GTK_SRC) \
+ $(GENERATED_C_FILES) \
+ capture_if_details_dlg_win32.c
+
+PORT_AUDIO_SRC = \
+!IFDEF PORTAUDIO_DIR
+!IF "$(PORTAUDIO_VERSION)" == "18"
+ pa_lib.c \
+!ELSE
+ pa_allocation.c \
+ pa_converters.c \
+ pa_cpuload.c \
+ pa_dither.c \
+ pa_front.c \
+ pa_process.c \
+ pa_skeleton.c \
+ pa_stream.c \
+ pa_trace.c \
+ pa_win_util.c \
+ pa_win_hostapis.c \
+ pa_x86_plain_converters.c \
+ pa_win_waveformat.c \
+!ENDIF
+ pa_win_wmme.c
+!ENDIF
+
+WIRESHARK_TAP_OBJECTS = $(WIRESHARK_TAP_SRC:.c=.obj)
+
+
+WIRESHARK_GTK_OBJECTS = \
+ $(WIRESHARK_GTK_SRC:.c=.obj) \
+ $(DIRTY_GENERATED_C_FILES:.c=.obj) \
+ $(PORT_AUDIO_SRC:.c=.obj)
+
+libui.lib : ..\config.h $(WIRESHARK_GTK_OBJECTS) $(WIRESHARK_TAP_OBJECTS)
+ link /lib /out:libui.lib $(WIRESHARK_GTK_OBJECTS) $(WIRESHARK_TAP_OBJECTS) winmm.lib
+
+# The shell script runs slowly, as multiple greps and seds are run
+# for each input file; this is especially slow on Windows. Therefore,
+# if Python is present (as indicated by PYTHON being defined), we run
+# a faster Python script to do that work instead.
+#
+# The first argument is the directory in which the source files live.
+# The second argument is "plugin", to indicate that we should build
+# a plugin.c file for a plugin.
+# All subsequent arguments are the files to scan.
+#
+!IFDEF PYTHON
+wireshark-tap-register.c: $(WIRESHARK_TAP_SRC) ../tools/make-tap-reg.py
+ @echo Making wireshark-tap-register.c (using python)
+ @$(PYTHON) "../tools/make-tap-reg.py" . taps $(WIRESHARK_TAP_SRC)
+!ELSE
+wireshark-tap-register.c: $(WIRESHARK_TAP_SRC) ../tools/make-tapreg-dotc Makefile.common
+ @echo Making wireshark-tap-register.c
+ @$(SH) ../tools/make-tapreg-dotc wireshark-tap-register.c . $(WIRESHARK_TAP_SRC)
+!ENDIF
+
+RUNLEX=..\tools\runlex.sh
+
+text_import_scanner.h: text_import_scanner.c
+text_import_scanner_lex.h: text_import_scanner.c
+text_import_scanner.obj : text_import_scanner.c
+ $(CC) $(CVARSDLL) $(GENERATED_CFLAGS) $(WSUG_CFLAGS) -Fd.\ -c $?
+
+!IF "$(PORTAUDIO_VERSION)" == "18"
+pa_lib.obj: $(PORTAUDIO_DIR)\pa_common\pa_lib.c
+ $(CC) -c $(LOCAL_CFLAGS) $?
+
+pa_win_wmme.obj: $(PORTAUDIO_DIR)\pa_win_wmme\pa_win_wmme.c
+ $(CC) -c $(LOCAL_CFLAGS) $(PORTAUDIO_CFLAGS) $?
+!ELSE
+pa_front.obj: $(PORTAUDIO_DIR)\src\common\pa_front.c
+ $(CC) -c $(LOCAL_CFLAGS) $(PORTAUDIO_CFLAGS) $?
+
+pa_allocation.obj: $(PORTAUDIO_DIR)\src\common\pa_allocation.c
+ $(CC) -c $(LOCAL_CFLAGS) $(PORTAUDIO_CFLAGS) $?
+
+pa_converters.obj: $(PORTAUDIO_DIR)\src\common\pa_converters.c
+ $(CC) -c $(LOCAL_CFLAGS) $(PORTAUDIO_CFLAGS) $?
+
+pa_cpuload.obj: $(PORTAUDIO_DIR)\src\common\pa_cpuload.c
+ $(CC) -c $(LOCAL_CFLAGS) $(PORTAUDIO_CFLAGS) $?
+
+pa_dither.obj: $(PORTAUDIO_DIR)\src\common\pa_dither.c
+ $(CC) -c $(LOCAL_CFLAGS) $(PORTAUDIO_CFLAGS) $?
+
+pa_process.obj: $(PORTAUDIO_DIR)\src\common\pa_process.c
+ $(CC) -c $(LOCAL_CFLAGS) $(PORTAUDIO_CFLAGS) $?
+
+pa_skeleton.obj: $(PORTAUDIO_DIR)\src\common\pa_skeleton.c
+ $(CC) -c $(LOCAL_CFLAGS) $(PORTAUDIO_CFLAGS) $?
+
+pa_stream.obj: $(PORTAUDIO_DIR)\src\common\pa_stream.c
+ $(CC) -c $(LOCAL_CFLAGS) $(PORTAUDIO_CFLAGS) $?
+
+pa_trace.obj: $(PORTAUDIO_DIR)\src\common\pa_trace.c
+ $(CC) -c $(LOCAL_CFLAGS) $(PORTAUDIO_CFLAGS) $?
+
+pa_win_wmme.obj: $(PORTAUDIO_DIR)\src\hostapi\wmme\pa_win_wmme.c
+ $(CC) -c $(LOCAL_CFLAGS) $(PORTAUDIO_CFLAGS) $?
+
+pa_win_util.obj: $(PORTAUDIO_DIR)\src\os\win\pa_win_util.c
+ $(CC) -c $(LOCAL_CFLAGS) $(PORTAUDIO_CFLAGS) $?
+
+pa_win_hostapis.obj: $(PORTAUDIO_DIR)\src\os\win\pa_win_hostapis.c
+ $(CC) -c $(LOCAL_CFLAGS) $(PORTAUDIO_CFLAGS) $?
+
+pa_x86_plain_converters.obj: $(PORTAUDIO_DIR)\src\os\win\pa_x86_plain_converters.c
+ $(CC) -c $(LOCAL_CFLAGS) $(PORTAUDIO_CFLAGS) $?
+
+pa_win_waveformat.obj: $(PORTAUDIO_DIR)\src\os\win\pa_win_waveformat.c
+ $(CC) -c $(LOCAL_CFLAGS) $(PORTAUDIO_CFLAGS) $?
+
+!ENDIF
+
+clean:
+ rm -f $(WIRESHARK_GTK_OBJECTS) $(WIRESHARK_TAP_OBJECTS) libui.lib *.pdb doxygen.cfg html/*.* wireshark-tap-register-cache.pkl
+ if exist html rmdir html
+
+distclean: clean
+
+maintainer-clean: distclean
+ rm -f $(GENERATED_FILES)
+
+# convert doxygen.cfg.in to doxygen.cfg with stamped version info
+doxygen.cfg: ..\config.nmake doxygen.cfg.in
+!IFDEF DOXYGEN
+ sed -e s/@VERSION@/$(VERSION)/ \
+ < doxygen.cfg.in > $@
+!ENDIF
+
+doxygen-run:
+!IFDEF DOXYGEN
+ $(DOXYGEN) doxygen.cfg
+!ENDIF
+
+# MS html help compiler hhc returns 1 on success, but as nmake expects 0 it would stop here.
+# the prepended -1 will raise the accepted error levels of nmake, so it will continue
+doxygen.chm:
+!IFDEF HHC
+ -1 $(HHC) html\index.hhp
+!ENDIF
+
+doxygen: doxygen.cfg doxygen-run doxygen.chm
+
+checkapi: checkapi-base checkapi-todo
+
+checkapi-base:
+ $(PERL) ../tools/checkAPIs.pl -g deprecated-gtk \
+ $(WIRESHARK_GTK_SRC) \
+ $(WIRESHARK_TAP_SRC)
+
+checkapi-todo:
+ $(PERL) ../tools/checkAPIs.pl -M -g deprecated-gtk-todo \
+ $(WIRESHARK_GTK_SRC) \
+ $(WIRESHARK_TAP_SRC)
diff --git a/ui/gtk/Makefile_custom.common b/ui/gtk/Makefile_custom.common
new file mode 100644
index 0000000000..539b3f3b8f
--- /dev/null
+++ b/ui/gtk/Makefile_custom.common
@@ -0,0 +1,13 @@
+#
+# $Id$
+# You can add custom GUI files here
+#
+
+#Add GUI source files here
+WIRESHARK_CUSTOM_GTK_SRC =
+
+#Add your tap source files here:
+WIRESHARK_CUSTOM_TAP_SRC =
+
+#Add headers here:
+WIRESHARK_CUSTOM_HDRS =
diff --git a/ui/gtk/STATUS.gtk3 b/ui/gtk/STATUS.gtk3
new file mode 100644
index 0000000000..58999d87ed
--- /dev/null
+++ b/ui/gtk/STATUS.gtk3
@@ -0,0 +1,34 @@
+Status of the GTK+ 3 port :
+===========================
+
+$Id$
+
+The port is done. We used the methods described in the "Migrating from
+GTK+ 2.x to GTK+ 3" document at:
+http://developer.gnome.org/gtk3/stable/gtk-migrating-2-to-3.html
+
+To build Wireshark with GTK3 instead of GTK2, use:
+
+ - cmake: ENABLE_GTK3=ON
+ - configure: --with-gtk3
+ This will only work if building without gtkvumeter.c (which is used on Windows
+ only and only in the WLAN code). It also requires GTK3 to be installed.
+ GTK3 is available for Windows as a mingw build, see
+ http://lists-archives.org/gtk-devel/13233-fwd-precompiled-gtk3-for-windows.html
+ A Win32 port is in the making (and availalbe as source):
+ http://blogs.gnome.org/alexl/2011/11/25/gtk-work-on-windows/
+ The location of an experimental build can be found at:
+ http://www.pguridi.com.ar/2011/10/experimental-gtk-3-3-2-win32-build/
+
+Except in one place we are building on GTK2 using the compat flags
+ -DGTK_DISABLE_SINGLE_INCLUDES
+ -DGSEAL_ENABLE
+ -DGDK_DISABLE_DEPRECATED [works everywhere except tcp_graph.h]
+ -DGTK_DISABLE_DEPRECATED
+
+Summary: The only known files that cause some sort of problems (or lost
+ functionality) are:
+ - gtkvumeter.c (removed, only used for wlan on windows)
+ - proto_help.c (not ported to UI-MANAGER)
+ - tcp_graph.c (does not compile with GTK2 and GDK_DISABLE_DEPRECATED)
+
diff --git a/ui/gtk/about_dlg.c b/ui/gtk/about_dlg.c
new file mode 100644
index 0000000000..d0e0f228be
--- /dev/null
+++ b/ui/gtk/about_dlg.c
@@ -0,0 +1,759 @@
+/* about_dlg.c
+ *
+ * $Id$
+ *
+ * Ulf Lamping <ulf.lamping@web.de>
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <string.h>
+
+#include <epan/filesystem.h>
+#include <epan/plugins.h>
+#ifdef HAVE_LIBSMI
+#include <epan/oids.h>
+#endif
+#ifdef HAVE_GEOIP
+#include <epan/geoip_db.h>
+#endif
+
+#include "../log.h"
+#include "../version_info.h"
+
+#include "ui/gtk/about_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/file_dlg.h"
+#include "ui/gtk/text_page_utils.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/plugins_dlg.h"
+
+#include "../image/wssplash-dev.xpm"
+#include "webbrowser.h"
+
+/*
+ * Update frequence for the splash screen, given in milliseconds.
+ */
+int info_update_freq = 100;
+
+static void about_wireshark_destroy_cb(GtkWidget *, gpointer);
+
+
+/*
+ * Keep a static pointer to the current "About Wireshark" window, if any, so
+ * that if somebody tries to do "About Wireshark" while there's already an
+ * "About Wireshark" window up, we just pop up the existing one, rather than
+ * creating a new one.
+ */
+static GtkWidget *about_wireshark_w;
+
+
+static void
+about_wireshark(GtkWidget *parent _U_, GtkWidget *main_vb)
+{
+ GtkWidget *msg_label, *icon;
+ gchar *message;
+ const char *title = "Network Protocol Analyzer";
+
+ /*icon = xpm_to_widget_from_parent(parent, wssplash_xpm);*/
+ icon = xpm_to_widget(wssplash_xpm);
+
+ gtk_container_add(GTK_CONTAINER(main_vb), icon);
+
+ msg_label = gtk_label_new(title);
+ message = g_strdup_printf("<span size=\"x-large\" weight=\"bold\">%s</span>", title);
+ gtk_label_set_markup(GTK_LABEL(msg_label), message);
+ g_free(message);
+ gtk_container_add(GTK_CONTAINER(main_vb), msg_label);
+}
+
+static void
+splash_update_label(GtkWidget *win, const char *message)
+{
+ GtkWidget *main_lb;
+
+ if (win == NULL) return;
+
+ main_lb = (GtkWidget *)g_object_get_data(G_OBJECT(win), "splash_label");
+ gtk_label_set_text(GTK_LABEL(main_lb), message);
+
+ /* Process all pending GUI events before continuing, so that
+ the splash screen window gets updated. */
+ while (gtk_events_pending()) gtk_main_iteration();
+}
+#if GTK_CHECK_VERSION(3,0,0)
+GtkWidget*
+splash_new(const char *message)
+{
+ GtkWidget *win;
+ GtkWidget *main_lb;
+
+ GtkWidget *main_box;
+ GtkWidget *percentage_box;
+ GtkWidget *prog_bar;
+ GtkWidget *percentage_lb;
+
+ win = splash_window_new();
+
+ /* When calling about_wireshark(), we must realize the top-level
+ widget for the window, otherwise GTK will throw a warning
+ because we don't have a colormap associated with that window and
+ can't handle the pixmap. */
+ gtk_widget_realize(win);
+
+ main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_set_border_width(GTK_CONTAINER(main_box), 24);
+ gtk_container_add(GTK_CONTAINER(win), main_box);
+
+ about_wireshark(win, main_box);
+
+ main_lb = gtk_label_new(message);
+ gtk_container_add(GTK_CONTAINER(main_box), main_lb);
+ g_object_set_data(G_OBJECT(win), "splash_label", main_lb);
+
+ main_lb = gtk_label_new("");
+ gtk_container_add(GTK_CONTAINER(main_box), main_lb);
+ g_object_set_data(G_OBJECT(win), "protocol_label", main_lb);
+
+ percentage_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 1);
+ gtk_box_pack_start(GTK_BOX(main_box), percentage_box, TRUE, TRUE, 3);
+
+ prog_bar = gtk_progress_bar_new();
+ gtk_box_pack_start(GTK_BOX(percentage_box), prog_bar, TRUE, TRUE, 3);
+ g_object_set_data(G_OBJECT(win), "progress_bar", prog_bar);
+
+ percentage_lb = gtk_label_new(" 0%");
+ gtk_misc_set_alignment(GTK_MISC(percentage_lb), 0.0f, 0.0f);
+ gtk_box_pack_start(GTK_BOX(percentage_box), percentage_lb, FALSE, TRUE, 3);
+ g_object_set_data(G_OBJECT(win), "percentage_label", percentage_lb);
+
+ gtk_widget_show_all(win);
+
+ splash_update_label(win, message);
+
+ return win;
+}
+#else
+GtkWidget*
+splash_new(const char *message)
+{
+ GtkWidget *win;
+ GtkWidget *main_lb;
+
+ GtkWidget *main_vb;
+ GtkWidget *percentage_hb;
+ GtkWidget *prog_bar;
+ GtkWidget *percentage_lb;
+
+ win = splash_window_new();
+
+ /* When calling about_wireshark(), we must realize the top-level
+ widget for the window, otherwise GTK will throw a warning
+ because we don't have a colormap associated with that window and
+ can't handle the pixmap. */
+ gtk_widget_realize(win);
+
+ main_vb = gtk_vbox_new(FALSE, 6);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 24);
+ gtk_container_add(GTK_CONTAINER(win), main_vb);
+
+ about_wireshark(win, main_vb);
+
+ main_lb = gtk_label_new(message);
+ gtk_container_add(GTK_CONTAINER(main_vb), main_lb);
+ g_object_set_data(G_OBJECT(win), "splash_label", main_lb);
+
+ main_lb = gtk_label_new("");
+ gtk_container_add(GTK_CONTAINER(main_vb), main_lb);
+ g_object_set_data(G_OBJECT(win), "protocol_label", main_lb);
+
+ percentage_hb = gtk_hbox_new(FALSE, 1);
+ gtk_box_pack_start(GTK_BOX(main_vb), percentage_hb, TRUE, TRUE, 3);
+
+ prog_bar = gtk_progress_bar_new();
+ gtk_box_pack_start(GTK_BOX(percentage_hb), prog_bar, TRUE, TRUE, 3);
+ g_object_set_data(G_OBJECT(win), "progress_bar", prog_bar);
+
+ percentage_lb = gtk_label_new(" 0%");
+ gtk_misc_set_alignment(GTK_MISC(percentage_lb), 0.0f, 0.0f);
+ gtk_box_pack_start(GTK_BOX(percentage_hb), percentage_lb, FALSE, TRUE, 3);
+ g_object_set_data(G_OBJECT(win), "percentage_label", percentage_lb);
+
+ gtk_widget_show_all(win);
+
+ splash_update_label(win, message);
+
+ return win;
+}
+#endif /* GTK_CHECK_VERSION(3,0,0) */
+
+void
+splash_update(register_action_e action, const char *message, gpointer client_data)
+{
+ GtkWidget *win;
+ GtkWidget *main_lb;
+ GtkWidget *prog_bar;
+ GtkWidget *percentage_lb;
+ gfloat percentage;
+ gulong ul_percentage;
+ gchar tmp[100];
+ const char *action_msg;
+
+ static gulong ul_sofar = 0;
+ static gulong ul_count = 0;
+
+ static register_action_e last_action = RA_NONE;
+
+ static GTimeVal cur_tv;
+ static GTimeVal next_tv = {0, 0};
+
+ win = (GtkWidget *)client_data;
+
+ if (win == NULL) return;
+
+ g_get_current_time(&cur_tv);
+ if (last_action == action && cur_tv.tv_sec <= next_tv.tv_sec && cur_tv.tv_usec <= next_tv.tv_usec && ul_sofar < ul_count - 1) {
+ /* Only update every splash_register_freq milliseconds */
+ ul_sofar++;
+ return;
+ }
+ memcpy(&next_tv, &cur_tv, sizeof(next_tv));
+ next_tv.tv_usec += info_update_freq * 1000;
+ if (next_tv.tv_usec >= 1000000) {
+ next_tv.tv_sec++;
+ next_tv.tv_usec -= 1000000;
+ }
+
+ if(last_action != action) {
+ /* the action has changed */
+ switch(action) {
+ case RA_DISSECTORS:
+ action_msg = "Initializing dissectors ...";
+ break;
+ case RA_LISTENERS:
+ action_msg = "Initializing tap listeners ...";
+ break;
+ case RA_REGISTER:
+ action_msg = "Registering dissector ...";
+ break;
+ case RA_PLUGIN_REGISTER:
+ action_msg = "Registering plugins ...";
+ break;
+ case RA_PYTHON_REGISTER:
+ action_msg = "Registering Python dissectors ...";
+ break;
+ case RA_HANDOFF:
+ action_msg = "Handing off dissector ...";
+ break;
+ case RA_PLUGIN_HANDOFF:
+ action_msg = "Handing off plugins ...";
+ break;
+ case RA_PYTHON_HANDOFF:
+ action_msg = "Handing off Python dissectors ...";
+ break;
+ case RA_LUA_PLUGINS:
+ action_msg = "Loading Lua plugins ...";
+ break;
+ case RA_PREFERENCES:
+ action_msg = "Loading module preferences ...";
+ break;
+ case RA_CONFIGURATION:
+ action_msg = "Loading configuration files ...";
+ break;
+ default:
+ action_msg = "(Unknown action)";;
+ break;
+ }
+ splash_update_label(win, action_msg);
+ last_action = action;
+ }
+
+ if(ul_count == 0) { /* get the count of dissectors */
+ ul_count = register_count() + 6; /* additional 6 for:
+ dissectors, listeners,
+ registering plugins, handingoff plugins,
+ preferences and configuration */
+#ifdef HAVE_LUA_5_1
+ ul_count++; /* additional one for lua plugins */
+#endif
+#ifdef HAVE_PYTHON
+ ul_count += 2; /* additional 2 for python register and handoff */
+#endif
+ }
+
+ main_lb = (GtkWidget *)g_object_get_data(G_OBJECT(win), "protocol_label");
+ /* make_dissector_reg.py changed -
+ so we need to strip off the leading elements to get back to the protocol */
+ if(message) {
+ if(!strncmp(message, "proto_register_", 15))
+ message += 15;
+ else if(!strncmp(message, "proto_reg_handoff_", 18))
+ message += 18;
+ }
+ gtk_label_set_text(GTK_LABEL(main_lb), message ? message : "");
+
+ ul_sofar++;
+
+ g_assert (ul_sofar <= ul_count);
+
+ percentage = (gfloat)ul_sofar/(gfloat)ul_count;
+ ul_percentage = (gulong)(percentage * 100);
+
+ /* update progress bar */
+ prog_bar = (GtkWidget *)g_object_get_data(G_OBJECT(win), "progress_bar");
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(prog_bar), percentage);
+
+ percentage_lb = g_object_get_data(G_OBJECT(win), "percentage_label");
+ g_snprintf(tmp, sizeof(tmp), "%lu%%", ul_percentage);
+ gtk_label_set_text((GtkLabel*)percentage_lb, tmp);
+
+ /* Process all pending GUI events before continuing, so that
+ the splash screen window gets updated. */
+ while (gtk_events_pending()) gtk_main_iteration();
+
+}
+
+gboolean
+splash_destroy(GtkWidget *win)
+{
+ if (win == NULL)
+ return FALSE;
+
+ gtk_widget_destroy(win);
+ return FALSE;
+}
+
+#if GTK_CHECK_VERSION(3,0,0)
+static GtkWidget *
+about_wireshark_page_new(void)
+{
+ GtkWidget *main_box, *msg_label /*, *icon*/;
+ gchar *message;
+
+ main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
+ gtk_container_set_border_width(GTK_CONTAINER(main_box), 12);
+
+ g_object_set(gtk_widget_get_settings(main_box),
+ "gtk-label-select-on-focus", FALSE, NULL);
+
+ about_wireshark(top_level, main_box);
+
+ /* Construct the message string */
+ message = g_strdup_printf(
+ "Version " VERSION "%s\n"
+ "\n"
+ "%s"
+ "\n"
+ "%s"
+ "\n"
+ "%s"
+ "\n"
+ "Wireshark is Open Source Software released under the GNU General Public License.\n"
+ "\n"
+ "Check the man page and http://www.wireshark.org for more information.",
+ wireshark_svnversion, get_copyright_info(), comp_info_str->str,
+ runtime_info_str->str);
+
+ msg_label = gtk_label_new(message);
+ g_free(message);
+ gtk_label_set_justify(GTK_LABEL(msg_label), GTK_JUSTIFY_FILL);
+ gtk_label_set_selectable(GTK_LABEL(msg_label), TRUE);
+ gtk_container_add(GTK_CONTAINER(main_box), msg_label);
+
+ return main_box;
+}
+#else
+static GtkWidget *
+about_wireshark_page_new(void)
+{
+ GtkWidget *main_vb, *msg_label /*, *icon*/;
+ gchar *message;
+
+ main_vb = gtk_vbox_new(FALSE, 6);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 12);
+
+ g_object_set(gtk_widget_get_settings(main_vb),
+ "gtk-label-select-on-focus", FALSE, NULL);
+
+ about_wireshark(top_level, main_vb);
+
+ /* Construct the message string */
+ message = g_strdup_printf(
+ "Version " VERSION "%s\n"
+ "\n"
+ "%s"
+ "\n"
+ "%s"
+ "\n"
+ "%s"
+ "\n"
+ "Wireshark is Open Source Software released under the GNU General Public License.\n"
+ "\n"
+ "Check the man page and http://www.wireshark.org for more information.",
+ wireshark_svnversion, get_copyright_info(), comp_info_str->str,
+ runtime_info_str->str);
+
+ msg_label = gtk_label_new(message);
+ g_free(message);
+ gtk_label_set_justify(GTK_LABEL(msg_label), GTK_JUSTIFY_FILL);
+ gtk_label_set_selectable(GTK_LABEL(msg_label), TRUE);
+ gtk_container_add(GTK_CONTAINER(main_vb), msg_label);
+
+ return main_vb;
+}
+#endif /* GTK_CHECK_VERSION(3,0,0) */
+static GtkWidget *
+about_authors_page_new(void)
+{
+ GtkWidget *page;
+ char *absolute_path;
+
+ absolute_path = get_datafile_path("AUTHORS-SHORT");
+ page = text_page_new(absolute_path);
+
+ return page;
+}
+
+static gboolean about_folders_callback(GtkWidget *widget, GdkEventButton *event, gint id _U_)
+{
+ GtkTreeSelection *tree_selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gchar *path;
+
+ tree_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
+
+ if(gtk_tree_selection_count_selected_rows(tree_selection) == 0)
+ return FALSE;
+
+ if(event->type != GDK_2BUTTON_PRESS)
+ /* not a double click */
+ return FALSE;
+
+ if(gtk_tree_selection_get_selected (tree_selection, &model, &iter)) {
+ gtk_tree_model_get(model, &iter, 1, &path, -1);
+ filemanager_open_directory(path);
+ g_free(path);
+ }
+
+ return TRUE;
+}
+
+static void
+about_folders_row(GtkWidget *table, const char *label, const char *dir, const char *tip)
+{
+ simple_list_append(table, 0, label, 1, dir, 2, tip, -1);
+}
+
+static GtkWidget *
+about_folders_page_new(void)
+{
+ GtkWidget *table;
+ const char *constpath;
+ char *path;
+ const gchar *titles[] = { "Name", "Folder", "Typical Files"};
+ GtkWidget *scrolledwindow;
+#if defined (HAVE_LIBSMI) || defined (HAVE_GEOIP)
+ gint i;
+ gchar **resultArray;
+#endif
+
+ scrolledwindow = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwindow),
+ GTK_SHADOW_IN);
+
+ /* Container for our data */
+ table = simple_list_new(3, titles);
+
+ /* connect a callback so we can spot a double-click */
+ g_signal_connect(table, "button_press_event",
+ G_CALLBACK(about_folders_callback), NULL);
+
+ simple_list_url_col(table, 1);
+
+ /* "file open" */
+ about_folders_row(table, "\"File\" dialogs", get_last_open_dir(),
+ "capture files");
+
+ /* temp */
+ path = get_tempfile_path("");
+ about_folders_row(table, "Temp", path,
+ "untitled capture files");
+ g_free(path);
+
+ /* pers conf */
+ path = get_persconffile_path("", FALSE, FALSE);
+ about_folders_row(table, "Personal configuration", path,
+ "\"dfilters\", \"preferences\", \"ethers\", ...");
+ g_free(path);
+
+ /* global conf */
+ constpath = get_datafile_dir();
+ if (constpath != NULL) {
+ about_folders_row(table, "Global configuration", constpath,
+ "\"dfilters\", \"preferences\", \"manuf\", ...");
+ }
+
+ /* system */
+ constpath = get_systemfile_dir();
+ about_folders_row(table, "System", constpath,
+ "\"ethers\", \"ipxnets\"");
+
+ /* program */
+ constpath = get_progfile_dir();
+ about_folders_row(table, "Program", constpath,
+ "program files");
+
+#if defined(HAVE_PLUGINS) || defined(HAVE_LUA_5_1)
+ /* pers plugins */
+ path = get_plugins_pers_dir();
+ about_folders_row(table, "Personal Plugins", path,
+ "dissector plugins");
+ g_free(path);
+
+ /* global plugins */
+ about_folders_row(table, "Global Plugins", get_plugin_dir(),
+ "dissector plugins");
+#endif
+
+#ifdef HAVE_PYTHON
+ /* global python bindings */
+ about_folders_row(table, "Python Bindings", get_wspython_dir(),
+ "python bindings");
+#endif
+
+#ifdef HAVE_GEOIP
+ /* GeoIP */
+ path = geoip_db_get_paths();
+
+ resultArray = g_strsplit(path, G_SEARCHPATH_SEPARATOR_S, 10);
+
+ for(i = 0; resultArray[i]; i++)
+ about_folders_row(table, "GeoIP path", g_strstrip(resultArray[i]),
+ "GeoIP database search path");
+ g_strfreev(resultArray);
+ g_free(path);
+#endif
+
+#ifdef HAVE_LIBSMI
+ /* SMI MIBs/PIBs */
+ path = oid_get_default_mib_path();
+
+ resultArray = g_strsplit(path, G_SEARCHPATH_SEPARATOR_S, 10);
+
+ for(i = 0; resultArray[i]; i++)
+ about_folders_row(table, "MIB/PIB path", g_strstrip(resultArray[i]),
+ "SMI MIB/PIB search path");
+ g_strfreev(resultArray);
+ g_free(path);
+#endif
+
+ gtk_container_add(GTK_CONTAINER(scrolledwindow), table);
+
+ return scrolledwindow;
+}
+
+static GtkWidget *
+about_license_page_new(void)
+{
+ GtkWidget *page;
+ char *absolute_path;
+
+#if defined(_WIN32)
+ absolute_path = get_datafile_path("COPYING.txt");
+#else
+ absolute_path = get_datafile_path("COPYING");
+#endif
+ page = text_page_new(absolute_path);
+
+ return page;
+}
+
+#if GTK_CHECK_VERSION(3,0,0)
+void
+about_wireshark_cb( GtkWidget *w _U_, gpointer data _U_ )
+{
+ GtkWidget *main_box, *main_nb, *bbox, *ok_btn;
+ GtkWidget *page_lb, *about_page, *folders_page;
+
+#if defined(HAVE_PLUGINS) || defined(HAVE_LUA_5_1)
+ GtkWidget *plugins_page;
+#endif
+
+ GtkWidget *authors_page, *license_page;
+
+ if (about_wireshark_w != NULL) {
+ /* There's already an "About Wireshark" dialog box; reactivate it. */
+ reactivate_window(about_wireshark_w);
+ return;
+ }
+
+ /*
+ * XXX - use GtkDialog? The GNOME 2.x GnomeAbout widget does.
+ * Should we use GtkDialog for simple_dialog() as well? Or
+ * is the GTK+ 2.x GtkDialog appropriate but the 1.2[.x] one
+ * not? (The GNOME 1.x GnomeAbout widget uses GnomeDialog.)
+ */
+ about_wireshark_w = dlg_window_new("About Wireshark");
+ /* set the initial position (must be done, before show is called!) */
+ /* default position is not appropriate for the about dialog */
+ gtk_window_set_position(GTK_WINDOW(about_wireshark_w), GTK_WIN_POS_CENTER_ON_PARENT);
+ gtk_window_set_default_size(GTK_WINDOW(about_wireshark_w), 600, 400);
+ gtk_container_set_border_width(GTK_CONTAINER(about_wireshark_w), 6);
+
+ main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width(GTK_CONTAINER(main_box), 6);
+ gtk_container_add(GTK_CONTAINER(about_wireshark_w), main_box);
+
+ main_nb = gtk_notebook_new();
+ gtk_box_pack_start(GTK_BOX(main_box), main_nb, TRUE, TRUE, 0);
+
+ about_page = about_wireshark_page_new();
+ page_lb = gtk_label_new("Wireshark");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), about_page, page_lb);
+
+ authors_page = about_authors_page_new();
+ page_lb = gtk_label_new("Authors");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), authors_page, page_lb);
+
+ folders_page = about_folders_page_new();
+ page_lb = gtk_label_new("Folders");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), folders_page, page_lb);
+
+#if defined(HAVE_PLUGINS) || defined(HAVE_LUA_5_1)
+ plugins_page = about_plugins_page_new();
+ page_lb = gtk_label_new("Plugins");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), plugins_page, page_lb);
+#endif
+
+ license_page = about_license_page_new();
+ page_lb = gtk_label_new("License");
+ /* set a minmum width to avoid a lot of line breaks at the wrong places */
+ gtk_widget_set_size_request(license_page, 600, -1);
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), license_page, page_lb);
+
+ /* Button row */
+ bbox = dlg_button_row_new(GTK_STOCK_OK, NULL);
+ gtk_box_pack_start(GTK_BOX(main_box), bbox, FALSE, FALSE, 0);
+
+ ok_btn = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ gtk_widget_grab_focus(ok_btn);
+ gtk_widget_grab_default(ok_btn);
+ window_set_cancel_button(about_wireshark_w, ok_btn, window_cancel_button_cb);
+
+ g_signal_connect(about_wireshark_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(about_wireshark_w, "destroy", G_CALLBACK(about_wireshark_destroy_cb), NULL);
+
+ gtk_widget_show_all(about_wireshark_w);
+ window_present(about_wireshark_w);
+}
+#else
+void
+about_wireshark_cb( GtkWidget *w _U_, gpointer data _U_ )
+{
+ GtkWidget *main_vb, *main_nb, *bbox, *ok_btn;
+ GtkWidget *page_lb, *about_page, *folders_page;
+
+#if defined(HAVE_PLUGINS) || defined(HAVE_LUA_5_1)
+ GtkWidget *plugins_page;
+#endif
+
+ GtkWidget *authors_page, *license_page;
+
+ if (about_wireshark_w != NULL) {
+ /* There's already an "About Wireshark" dialog box; reactivate it. */
+ reactivate_window(about_wireshark_w);
+ return;
+ }
+
+ /*
+ * XXX - use GtkDialog? The GNOME 2.x GnomeAbout widget does.
+ * Should we use GtkDialog for simple_dialog() as well? Or
+ * is the GTK+ 2.x GtkDialog appropriate but the 1.2[.x] one
+ * not? (The GNOME 1.x GnomeAbout widget uses GnomeDialog.)
+ */
+ about_wireshark_w = dlg_window_new("About Wireshark");
+ /* set the initial position (must be done, before show is called!) */
+ /* default position is not appropriate for the about dialog */
+ gtk_window_set_position(GTK_WINDOW(about_wireshark_w), GTK_WIN_POS_CENTER_ON_PARENT);
+ gtk_window_set_default_size(GTK_WINDOW(about_wireshark_w), 600, 400);
+ gtk_container_set_border_width(GTK_CONTAINER(about_wireshark_w), 6);
+
+ main_vb = gtk_vbox_new(FALSE, 12);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 6);
+ gtk_container_add(GTK_CONTAINER(about_wireshark_w), main_vb);
+
+ main_nb = gtk_notebook_new();
+ gtk_box_pack_start(GTK_BOX(main_vb), main_nb, TRUE, TRUE, 0);
+
+ about_page = about_wireshark_page_new();
+ page_lb = gtk_label_new("Wireshark");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), about_page, page_lb);
+
+ authors_page = about_authors_page_new();
+ page_lb = gtk_label_new("Authors");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), authors_page, page_lb);
+
+ folders_page = about_folders_page_new();
+ page_lb = gtk_label_new("Folders");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), folders_page, page_lb);
+
+#if defined(HAVE_PLUGINS) || defined(HAVE_LUA_5_1)
+ plugins_page = about_plugins_page_new();
+ page_lb = gtk_label_new("Plugins");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), plugins_page, page_lb);
+#endif
+
+ license_page = about_license_page_new();
+ page_lb = gtk_label_new("License");
+ /* set a minmum width to avoid a lot of line breaks at the wrong places */
+ gtk_widget_set_size_request(license_page, 600, -1);
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), license_page, page_lb);
+
+ /* Button row */
+ bbox = dlg_button_row_new(GTK_STOCK_OK, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+
+ ok_btn = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ gtk_widget_grab_focus(ok_btn);
+ gtk_widget_grab_default(ok_btn);
+ window_set_cancel_button(about_wireshark_w, ok_btn, window_cancel_button_cb);
+
+ g_signal_connect(about_wireshark_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(about_wireshark_w, "destroy", G_CALLBACK(about_wireshark_destroy_cb), NULL);
+
+ gtk_widget_show_all(about_wireshark_w);
+ window_present(about_wireshark_w);
+}
+#endif /*GTK_CHECK_VERSION(3,0,0)*/
+
+static void
+about_wireshark_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+ /* Note that we no longer have an "About Wireshark" dialog box. */
+ about_wireshark_w = NULL;
+}
diff --git a/ui/gtk/about_dlg.h b/ui/gtk/about_dlg.h
new file mode 100644
index 0000000000..b36d7bf0b0
--- /dev/null
+++ b/ui/gtk/about_dlg.h
@@ -0,0 +1,63 @@
+/* about_dlg.h
+ * Declarations of routines for the "About" dialog
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ABOUT_DLG_H__
+#define __ABOUT_DLG_H__
+
+/** @file
+ * "About" dialog box.
+ * @ingroup dialog_group
+ */
+
+/** Create a splash screen showed when Wireshark is started.
+ *
+ * @param message the new message to be displayed
+ * @return the newly created window handle
+ */
+extern GtkWidget *splash_new(const char *message);
+
+/** Update the splash screen message when loading dissectors and handoffs
+ *
+ * @param action the initialisation action being performed
+ * @param message additional information
+ * @param call_data the window handle from splash_new()
+ */
+extern void splash_update(register_action_e action, const char *message, void *call_data);
+
+/** Destroy the splash screen.
+ *
+ * @param win the window handle from splash_new()
+ * @return always FALSE, so this function can be used as a callback for gtk_timeout_add()
+ */
+extern gboolean splash_destroy(GtkWidget *win);
+
+/** User requested the "About" dialog box by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void about_wireshark_cb( GtkWidget *widget, gpointer data);
+
+
+#endif /* __ABOUT_DLG_H__ */
diff --git a/ui/gtk/afp_stat.c b/ui/gtk/afp_stat.c
new file mode 100644
index 0000000000..f0da6e1d6e
--- /dev/null
+++ b/ui/gtk/afp_stat.c
@@ -0,0 +1,224 @@
+/* afp_stat.c
+ * Based on
+ * smb_stat 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/value_string.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-afp.h>
+
+#include "../timestats.h"
+#include "../simple_dialog.h"
+#include "../file.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/service_response_time_table.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/main.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _afpstat_t {
+ GtkWidget *win;
+ srt_stat_table afp_srt_table;
+} afpstat_t;
+
+static void
+afpstat_set_title(afpstat_t *ss)
+{
+ char *title;
+
+ title = g_strdup_printf("AFP Service Response Time statistics: %s",
+ cf_get_display_name(&cfile));
+ gtk_window_set_title(GTK_WINDOW(ss->win), title);
+ g_free(title);
+}
+
+static void
+afpstat_reset(void *pss)
+{
+ afpstat_t *ss=(afpstat_t *)pss;
+
+ reset_srt_table_data(&ss->afp_srt_table);
+ afpstat_set_title(ss);
+}
+
+static int
+afpstat_packet(void *pss, packet_info *pinfo, epan_dissect_t *edt _U_, const void *prv)
+{
+ afpstat_t *ss=(afpstat_t *)pss;
+ const afp_request_val *request_val=prv;
+
+ /* if we havnt seen the request, just ignore it */
+ if(!request_val){
+ return 0;
+ }
+
+ add_srt_table_data(&ss->afp_srt_table, request_val->command, &request_val->req_time, pinfo);
+
+ return 1;
+}
+
+
+
+static void
+afpstat_draw(void *pss)
+{
+ afpstat_t *ss=(afpstat_t *)pss;
+
+ draw_srt_table_data(&ss->afp_srt_table);
+}
+
+
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ afpstat_t *ss=(afpstat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(ss);
+ unprotect_thread_critical_region();
+
+ free_srt_table_data(&ss->afp_srt_table);
+ g_free(ss);
+}
+
+
+static void
+gtk_afpstat_init(const char *optarg, void *userdata _U_)
+{
+ afpstat_t *ss;
+ const char *filter=NULL;
+ GtkWidget *label;
+ char *filter_string;
+ GString *error_string;
+ int i;
+ GtkWidget *vbox;
+ GtkWidget *bbox;
+ GtkWidget *close_bt;
+
+ if(!strncmp(optarg,"afp,srt,",8)){
+ filter=optarg+8;
+ } else {
+ filter=NULL;
+ }
+
+ ss=g_malloc(sizeof(afpstat_t));
+
+ ss->win=dlg_window_new("afp-stat"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(ss->win), TRUE);
+ gtk_window_set_default_size(GTK_WINDOW(ss->win), 550, 600);
+ afpstat_set_title(ss);
+
+ vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(ss->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ label=gtk_label_new("AFP Service Response Time statistics");
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ filter_string = g_strdup_printf("Filter: %s", filter ? filter : "");
+ label=gtk_label_new(filter_string);
+ gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+ g_free(filter_string);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ label=gtk_label_new("AFP Commands");
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ /* We must display TOP LEVEL Widget before calling init_srt_table() */
+ gtk_widget_show_all(ss->win);
+
+ init_srt_table(&ss->afp_srt_table, 256, vbox, "afp.command");
+ for(i=0;i<256;i++){
+ init_srt_table_row(&ss->afp_srt_table, i, val_to_str_ext(i, &CommandCode_vals_ext, "Unknown(%u)"));
+ }
+
+
+ error_string=register_tap_listener("afp", ss, filter, 0, afpstat_reset, afpstat_packet, afpstat_draw);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(ss);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(ss->win, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(ss->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(ss->win, "destroy", G_CALLBACK(win_destroy_cb), ss);
+
+ gtk_widget_show_all(ss->win);
+ window_present(ss->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(ss->win));
+}
+
+static tap_param afp_stat_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg afp_stat_dlg = {
+ "AFP SRT Statistics",
+ "afp,srt",
+ gtk_afpstat_init,
+ -1,
+ G_N_ELEMENTS(afp_stat_params),
+ afp_stat_params
+};
+
+void
+register_tap_listener_gtkafpstat(void)
+{
+ register_dfilter_stat(&afp_stat_dlg, "AFP",
+ REGISTER_STAT_GROUP_RESPONSE_TIME);
+}
+
+void afp_srt_stat_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &afp_stat_dlg);
+}
+
diff --git a/ui/gtk/airpcap_dlg.c b/ui/gtk/airpcap_dlg.c
new file mode 100644
index 0000000000..0ab3d717fb
--- /dev/null
+++ b/ui/gtk/airpcap_dlg.c
@@ -0,0 +1,2911 @@
+/* airpcap_dlg.c
+ *
+ * $Id$
+ *
+ * Giorgio Tino <giorgio.tino@cacetech.com>
+ * Copyright (c) CACE Technologies, LLC 2006
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2000 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_AIRPCAP
+
+#include <gtk/gtk.h>
+/*#include <glib/gprintf.h> */
+
+#include <string.h>
+
+#include <epan/filesystem.h>
+#include <epan/emem.h>
+#include <epan/prefs.h>
+#include <epan/prefs-int.h>
+#include <epan/frequency-utils.h>
+#include <epan/crypt/wep-wpadefs.h>
+
+#include <pcap.h>
+
+#include "../simple_dialog.h"
+
+#include "ui/gtk/main.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dfilter_expr_dlg.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/old-gtk-compat.h"
+
+#include <airpcap.h>
+#include "airpcap_loader.h"
+#include "airpcap_gui_utils.h"
+#include "airpcap_dlg.h"
+
+/*
+ * This structure is used because we need to store infos about the currently selected
+ * row in the key list.
+ */
+typedef struct{
+ gint row;
+}airpcap_key_ls_selected_info_t;
+
+/*
+ * This function is used to write the preferences to the preferences file.
+ * It has the same behaviour as prefs_main_write() in prefs_dlg.c
+ */
+static void
+write_prefs_to_file(void)
+{
+ int err;
+ char *pf_dir_path;
+ char *pf_path;
+
+ /* Create the directory that holds personal configuration files, if
+ necessary. */
+ if (create_persconffile_dir(&pf_dir_path) == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't create directory\n\"%s\"\nfor preferences file: %s.", pf_dir_path,
+ g_strerror(errno));
+ g_free(pf_dir_path);
+ } else {
+ /* Write the preferencs out. */
+ err = write_prefs(&pf_path);
+ if (err != 0) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't open preferences file\n\"%s\": %s.", pf_path,
+ g_strerror(err));
+ g_free(pf_path);
+ }
+ }
+}
+
+/*
+ * Callback for the select row event in the key list widget
+ */
+static void
+on_key_list_select_row(GtkTreeSelection *selection, gpointer data)
+{
+ GtkWidget *add_new_key_bt, *edit_key_bt, *remove_key_bt;
+ GtkWidget *move_key_up_bt, *move_key_down_bt;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreePath *path, *path_up, *path_down;
+
+ add_new_key_bt = g_object_get_data(G_OBJECT(data), AIRPCAP_KEY_MGMT_NEW_KEY);
+ edit_key_bt = g_object_get_data(G_OBJECT(data), AIRPCAP_KEY_MGMT_EDIT_KEY);
+ remove_key_bt = g_object_get_data(G_OBJECT(data), AIRPCAP_KEY_MGMT_DELETE_KEY);
+ move_key_up_bt = g_object_get_data(G_OBJECT(data), AIRPCAP_KEY_MGMT_UP_KEY);
+ move_key_down_bt = g_object_get_data(G_OBJECT(data), AIRPCAP_KEY_MGMT_DOWN_KEY);
+
+ if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
+ path = gtk_tree_model_get_path(model, &iter);
+ path_up = gtk_tree_path_copy(path);
+ path_down = gtk_tree_path_copy(path);
+ gtk_tree_path_next(path_down);
+
+ if (gtk_tree_model_iter_n_children(model, NULL) >= MAX_ENCRYPTION_KEYS) {
+ gtk_widget_set_sensitive(add_new_key_bt, FALSE);
+ } else {
+ gtk_widget_set_sensitive(add_new_key_bt, TRUE);
+ }
+
+ gtk_widget_set_sensitive(edit_key_bt, TRUE);
+ gtk_widget_set_sensitive(remove_key_bt, TRUE);
+
+ /* ...and we have to use two different methods to figure out first/last because? */
+ if (gtk_tree_path_prev(path_up)) {
+ gtk_widget_set_sensitive(move_key_up_bt, TRUE);
+ } else {
+ gtk_widget_set_sensitive(move_key_up_bt, FALSE);
+ }
+
+ if (gtk_tree_model_get_iter(model, &iter, path_down)) {
+ gtk_widget_set_sensitive(move_key_down_bt, TRUE);
+ } else {
+ gtk_widget_set_sensitive(move_key_down_bt, FALSE);
+ }
+
+ gtk_tree_path_free(path);
+ gtk_tree_path_free(path_up);
+ gtk_tree_path_free(path_down);
+ } else {
+ gtk_widget_set_sensitive(add_new_key_bt, FALSE);
+ gtk_widget_set_sensitive(edit_key_bt, FALSE);
+ gtk_widget_set_sensitive(remove_key_bt, FALSE);
+ gtk_widget_set_sensitive(move_key_up_bt, FALSE);
+ gtk_widget_set_sensitive(move_key_down_bt, FALSE);
+ }
+}
+/*
+ * Callback for the select row event in the key list widget
+ */
+static void
+on_key_list_reorder(GtkTreeModel *model _U_, GtkTreePath *path _U_, GtkTreeIter *iter _U_, gpointer no _U_, gpointer data) {
+ GtkTreeSelection *selection;
+
+ selection = g_object_get_data(G_OBJECT(data), AIRPCAP_ADVANCED_EDIT_KEY_SELECTION_KEY);
+ on_key_list_select_row(selection, data);
+}
+
+/*
+ * Callback for the crc checkbox
+ */
+static void
+on_fcs_ck_toggled(GtkWidget *w _U_, gpointer user_data _U_)
+
+{
+ if (airpcap_if_selected != NULL)
+ {
+ if (airpcap_if_selected->IsFcsPresent)
+ {
+ airpcap_if_selected->IsFcsPresent = FALSE;
+ airpcap_if_selected->saved = FALSE;
+ }
+ else
+ {
+ airpcap_if_selected->IsFcsPresent = TRUE;
+ airpcap_if_selected->saved = FALSE;
+ }
+ }
+}
+
+/*
+ * Callback for the wrong crc combo
+ */
+static void
+on_edit_type_cb_changed(GtkWidget *w, gpointer data)
+{
+ GtkWidget *edit_key_w;
+ GtkWidget *edit_ssid_te;
+ GtkWidget *type_cb;
+ GtkWidget *key_lb;
+ GtkWidget *ssid_lb;
+
+ gchar* type_text = NULL;
+
+ edit_key_w = GTK_WIDGET(data);
+ type_cb = w;
+
+ edit_ssid_te = g_object_get_data(G_OBJECT(edit_key_w),AIRPCAP_ADVANCED_EDIT_KEY_SSID_KEY);
+ key_lb = g_object_get_data(G_OBJECT(edit_key_w),AIRPCAP_ADVANCED_EDIT_KEY_KEY_LABEL_KEY);
+ ssid_lb = g_object_get_data(G_OBJECT(edit_key_w),AIRPCAP_ADVANCED_EDIT_KEY_SSID_LABEL_KEY);
+
+ type_text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(type_cb));
+
+ if (g_ascii_strcasecmp(type_text, ""))
+ {
+ /*
+ * If it is a WEP key, no SSID is required! Gray out the entry text so
+ * it doesn't create confusion ...
+ */
+ if (g_ascii_strcasecmp(type_text,AIRPCAP_WEP_KEY_STRING) == 0)
+ {
+ gtk_widget_set_sensitive(edit_ssid_te,FALSE);
+ /*
+ * Maybe the user has already entered some text into the SSID field
+ * and then switched to WEP...
+ */
+ gtk_entry_set_text(GTK_ENTRY(edit_ssid_te),"");
+ gtk_label_set_text(GTK_LABEL(key_lb),"Key");
+ gtk_label_set_text(GTK_LABEL(ssid_lb),"");
+ }
+ else if (g_ascii_strcasecmp(type_text,AIRPCAP_WPA_BIN_KEY_STRING) == 0)
+ {
+ gtk_widget_set_sensitive(edit_ssid_te,FALSE);
+ /*
+ * Maybe the user has already entered some text into the SSID field
+ * and then switched to WPA...
+ */
+ gtk_entry_set_text(GTK_ENTRY(edit_ssid_te),"");
+ gtk_label_set_text(GTK_LABEL(key_lb),"Key");
+ gtk_label_set_text(GTK_LABEL(ssid_lb),"");
+ }
+ else if (g_ascii_strcasecmp(type_text,AIRPCAP_WPA_PWD_KEY_STRING) == 0)
+ {
+ gtk_widget_set_sensitive(edit_ssid_te,TRUE);
+ /*
+ * Maybe the user has already entered some text into the SSID field
+ * and then switched to WPA...
+ */
+ gtk_entry_set_text(GTK_ENTRY(edit_ssid_te),"");
+ gtk_label_set_text(GTK_LABEL(key_lb),"Passphrase");
+ gtk_label_set_text(GTK_LABEL(ssid_lb),"SSID");
+ }
+ }
+ gtk_widget_show(edit_ssid_te);
+
+ g_free(type_text);
+}
+
+/*
+ * Callback for the wrong crc combo
+ */
+static void
+on_add_type_cb_changed(GtkWidget *w, gpointer data)
+{
+ GtkWidget *add_key_w;
+ GtkWidget *add_ssid_te;
+ GtkWidget *type_cb;
+ GtkWidget *key_lb;
+ GtkWidget *ssid_lb;
+
+ gchar* type_text = NULL;
+
+ add_key_w = GTK_WIDGET(data);
+ type_cb = w;
+
+ add_ssid_te = g_object_get_data(G_OBJECT(add_key_w),AIRPCAP_ADVANCED_ADD_KEY_SSID_KEY);
+ key_lb = g_object_get_data(G_OBJECT(add_key_w),AIRPCAP_ADVANCED_ADD_KEY_KEY_LABEL_KEY);
+ ssid_lb = g_object_get_data(G_OBJECT(add_key_w),AIRPCAP_ADVANCED_ADD_KEY_SSID_LABEL_KEY);
+
+ type_text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(type_cb));
+
+ if (g_ascii_strcasecmp(type_text, ""))
+ {
+ /*
+ * If it is a WEP key, no SSID is required! Gray out rhe entry text so
+ * it doesn't create confusion ...
+ */
+ if (g_ascii_strcasecmp(type_text,AIRPCAP_WEP_KEY_STRING) == 0)
+ {
+ gtk_widget_set_sensitive(add_ssid_te,FALSE);
+ /*
+ * Maybe the user has already entered some text into the SSID field
+ * and then switched to WEP...
+ */
+ gtk_entry_set_text(GTK_ENTRY(add_ssid_te),"");
+ gtk_label_set_text(GTK_LABEL(key_lb),"Key");
+ gtk_label_set_text(GTK_LABEL(ssid_lb),"");
+ }
+ else if (g_ascii_strcasecmp(type_text,AIRPCAP_WPA_BIN_KEY_STRING) == 0)
+ {
+ gtk_widget_set_sensitive(add_ssid_te,FALSE);
+ /*
+ * Maybe the user has already entered some text into the SSID field
+ * and then switched to WPA...
+ */
+ gtk_entry_set_text(GTK_ENTRY(add_ssid_te),"");
+ gtk_label_set_text(GTK_LABEL(key_lb),"Key");
+ gtk_label_set_text(GTK_LABEL(ssid_lb),"");
+ }
+ else if (g_ascii_strcasecmp(type_text,AIRPCAP_WPA_PWD_KEY_STRING) == 0)
+ {
+ gtk_widget_set_sensitive(add_ssid_te,TRUE);
+ /*
+ * Maybe the user has already entered some text into the SSID field
+ * and then switched to WPA...
+ */
+ gtk_entry_set_text(GTK_ENTRY(add_ssid_te),"");
+ gtk_label_set_text(GTK_LABEL(key_lb),"Passphrase");
+ gtk_label_set_text(GTK_LABEL(ssid_lb),"SSID");
+ }
+ }
+ gtk_widget_show(add_ssid_te);
+
+ g_free(type_text);
+}
+
+/*
+ * Callback for the wrong crc combo
+ */
+static void
+on_fcs_filter_cb_changed(GtkWidget *fcs_filter_cb, gpointer data _U_)
+{
+ gchar *fcs_filter_str;
+
+ if (fcs_filter_cb != NULL)
+ {
+ fcs_filter_str = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(fcs_filter_cb));
+ if (fcs_filter_str && (g_ascii_strcasecmp("", fcs_filter_str)))
+ {
+ airpcap_if_selected->CrcValidationOn = airpcap_get_validation_type(fcs_filter_str);
+ airpcap_if_selected->saved = FALSE;
+ }
+ g_free(fcs_filter_str);
+ }
+}
+
+
+/*
+ * Changed callback for the capture type combobox
+ */
+static void
+on_capture_type_cb_changed(GtkWidget *cb, gpointer user_data _U_)
+{
+ gchar *s;
+
+ if (cb == NULL) {
+ return;
+ }
+
+ s = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(cb));
+
+ if ((g_ascii_strcasecmp("",s)))
+ {
+ airpcap_if_selected->linkType = airpcap_get_link_type(s);
+ airpcap_if_selected->saved = FALSE;
+ }
+ g_free(s);
+}
+
+/*
+ * Thread function used to blink the led
+ */
+static gboolean
+update_blink(gpointer data)
+{
+ airpcap_if_info_t* sel;
+ PAirpcapHandle ad;
+ gchar ebuf[AIRPCAP_ERRBUF_SIZE];
+
+ sel = (airpcap_if_info_t*)data;
+
+ ad = airpcap_if_open(sel->name, ebuf);
+ if (ad)
+ {
+ if (sel->led)
+ {
+ airpcap_if_turn_led_off(ad, 0);
+ sel->led = FALSE;
+ }
+ else
+ {
+ airpcap_if_turn_led_on(ad, 0);
+ sel->led = TRUE;
+ }
+ airpcap_if_close(ad);
+ }
+ return TRUE;
+}
+
+/*
+ * Blink button callback
+ */
+static void
+on_blink_bt_clicked( GtkWidget *blink_bt, gpointer data _U_)
+{
+ PAirpcapHandle ad = NULL;
+ gchar ebuf[AIRPCAP_ERRBUF_SIZE];
+
+ if (airpcap_if_selected != NULL) {
+ if (!(airpcap_if_selected->blinking))
+ {
+ gtk_button_set_label(GTK_BUTTON(blink_bt),"Stop Blinking");
+ airpcap_if_selected->tag = g_timeout_add(500,update_blink,airpcap_if_selected);
+ airpcap_if_selected->blinking = TRUE;
+ }
+ else
+ {
+ gtk_button_set_label(GTK_BUTTON(blink_bt)," Blink Led ");
+ g_source_remove(airpcap_if_selected->tag);
+ airpcap_if_selected->blinking = FALSE;
+ /* Switch on the led! */
+ ad = airpcap_if_open(airpcap_if_selected->name, ebuf);
+ if (ad)
+ {
+ g_source_remove(airpcap_if_selected->tag);
+ airpcap_if_turn_led_on(ad, 0);
+ airpcap_if_selected->blinking = FALSE;
+ airpcap_if_selected->led = TRUE;
+ airpcap_if_close(ad);
+ }
+ }
+ }
+}
+
+/*
+ * Callback for the 'Any' adapter What's This button.
+ */
+static void
+on_what_s_this_bt_clicked( GtkWidget *blink_bt _U_, gpointer data _U_)
+{
+ simple_dialog(ESD_TYPE_INFO,ESD_BTN_OK,
+ "The Multi-Channel Aggregator is a virtual device "
+ "that can be used to capture packets from all the "
+ "AirPcap adapters at the same time.\n"
+ "The Capture Type, FCS and Encryption settings of "
+ "this virtual device can be configured as for any "
+ "real adapter.\nThe channel cannot be changed for "
+ "this adapter.\n"
+ "Refer to the AirPcap manual for more information.");
+}
+
+/* the window was closed, cleanup things */
+static void
+on_key_management_destroy(GtkWidget *w _U_, gpointer data)
+{
+ GtkWidget *airpcap_advanced_w,
+ *toolbar;
+
+ gint *from_widget = NULL;
+
+ /* Retrieve the GUI object pointers */
+ airpcap_advanced_w = GTK_WIDGET(data);
+
+ toolbar = GTK_WIDGET(g_object_get_data(G_OBJECT(airpcap_advanced_w),AIRPCAP_TOOLBAR_KEY));
+
+ /* ... */
+ from_widget = (gint*)g_object_get_data(G_OBJECT(toolbar),AIRPCAP_ADVANCED_FROM_KEY);
+ /* gray out the toolbar (if we came here from the toolbar advanced button)*/
+ if ( *from_widget == AIRPCAP_ADVANCED_FROM_TOOLBAR)
+ gtk_widget_set_sensitive(toolbar,TRUE);
+ else
+ gtk_widget_set_sensitive(toolbar,FALSE);
+
+ g_free(from_widget);
+
+ /* reload the configuration!!! Configuration has not been saved but
+ the corresponding structure has been modified probably...*/
+ if (airpcap_if_selected != NULL)
+ {
+ if (!airpcap_if_selected->saved)
+ {
+ airpcap_load_selected_if_configuration(airpcap_if_selected);
+ }
+ }
+
+}
+
+/* the Advenced wireless Settings window was closed, cleanup things */
+static void
+on_airpcap_advanced_destroy(GtkWidget *w _U_, gpointer data)
+{
+ GtkWidget *airpcap_advanced_w,
+ *toolbar;
+
+ gint *from_widget = NULL;
+
+ /* Retrieve the GUI object pointers */
+ airpcap_advanced_w = GTK_WIDGET(data);
+
+ toolbar = GTK_WIDGET(g_object_get_data(G_OBJECT(airpcap_advanced_w),AIRPCAP_TOOLBAR_KEY));
+
+ /* ... */
+ from_widget = (gint*)g_object_get_data(G_OBJECT(toolbar),AIRPCAP_ADVANCED_FROM_KEY);
+ /* gray out the toolbar (if we came here from the toolbar advanced button)*/
+ if ( *from_widget == AIRPCAP_ADVANCED_FROM_TOOLBAR)
+ gtk_widget_set_sensitive(toolbar,TRUE);
+ else
+ gtk_widget_set_sensitive(toolbar,FALSE);
+
+ g_free(from_widget);
+
+ /* reload the configuration!!! Configuration has not been saved but
+ the corresponding structure has been modified probably...*/
+ if (!airpcap_if_selected->saved)
+ {
+ airpcap_load_selected_if_configuration(airpcap_if_selected);
+ }
+}
+
+/*
+ * Callback for the 'Apply' button.
+ */
+/*
+ * XXX - Pressing 'Apply' has the same effect as pressing 'OK' -- you
+ * can't revert back to the old set of keys by pressing 'Cancel'. We
+ * either need to fix reversion or get rid of the 'Apply' button.
+ */
+static void
+on_key_management_apply_bt_clicked(GtkWidget *button _U_, gpointer data)
+{
+ /* advenced window */
+ GtkWidget *key_management_w;
+
+ /* widgets in the toolbar */
+ GtkWidget *toolbar;
+ GtkWidget *toolbar_cb;
+ GtkWidget *decryption_mode_cb;
+
+ GtkListStore *key_list_store;
+
+ module_t *wlan_module = prefs_find_module("wlan");
+ gchar *decryption_mode_string;
+
+ /* retrieve main window */
+ key_management_w = GTK_WIDGET(data);
+ decryption_mode_cb = GTK_WIDGET(g_object_get_data(G_OBJECT(key_management_w),AIRPCAP_ADVANCED_DECRYPTION_MODE_KEY));
+ key_list_store = GTK_LIST_STORE(g_object_get_data(G_OBJECT(key_management_w),AIRPCAP_ADVANCED_KEYLIST_KEY));
+ toolbar = GTK_WIDGET(g_object_get_data(G_OBJECT(key_management_w),AIRPCAP_TOOLBAR_KEY));
+ toolbar_cb = GTK_WIDGET(g_object_get_data(G_OBJECT(key_management_w),AIRPCAP_TOOLBAR_DECRYPTION_KEY));
+
+#define CANT_SAVE_ERR_STR "Cannot save configuration! Another application " \
+ "might be using AirPcap, or you might not have sufficient privileges."
+ /* Set the Decryption Mode */
+
+ decryption_mode_string = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(decryption_mode_cb));
+ if (g_ascii_strcasecmp(decryption_mode_string, AIRPCAP_DECRYPTION_TYPE_STRING_WIRESHARK) == 0)
+ {
+ set_wireshark_decryption(TRUE);
+ if (!set_airpcap_decryption(FALSE)) g_warning(CANT_SAVE_ERR_STR);
+ }
+ else if (g_ascii_strcasecmp(decryption_mode_string, AIRPCAP_DECRYPTION_TYPE_STRING_AIRPCAP) == 0)
+ {
+ set_wireshark_decryption(FALSE);
+ if (!set_airpcap_decryption(TRUE)) g_warning(CANT_SAVE_ERR_STR);
+ }
+ else if (g_ascii_strcasecmp(decryption_mode_string, AIRPCAP_DECRYPTION_TYPE_STRING_NONE) == 0)
+ {
+ set_wireshark_decryption(FALSE);
+ if (!set_airpcap_decryption(FALSE)) g_warning(CANT_SAVE_ERR_STR);
+ }
+ g_free(decryption_mode_string);
+
+ /* Save the configuration */
+ airpcap_read_and_save_decryption_keys_from_list_store(key_list_store,airpcap_if_selected,airpcap_if_list); /* This will save the keys for every adapter */
+
+ /* The update will make redissect al the packets... no need to do it here again */
+ update_decryption_mode(toolbar_cb);
+
+ /* Redissect all the packets, and re-evaluate the display filter. */
+ prefs_apply(wlan_module);
+}
+
+/*
+ * Callback used to add a WEP key in the add new key box;
+ */
+static void
+on_add_key_ok_bt_clicked(GtkWidget *widget _U_, gpointer data)
+{
+ GtkWidget *type_cb,
+ *key_en,
+ *ssid_en;
+
+ GtkListStore *key_list_store;
+
+ GString *new_type_string,
+ *new_key_string,
+ *new_ssid_string;
+
+ gchar *type_entered = NULL;
+ gchar *key_entered = NULL;
+ gchar *ssid_entered = NULL;
+
+ unsigned int i;
+
+ key_list_store = g_object_get_data(G_OBJECT(data),AIRPCAP_ADVANCED_ADD_KEY_LIST_KEY);
+ type_cb = g_object_get_data(G_OBJECT(data),AIRPCAP_ADVANCED_ADD_KEY_TYPE_KEY);
+ key_en = g_object_get_data(G_OBJECT(data),AIRPCAP_ADVANCED_ADD_KEY_KEY_KEY);
+ ssid_en = g_object_get_data(G_OBJECT(data),AIRPCAP_ADVANCED_ADD_KEY_SSID_KEY);
+
+ type_entered = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(type_cb));
+ key_entered = g_strdup(gtk_entry_get_text(GTK_ENTRY(key_en)));
+ ssid_entered = g_strdup(gtk_entry_get_text(GTK_ENTRY(ssid_en)));
+
+ /* Check if key is correct */
+ new_type_string = g_string_new(type_entered);
+ new_key_string = g_string_new(key_entered);
+ new_ssid_string = g_string_new(ssid_entered);
+
+ g_free(type_entered);
+ g_free(key_entered );
+ g_free(ssid_entered);
+
+ g_strstrip(new_key_string->str);
+ g_strstrip(new_ssid_string->str);
+
+ /* Check which type of key the user has entered */
+ if (g_ascii_strcasecmp(new_type_string->str,AIRPCAP_WEP_KEY_STRING) == 0) /* WEP key */
+ {
+
+ if ( ((new_key_string->len) > WEP_KEY_MAX_CHAR_SIZE) || ((new_key_string->len) < WEP_KEY_MIN_CHAR_SIZE))
+ {
+ simple_dialog(ESD_TYPE_ERROR,ESD_BTN_OK,"WEP key size out of range!\nValid key size range is %d-%d characters (%d-%d bits).",WEP_KEY_MIN_CHAR_SIZE,WEP_KEY_MAX_CHAR_SIZE,WEP_KEY_MIN_BIT_SIZE,WEP_KEY_MAX_BIT_SIZE);
+
+ g_string_free(new_type_string,TRUE);
+ g_string_free(new_key_string, TRUE);
+ g_string_free(new_ssid_string,TRUE);
+
+ return;
+ }
+
+ if ((new_key_string->len % 2) != 0)
+ {
+ simple_dialog(ESD_TYPE_ERROR,ESD_BTN_OK,"Invalid WEP key!\nThe number of characters must be even.");
+
+ g_string_free(new_type_string,TRUE);
+ g_string_free(new_key_string, TRUE);
+ g_string_free(new_ssid_string,TRUE);
+
+ return;
+ }
+
+ for (i = 0; i < new_key_string->len; i++)
+ {
+ if (!g_ascii_isxdigit(new_key_string->str[i]))
+ {
+ simple_dialog(ESD_TYPE_ERROR,ESD_BTN_OK,"Invalid WEP key!\nA WEP key must be a hexadecimal number.\nThe valid characters are: 0123456789ABCDEF.");
+
+ g_string_free(new_type_string,TRUE);
+ g_string_free(new_key_string, TRUE);
+ g_string_free(new_ssid_string,TRUE);
+
+ return;
+ }
+ }
+
+ /* If so... add key */
+ airpcap_add_key_to_list(key_list_store, new_type_string->str, new_key_string->str, new_ssid_string->str);
+
+ if (airpcap_if_selected != NULL) airpcap_if_selected->saved = FALSE;
+ }
+ else if (g_ascii_strcasecmp(new_type_string->str,AIRPCAP_WPA_PWD_KEY_STRING) == 0) /* WPA Key */
+ {
+ /* XXX - Perform some WPA related input fields check */
+ /* If everything is ok, modify the entry in the list */
+
+ if ( ((new_key_string->len) > WPA_KEY_MAX_CHAR_SIZE) || ((new_key_string->len) < WPA_KEY_MIN_CHAR_SIZE))
+ {
+ simple_dialog(ESD_TYPE_ERROR,ESD_BTN_OK,"WPA key size out of range!\nValid key size range is %d-%d ASCII characters (%d-%d bits).",WPA_KEY_MIN_CHAR_SIZE,WPA_KEY_MAX_CHAR_SIZE,WPA_KEY_MIN_BIT_SIZE,WPA_KEY_MAX_BIT_SIZE);
+
+ g_string_free(new_type_string,TRUE);
+ g_string_free(new_key_string, TRUE);
+ g_string_free(new_ssid_string,TRUE);
+
+ return;
+ }
+
+ /*
+ * XXX - Maybe we need some check on the characters? I'm not sure if only standard ASCII are ok...
+ */
+ if ((new_ssid_string->len) > WPA_SSID_MAX_CHAR_SIZE)
+ {
+ simple_dialog(ESD_TYPE_ERROR,ESD_BTN_OK,"SSID key size out of range!\nValid SSID size range is %d-%d ASCII characters (%d-%d bits).",WPA_SSID_MIN_CHAR_SIZE,WPA_SSID_MAX_CHAR_SIZE,WPA_SSID_MIN_BIT_SIZE,WPA_SSID_MAX_BIT_SIZE);
+
+ g_string_free(new_type_string,TRUE);
+ g_string_free(new_key_string, TRUE);
+ g_string_free(new_ssid_string,TRUE);
+
+ return;
+ }
+
+ /* If so... add key */
+ airpcap_add_key_to_list(key_list_store, new_type_string->str, new_key_string->str, new_ssid_string->str);
+
+ if (airpcap_if_selected != NULL) airpcap_if_selected->saved = FALSE;
+ }
+ else if (g_ascii_strcasecmp(new_type_string->str,AIRPCAP_WPA_BIN_KEY_STRING) == 0) /* WPA_BIN Key */
+ {
+ /* XXX - Perform some WPA_BIN related input fields check */
+ /* If everything is ok, modify the entry int he list */
+
+ if ( ((new_key_string->len) != WPA_PSK_KEY_CHAR_SIZE))
+ {
+ simple_dialog(ESD_TYPE_ERROR,ESD_BTN_OK,"WPA PSK/PMK key size is wrong!\nValid key size is %d characters (%d bits).",WPA_PSK_KEY_CHAR_SIZE,WPA_PSK_KEY_BIT_SIZE);
+
+ g_string_free(new_type_string,TRUE);
+ g_string_free(new_key_string, TRUE);
+ g_string_free(new_ssid_string,TRUE);
+
+ return;
+ }
+
+ for (i = 0; i < new_key_string->len; i++)
+ {
+ if (!g_ascii_isxdigit(new_key_string->str[i]))
+ {
+ simple_dialog(ESD_TYPE_ERROR,ESD_BTN_OK,"Invalid WPA PSK/PMK key!\nKey must be an hexadecimal number.\nThe valid characters are: 0123456789ABCDEF.");
+
+ g_string_free(new_type_string,TRUE);
+ g_string_free(new_key_string, TRUE);
+ g_string_free(new_ssid_string,TRUE);
+
+ return;
+ }
+ }
+
+ /* If so... add key */
+ airpcap_add_key_to_list(key_list_store, new_type_string->str, new_key_string->str, new_ssid_string->str);
+
+ if (airpcap_if_selected != NULL) airpcap_if_selected->saved = FALSE;
+ }
+ else /* Should never happen!!! */
+ {
+ simple_dialog(ESD_TYPE_ERROR,ESD_BTN_OK,"Unknown error in the key \"Type\" field!");
+ }
+
+ g_string_free(new_type_string,TRUE);
+ g_string_free(new_key_string, TRUE);
+ g_string_free(new_ssid_string,TRUE);
+
+ window_destroy(GTK_WIDGET(data));
+ return;
+}
+
+/*
+ * Callback used to edit a WEP key in the edit key box;
+ */
+static void
+on_edit_key_ok_bt_clicked(GtkWidget *widget _U_, gpointer data)
+{
+ GtkWidget *type_cb,
+ *key_en,
+ *ssid_en;
+
+ GtkListStore *key_list_store;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+
+ GString *new_type_string,
+ *new_key_string,
+ *new_ssid_string;
+
+ gchar *type_entered = NULL;
+ gchar *key_entered = NULL;
+ gchar *ssid_entered = NULL;
+
+ unsigned int i;
+
+ key_list_store = g_object_get_data(G_OBJECT(data),AIRPCAP_ADVANCED_EDIT_KEY_LIST_KEY);
+ selection = g_object_get_data(G_OBJECT(data), AIRPCAP_ADVANCED_EDIT_KEY_SELECTION_KEY);
+ type_cb = g_object_get_data(G_OBJECT(data),AIRPCAP_ADVANCED_EDIT_KEY_TYPE_KEY);
+ key_en = g_object_get_data(G_OBJECT(data),AIRPCAP_ADVANCED_EDIT_KEY_KEY_KEY);
+ ssid_en = g_object_get_data(G_OBJECT(data),AIRPCAP_ADVANCED_EDIT_KEY_SSID_KEY);
+
+ if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
+ return;
+
+ type_entered = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(type_cb));
+ key_entered = g_strdup(gtk_entry_get_text(GTK_ENTRY(key_en)));
+ ssid_entered = g_strdup(gtk_entry_get_text(GTK_ENTRY(ssid_en)));
+
+ g_strstrip(key_entered);
+ g_strstrip(ssid_entered);
+
+ /* Check if key is correct */
+ new_type_string = g_string_new(type_entered);
+ new_key_string = g_string_new(key_entered);
+ new_ssid_string = g_string_new(ssid_entered);
+
+ g_free(type_entered);
+ g_free(key_entered );
+ g_free(ssid_entered);
+
+ /* Check which type of key the user has entered */
+ if (g_ascii_strcasecmp(new_type_string->str,AIRPCAP_WEP_KEY_STRING) == 0) /* WEP key */
+ {
+
+ if ( ((new_key_string->len) > WEP_KEY_MAX_CHAR_SIZE) || ((new_key_string->len) < WEP_KEY_MIN_CHAR_SIZE))
+ {
+ simple_dialog(ESD_TYPE_ERROR,ESD_BTN_OK,"WEP key size out of range!\nValid key size range is %d-%d characters (%d-%d bits).",WEP_KEY_MIN_CHAR_SIZE,WEP_KEY_MAX_CHAR_SIZE,WEP_KEY_MIN_BIT_SIZE,WEP_KEY_MAX_BIT_SIZE);
+
+ g_string_free(new_type_string,TRUE);
+ g_string_free(new_key_string, TRUE);
+ g_string_free(new_ssid_string,TRUE);
+
+ return;
+ }
+
+ if ((new_key_string->len % 2) != 0)
+ {
+ simple_dialog(ESD_TYPE_ERROR,ESD_BTN_OK,"Invalid WEP key!\nThe number of characters must be even.");
+
+ g_string_free(new_type_string,TRUE);
+ g_string_free(new_key_string, TRUE);
+ g_string_free(new_ssid_string,TRUE);
+
+ return;
+ }
+
+ for (i = 0; i < new_key_string->len; i++)
+ {
+ if (!g_ascii_isxdigit(new_key_string->str[i]))
+ {
+ simple_dialog(ESD_TYPE_ERROR,ESD_BTN_OK,"Invalid WEP key!\nA WEP key must be an hexadecimal number.\nThe valid characters are: 0123456789ABCDEF.");
+
+ g_string_free(new_type_string,TRUE);
+ g_string_free(new_key_string, TRUE);
+ g_string_free(new_ssid_string,TRUE);
+
+ return;
+ }
+ }
+
+ /* If so... Modify key */
+ gtk_list_store_set(key_list_store, &iter,
+ KL_COL_TYPE, new_type_string->str,
+ KL_COL_KEY, new_key_string->str,
+ KL_COL_SSID, new_ssid_string->str,
+ -1);
+
+ if (airpcap_if_selected != NULL) airpcap_if_selected->saved = FALSE;
+ }
+ else if (g_ascii_strcasecmp(new_type_string->str,AIRPCAP_WPA_PWD_KEY_STRING) == 0) /* WPA Key */
+ {
+ /* XXX - Perform some WPA related input fields check */
+ /* If everything is ok, modify the entry in the list */
+
+ if ( ((new_key_string->len) > WPA_KEY_MAX_CHAR_SIZE) || ((new_key_string->len) < WPA_KEY_MIN_CHAR_SIZE))
+ {
+ simple_dialog(ESD_TYPE_ERROR,ESD_BTN_OK,"WPA key size out of range!\nValid key size range is %d-%d ASCII characters (%d-%d bits).",WPA_KEY_MIN_CHAR_SIZE,WPA_KEY_MAX_CHAR_SIZE,WPA_KEY_MIN_BIT_SIZE,WPA_KEY_MAX_BIT_SIZE);
+
+ g_string_free(new_type_string,TRUE);
+ g_string_free(new_key_string, TRUE);
+ g_string_free(new_ssid_string,TRUE);
+
+ return;
+ }
+
+ /*
+ * XXX - Maybe we need some check on the characters? I'm not sure if only standard ASCII are ok...
+ */
+ if ((new_ssid_string->len) > WPA_SSID_MAX_CHAR_SIZE)
+ {
+ simple_dialog(ESD_TYPE_ERROR,ESD_BTN_OK,"SSID key size out of range!\nValid SSID size range is %d-%d ASCII characters (%d-%d bits).",WPA_SSID_MIN_CHAR_SIZE,WPA_SSID_MAX_CHAR_SIZE,WPA_SSID_MIN_BIT_SIZE,WPA_SSID_MAX_BIT_SIZE);
+
+ g_string_free(new_type_string,TRUE);
+ g_string_free(new_key_string, TRUE);
+ g_string_free(new_ssid_string,TRUE);
+
+ return;
+ }
+
+ /* If so... Modify key */
+ gtk_list_store_set(key_list_store, &iter,
+ KL_COL_TYPE, new_type_string->str,
+ KL_COL_KEY, new_key_string->str,
+ KL_COL_SSID, new_ssid_string->str,
+ -1);
+
+ if (airpcap_if_selected != NULL) airpcap_if_selected->saved = FALSE;
+ }
+ else if (g_ascii_strcasecmp(new_type_string->str,AIRPCAP_WPA_BIN_KEY_STRING) == 0) /* WPA_BIN Key */
+ {
+ /* XXX - Perform some WPA_BIN related input fields check */
+ /* If everything is ok, modify the entry in the list */
+
+ if ( ((new_key_string->len) != WPA_PSK_KEY_CHAR_SIZE))
+ {
+ simple_dialog(ESD_TYPE_ERROR,ESD_BTN_OK,"WPA PSK/PMK key size is wrong!\nValid key size is %d characters (%d bits).",WPA_PSK_KEY_CHAR_SIZE,WPA_PSK_KEY_BIT_SIZE);
+
+ g_string_free(new_type_string,TRUE);
+ g_string_free(new_key_string, TRUE);
+ g_string_free(new_ssid_string,TRUE);
+
+ return;
+ }
+
+ for (i = 0; i < new_key_string->len; i++)
+ {
+ if (!g_ascii_isxdigit(new_key_string->str[i]))
+ {
+ simple_dialog(ESD_TYPE_ERROR,ESD_BTN_OK,"Invalid WPA PSK/PMK key!\nKey must be an hexadecimal number.\nThe valid characters are: 0123456789ABCDEF.");
+
+ g_string_free(new_type_string,TRUE);
+ g_string_free(new_key_string, TRUE);
+ g_string_free(new_ssid_string,TRUE);
+
+ return;
+ }
+ }
+
+ /* If so... Modify key */
+ gtk_list_store_set(key_list_store, &iter,
+ KL_COL_TYPE, new_type_string->str,
+ KL_COL_KEY, new_key_string->str,
+ KL_COL_SSID, new_ssid_string->str,
+ -1);
+
+ if (airpcap_if_selected != NULL) airpcap_if_selected->saved = FALSE;
+ }
+ else /* Should never happen!!! */
+ {
+ simple_dialog(ESD_TYPE_ERROR,ESD_BTN_OK,"Unknown error in the key \"Type\" field!");
+ }
+
+ g_string_free(new_type_string,TRUE);
+ g_string_free(new_key_string, TRUE);
+ g_string_free(new_ssid_string,TRUE);
+
+ window_destroy(GTK_WIDGET(data));
+ return;
+}
+
+/*
+ * Add key window destroy callback
+ */
+static void
+on_add_key_w_destroy(GtkWidget *button _U_, gpointer data)
+{
+ GtkWidget *airpcap_advanced_w;
+
+ airpcap_advanced_w = GTK_WIDGET(data);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(airpcap_advanced_w),TRUE);
+
+ return;
+}
+
+/*
+ * Callback for the 'Add Key' button.
+ */
+static void
+on_add_new_key_bt_clicked(GtkWidget *button _U_, gpointer data)
+{
+ GtkWidget *add_key_window;
+ GtkWidget *add_frame;
+ GtkWidget *main_v_box;
+ GtkWidget *add_tb;
+ GtkWidget *add_frame_al;
+ GtkWidget *add_type_cb;
+ GtkWidget *add_key_te;
+ GtkWidget *add_ssid_te;
+ GtkWidget *add_type_lb;
+ GtkWidget *add_key_lb;
+ GtkWidget *add_ssid_lb;
+ GtkWidget *low_h_button_box;
+ GtkWidget *ok_bt;
+ GtkWidget *cancel_bt;
+ GtkWidget *add_frame_lb;
+
+ GtkWidget *airpcap_advanced_w;
+
+ GtkListStore *key_list_store;
+
+ airpcap_advanced_w = GTK_WIDGET(data);
+
+ key_list_store = g_object_get_data(G_OBJECT(airpcap_advanced_w),AIRPCAP_ADVANCED_KEYLIST_KEY);
+
+ if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(key_list_store), NULL) >= MAX_ENCRYPTION_KEYS)
+ {
+ simple_dialog(ESD_TYPE_ERROR,ESD_BTN_OK,"Maximum number (%d) of decryption keys reached! You cannot add another key!\n",MAX_ENCRYPTION_KEYS);
+ return;
+ }
+
+ /* Gray out the Advanced Wireless Setting window */
+ gtk_widget_set_sensitive(airpcap_advanced_w,FALSE);
+
+ /* Pop-up a new window */
+ add_key_window = dlg_window_new ("Add Decryption Key");
+ gtk_widget_set_name (add_key_window, "add_key_window");
+ gtk_container_set_border_width (GTK_CONTAINER (add_key_window), 5);
+ gtk_window_set_resizable (GTK_WINDOW (add_key_window), FALSE);
+
+ main_v_box = gtk_vbox_new (FALSE, 0);
+ gtk_widget_set_name (main_v_box, "main_v_box");
+ gtk_widget_show (main_v_box);
+ gtk_container_add (GTK_CONTAINER (add_key_window), main_v_box);
+
+ add_frame = gtk_frame_new (NULL);
+ gtk_widget_set_name (add_frame, "add_frame");
+ gtk_widget_show (add_frame);
+ gtk_box_pack_start (GTK_BOX (main_v_box), add_frame, TRUE, TRUE, 0);
+
+ add_frame_al = gtk_alignment_new (0.5, 0.5, 1, 1);
+ gtk_widget_set_name (add_frame_al, "add_frame_al");
+ gtk_widget_show (add_frame_al);
+ gtk_container_add (GTK_CONTAINER (add_frame), add_frame_al);
+ gtk_alignment_set_padding (GTK_ALIGNMENT (add_frame_al), 0, 0, 12, 0);
+
+ add_tb = gtk_table_new (2, 3, FALSE);
+ gtk_widget_set_name (add_tb, "add_tb");
+ gtk_container_set_border_width(GTK_CONTAINER(add_tb),5);
+ gtk_widget_show (add_tb);
+ gtk_container_add (GTK_CONTAINER (add_frame_al), add_tb);
+
+ add_type_cb = gtk_combo_box_text_new();
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(add_type_cb), AIRPCAP_WEP_KEY_STRING);
+
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(add_type_cb), AIRPCAP_WPA_PWD_KEY_STRING);
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(add_type_cb), AIRPCAP_WPA_BIN_KEY_STRING);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(add_type_cb), 0);
+ gtk_widget_set_name (add_type_cb, "add_type_cb");
+ gtk_widget_show (add_type_cb);
+ gtk_table_attach (GTK_TABLE (add_tb), add_type_cb, 0, 1, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_widget_set_size_request (add_type_cb, 83, -1);
+
+ add_key_te = gtk_entry_new ();
+ gtk_widget_set_name (add_key_te, "add_key_te");
+
+ gtk_widget_show (add_key_te);
+ gtk_table_attach (GTK_TABLE (add_tb), add_key_te, 1, 2, 1, 2,
+ (GtkAttachOptions) (0), (GtkAttachOptions) (0), 0, 0);
+ gtk_widget_set_size_request (add_key_te, 178, -1);
+
+ add_ssid_te = gtk_entry_new ();
+ gtk_widget_set_name (add_ssid_te, "add_ssid_te");
+ gtk_widget_set_sensitive(add_ssid_te,FALSE);
+ /* XXX - Decomment only when WPA and WPA_BIN will be ready */
+ gtk_widget_show (add_ssid_te);
+ gtk_table_attach (GTK_TABLE (add_tb), add_ssid_te, 2, 3, 1, 2,
+ (GtkAttachOptions) (0), (GtkAttachOptions) (0), 0, 0);
+
+ add_type_lb = gtk_label_new ("Type");
+ gtk_widget_set_name (add_type_lb, "add_type_lb");
+ gtk_widget_show (add_type_lb);
+ gtk_table_attach (GTK_TABLE (add_tb), add_type_lb, 0, 1, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_label_set_justify (GTK_LABEL (add_type_lb), GTK_JUSTIFY_CENTER);
+
+ add_key_lb = gtk_label_new ("Key");
+ gtk_widget_set_name (add_key_lb, "add_key_lb");
+ gtk_widget_show (add_key_lb);
+ gtk_table_attach (GTK_TABLE (add_tb), add_key_lb, 1, 2, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_label_set_justify (GTK_LABEL (add_key_lb), GTK_JUSTIFY_CENTER);
+
+ add_ssid_lb = gtk_label_new ("");
+ gtk_widget_set_name (add_ssid_lb, "add_ssid_lb");
+ /* XXX - Decomment only when WPA and WPA_BIN will be ready */
+ gtk_widget_show (add_ssid_lb);
+ gtk_table_attach (GTK_TABLE (add_tb), add_ssid_lb, 2, 3, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_label_set_justify (GTK_LABEL (add_ssid_lb), GTK_JUSTIFY_CENTER);
+
+ low_h_button_box = gtk_hbutton_box_new ();
+ gtk_widget_set_name (low_h_button_box, "low_h_button_box");
+ gtk_container_set_border_width (GTK_CONTAINER (low_h_button_box), 5);
+ gtk_widget_show (low_h_button_box);
+ gtk_box_pack_end (GTK_BOX (main_v_box), low_h_button_box, FALSE, FALSE, 0);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (low_h_button_box),
+ GTK_BUTTONBOX_END);
+
+ ok_bt = gtk_button_new_with_mnemonic ("OK");
+ gtk_widget_set_name (ok_bt, "ok_bt");
+ gtk_widget_show (ok_bt);
+ gtk_container_add (GTK_CONTAINER (low_h_button_box), ok_bt);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default (ok_bt, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS (ok_bt, GTK_CAN_DEFAULT);
+#endif
+
+ cancel_bt = gtk_button_new_with_mnemonic ("Cancel");
+ gtk_widget_set_name (cancel_bt, "cancel_bt");
+ gtk_widget_show (cancel_bt);
+ gtk_container_add (GTK_CONTAINER (low_h_button_box), cancel_bt);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default (cancel_bt, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS (cancel_bt, GTK_CAN_DEFAULT);
+#endif
+
+ add_frame_lb = gtk_label_new ("<b>Modify Selected Key</b>");
+ gtk_widget_set_name (add_frame_lb, "add_frame_lb");
+ gtk_widget_show (add_frame_lb);
+ gtk_frame_set_label_widget (GTK_FRAME (add_frame), add_frame_lb);
+ gtk_label_set_use_markup (GTK_LABEL (add_frame_lb), TRUE);
+
+ /* Add callbacks */
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(on_add_key_ok_bt_clicked), add_key_window );
+ g_signal_connect(cancel_bt, "clicked", G_CALLBACK(window_cancel_button_cb), add_key_window );
+ g_signal_connect(add_type_cb, "changed", G_CALLBACK(on_add_type_cb_changed), add_key_window);
+ g_signal_connect(add_key_window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(add_key_window, "destroy", G_CALLBACK(on_add_key_w_destroy), data);
+
+ /* Add widget data */
+ g_object_set_data(G_OBJECT(add_key_window),AIRPCAP_ADVANCED_ADD_KEY_LIST_KEY,key_list_store);
+ g_object_set_data(G_OBJECT(add_key_window),AIRPCAP_ADVANCED_ADD_KEY_TYPE_KEY,add_type_cb);
+ g_object_set_data(G_OBJECT(add_key_window),AIRPCAP_ADVANCED_ADD_KEY_KEY_KEY,add_key_te);
+ g_object_set_data(G_OBJECT(add_key_window),AIRPCAP_ADVANCED_ADD_KEY_SSID_KEY,add_ssid_te);
+ g_object_set_data(G_OBJECT(add_key_window),AIRPCAP_ADVANCED_ADD_KEY_KEY_LABEL_KEY,add_key_lb);
+ g_object_set_data(G_OBJECT(add_key_window),AIRPCAP_ADVANCED_ADD_KEY_SSID_LABEL_KEY,add_ssid_lb);
+
+ gtk_widget_show(add_key_window);
+}
+
+/*
+ * Edit key window destroy callback
+ */
+static void
+on_edit_key_w_destroy(GtkWidget *button _U_, gpointer data)
+{
+ GtkWidget *airpcap_advanced_w;
+
+ airpcap_advanced_w = GTK_WIDGET(data);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(airpcap_advanced_w),TRUE);
+
+ return;
+}
+
+/*
+ * Callback for the 'Remove Key' button.
+ */
+static void
+on_remove_key_bt_clicked(GtkWidget *button _U_, gpointer data)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+
+ /* retrieve needed stuff */
+ selection = g_object_get_data(G_OBJECT(data), AIRPCAP_ADVANCED_EDIT_KEY_SELECTION_KEY);
+
+ if (!gtk_tree_selection_get_selected(selection, &model, &iter))
+ return;
+
+ /* Remove selected key */
+ gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
+ gtk_tree_selection_select_iter(selection, &iter);
+ /* XXX - select the last item if needed? */
+
+ /* Need to save config... */
+ if (airpcap_if_selected != NULL) airpcap_if_selected->saved = FALSE;
+}
+
+/*
+ * Callback for the 'Edit Key' button.
+ */
+static void
+on_edit_key_bt_clicked(GtkWidget *button _U_, gpointer data)
+{
+ GtkWidget *edit_key_window;
+ GtkWidget *edit_frame;
+ GtkWidget *main_v_box;
+ GtkWidget *edit_tb;
+ GtkWidget *edit_frame_al;
+ GtkWidget *edit_type_cb;
+ GtkWidget *edit_key_te;
+ GtkWidget *edit_ssid_te;
+ GtkWidget *edit_type_lb;
+ GtkWidget *edit_key_lb;
+ GtkWidget *edit_ssid_lb;
+ GtkWidget *low_h_button_box;
+ GtkWidget *ok_bt;
+ GtkWidget *cancel_bt;
+ GtkWidget *edit_frame_lb;
+
+ GtkWidget *airpcap_advanced_w;
+
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+
+ /* Key List Store */
+ GtkListStore *key_list_store;
+
+ gchar *row_type,
+ *row_key,
+ *row_ssid = "";
+
+ airpcap_advanced_w = GTK_WIDGET(data);
+
+ /* Retrieve the selected item... if no row is selected, this is null... */
+ selection = g_object_get_data(G_OBJECT(data), AIRPCAP_ADVANCED_EDIT_KEY_SELECTION_KEY);
+ key_list_store = g_object_get_data (G_OBJECT(data), AIRPCAP_ADVANCED_KEYLIST_KEY);
+
+
+ if (!gtk_tree_selection_get_selected(selection, &model, &iter))
+ return;
+
+ gtk_tree_model_get(model, &iter,
+ KL_COL_TYPE, &row_type,
+ KL_COL_KEY, &row_key,
+ KL_COL_SSID, &row_ssid,
+ -1);
+
+ /* Gray out the Advanced Wireless Setting window */
+ gtk_widget_set_sensitive(airpcap_advanced_w,FALSE);
+
+ /* Pop-up a new window */
+ edit_key_window = dlg_window_new("Edit Decryption Key");
+ gtk_widget_set_name (edit_key_window, "edit_key_window");
+ gtk_container_set_border_width (GTK_CONTAINER (edit_key_window), 5);
+ gtk_window_set_resizable (GTK_WINDOW (edit_key_window), FALSE);
+
+ main_v_box = gtk_vbox_new (FALSE, 0);
+ gtk_widget_set_name (main_v_box, "main_v_box");
+ gtk_widget_show (main_v_box);
+ gtk_container_add (GTK_CONTAINER (edit_key_window), main_v_box);
+
+ edit_frame = gtk_frame_new (NULL);
+ gtk_widget_set_name (edit_frame, "edit_frame");
+ gtk_widget_show (edit_frame);
+ gtk_box_pack_start (GTK_BOX (main_v_box), edit_frame, TRUE, TRUE, 0);
+
+ edit_frame_al = gtk_alignment_new (0.5, 0.5, 1, 1);
+ gtk_widget_set_name (edit_frame_al, "edit_frame_al");
+ gtk_widget_show (edit_frame_al);
+ gtk_container_add (GTK_CONTAINER (edit_frame), edit_frame_al);
+ gtk_alignment_set_padding (GTK_ALIGNMENT (edit_frame_al), 0, 0, 12, 0);
+
+ edit_tb = gtk_table_new (2, 3, FALSE);
+ gtk_widget_set_name (edit_tb, "edit_tb");
+ gtk_container_set_border_width(GTK_CONTAINER(edit_tb),5);
+ gtk_widget_show (edit_tb);
+ gtk_container_add (GTK_CONTAINER (edit_frame_al), edit_tb);
+
+ edit_type_cb = gtk_combo_box_text_new();
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(edit_type_cb), AIRPCAP_WEP_KEY_STRING);
+
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(edit_type_cb), AIRPCAP_WPA_PWD_KEY_STRING);
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(edit_type_cb), AIRPCAP_WPA_BIN_KEY_STRING);
+ /* Set current type */
+ gtk_combo_box_set_active(GTK_COMBO_BOX(edit_type_cb), 0);
+ if (g_ascii_strcasecmp(row_type, AIRPCAP_WPA_PWD_KEY_STRING) == 0) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(edit_type_cb), 1);
+ } else if (g_ascii_strcasecmp(row_type, AIRPCAP_WPA_BIN_KEY_STRING) == 0) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(edit_type_cb), 2);
+ }
+ gtk_widget_set_name (edit_type_cb, "edit_type_cb");
+ gtk_widget_show (edit_type_cb);
+ gtk_table_attach (GTK_TABLE (edit_tb), edit_type_cb, 0, 1, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_widget_set_size_request (edit_type_cb, 83, -1);
+
+ edit_key_te = gtk_entry_new ();
+ gtk_widget_set_name (edit_key_te, "edit_key_te");
+ /* Set current key */
+ gtk_entry_set_text(GTK_ENTRY(edit_key_te),row_key);
+ gtk_widget_show (edit_key_te);
+ gtk_table_attach (GTK_TABLE (edit_tb), edit_key_te, 1, 2, 1, 2,
+ (GtkAttachOptions) (0), (GtkAttachOptions) (0), 0, 0);
+ gtk_widget_set_size_request (edit_key_te, 178, -1);
+
+ edit_ssid_te = gtk_entry_new ();
+ gtk_widget_set_name (edit_ssid_te, "edit_ssid_te");
+
+ /* Set current ssid (if key type is not WEP!)*/
+ if (g_ascii_strcasecmp(row_type,AIRPCAP_WEP_KEY_STRING) == 0)
+ {
+ gtk_widget_set_sensitive(edit_ssid_te,FALSE);
+ }
+ else
+ {
+ gtk_widget_set_sensitive(edit_ssid_te,TRUE);
+ gtk_entry_set_text(GTK_ENTRY(edit_ssid_te),row_ssid);
+ }
+
+ /* XXX - Decomment only when WPA and WPA@ will be ready */
+ gtk_widget_show (edit_ssid_te);
+ gtk_table_attach (GTK_TABLE (edit_tb), edit_ssid_te, 2, 3, 1, 2,
+ (GtkAttachOptions) (0), (GtkAttachOptions) (0), 0, 0);
+
+ edit_type_lb = gtk_label_new ("Type");
+ gtk_widget_set_name (edit_type_lb, "edit_type_lb");
+ gtk_widget_show (edit_type_lb);
+ gtk_table_attach (GTK_TABLE (edit_tb), edit_type_lb, 0, 1, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_label_set_justify (GTK_LABEL (edit_type_lb), GTK_JUSTIFY_CENTER);
+
+ edit_key_lb = gtk_label_new ("Key");
+ gtk_widget_set_name (edit_key_lb, "edit_key_lb");
+ gtk_widget_show (edit_key_lb);
+ gtk_table_attach (GTK_TABLE (edit_tb), edit_key_lb, 1, 2, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_label_set_justify (GTK_LABEL (edit_key_lb), GTK_JUSTIFY_CENTER);
+
+ edit_ssid_lb = gtk_label_new ("");
+ gtk_widget_set_name (edit_ssid_lb, "edit_ssid_lb");
+ /* XXX - Decomment only when WPA and WPA_BIN will be ready */
+ gtk_widget_show (edit_ssid_lb);
+ gtk_table_attach (GTK_TABLE (edit_tb), edit_ssid_lb, 2, 3, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_label_set_justify (GTK_LABEL (edit_ssid_lb), GTK_JUSTIFY_CENTER);
+
+ low_h_button_box = gtk_hbutton_box_new ();
+ gtk_widget_set_name (low_h_button_box, "low_h_button_box");
+ gtk_container_set_border_width (GTK_CONTAINER (low_h_button_box), 5);
+ gtk_widget_show (low_h_button_box);
+ gtk_box_pack_end (GTK_BOX (main_v_box), low_h_button_box, FALSE, FALSE, 0);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (low_h_button_box),
+ GTK_BUTTONBOX_END);
+
+ ok_bt = gtk_button_new_with_mnemonic ("OK");
+ gtk_widget_set_name (ok_bt, "ok_bt");
+ gtk_widget_show (ok_bt);
+ gtk_container_add (GTK_CONTAINER (low_h_button_box), ok_bt);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default (ok_bt, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS (ok_bt, GTK_CAN_DEFAULT);
+#endif
+
+ cancel_bt = gtk_button_new_with_mnemonic ("Cancel");
+ gtk_widget_set_name (cancel_bt, "cancel_bt");
+ gtk_widget_show (cancel_bt);
+ gtk_container_add (GTK_CONTAINER (low_h_button_box), cancel_bt);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default (cancel_bt, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS (cancel_bt, GTK_CAN_DEFAULT);
+#endif
+
+ edit_frame_lb = gtk_label_new ("<b>Modify Selected Key</b>");
+ gtk_widget_set_name (edit_frame_lb, "edit_frame_lb");
+ gtk_widget_show (edit_frame_lb);
+ gtk_frame_set_label_widget (GTK_FRAME (edit_frame), edit_frame_lb);
+ gtk_label_set_use_markup (GTK_LABEL (edit_frame_lb), TRUE);
+
+ /* Add callbacks */
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(on_edit_key_ok_bt_clicked), edit_key_window );
+ g_signal_connect(cancel_bt, "clicked", G_CALLBACK(window_cancel_button_cb), edit_key_window );
+ g_signal_connect(edit_type_cb, "changed", G_CALLBACK(on_edit_type_cb_changed), edit_key_window);
+ g_signal_connect(edit_key_window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(edit_key_window, "destroy", G_CALLBACK(on_edit_key_w_destroy), data);
+
+ /* Add widget data */
+ g_object_set_data(G_OBJECT(edit_key_window),AIRPCAP_ADVANCED_EDIT_KEY_LIST_KEY,key_list_store);
+ g_object_set_data(G_OBJECT(edit_key_window),AIRPCAP_ADVANCED_EDIT_KEY_SELECTION_KEY,selection);
+ g_object_set_data(G_OBJECT(edit_key_window),AIRPCAP_ADVANCED_EDIT_KEY_TYPE_KEY,edit_type_cb);
+ g_object_set_data(G_OBJECT(edit_key_window),AIRPCAP_ADVANCED_EDIT_KEY_KEY_KEY,edit_key_te);
+ g_object_set_data(G_OBJECT(edit_key_window),AIRPCAP_ADVANCED_EDIT_KEY_SSID_KEY,edit_ssid_te);
+ g_object_set_data(G_OBJECT(edit_key_window),AIRPCAP_ADVANCED_EDIT_KEY_KEY_LABEL_KEY,edit_key_lb);
+ g_object_set_data(G_OBJECT(edit_key_window),AIRPCAP_ADVANCED_EDIT_KEY_SSID_LABEL_KEY,edit_ssid_lb);
+
+
+ g_free(row_type);
+ g_free(row_key);
+ g_free(row_ssid);
+ gtk_widget_show(edit_key_window);
+}
+
+/*
+ * Callback for the 'Move Key Up' button.
+ */
+static void
+on_move_key_up_bt_clicked(GtkWidget *button _U_, gpointer key_list)
+{
+ tree_view_list_store_move_selection(GTK_TREE_VIEW(key_list), TRUE);
+}
+
+/*
+ * Callback for the 'Move Key Down' button.
+ */
+static void
+on_move_key_down_bt_clicked(GtkWidget *button _U_, gpointer list_view)
+{
+ tree_view_list_store_move_selection(GTK_TREE_VIEW(list_view), FALSE);
+}
+
+/* Turns the decryption on or off */
+void
+on_decryption_mode_cb_changed(GtkWidget *cb, gpointer data _U_)
+{
+ gint cur_active;
+
+ if (cb == NULL) {
+ return;
+ }
+
+ cur_active = gtk_combo_box_get_active(GTK_COMBO_BOX(cb));
+
+ if (cur_active < 0) {
+ return;
+ }
+
+ switch(cur_active) {
+ /* XXX - Don't use magic numbers here */
+ case 1: /* Wireshark */
+ set_wireshark_decryption(TRUE);
+ if (!set_airpcap_decryption(FALSE)) g_warning(CANT_SAVE_ERR_STR);
+ break;
+ case 2: /* Driver */
+ set_wireshark_decryption(FALSE);
+ if (!set_airpcap_decryption(TRUE)) g_warning(CANT_SAVE_ERR_STR);
+ break;
+ default:
+ set_wireshark_decryption(FALSE);
+ if (!set_airpcap_decryption(FALSE)) g_warning(CANT_SAVE_ERR_STR);
+ break;
+ }
+
+ /* Redissect all the packets, and re-evaluate the display filter. */
+ redissect_packets();
+}
+
+/*
+ * Selects the current decryption mode string in the decryption mode combo box
+ */
+void
+update_decryption_mode(GtkWidget *cb)
+{
+ if (cb == NULL) {
+ return;
+ }
+
+ /* Wireshark decryption is on */
+ if (wireshark_decryption_on())
+ {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(cb), 1);
+ }
+ /* AirPcap decryption is on */
+ else if (airpcap_decryption_on())
+ {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(cb), 2);
+ }
+ /* No decryption enabled */
+ else
+ {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(cb), 0);
+ }
+
+ return;
+}
+
+/*
+ * Creates the list of available decryption modes, depending on the adapters found
+ */
+void
+update_decryption_mode_list(GtkWidget *cb)
+{
+ gchar *current_text;
+
+ if (cb == NULL)
+ return;
+
+ current_text = NULL;
+
+ /*
+ * XXX - Retrieve the current 'decryption mode'. It would be better just block the
+ * signal handler, but it doesn't work... one of these days I'll try to figure out why...
+ */
+ current_text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(cb));
+
+ while (gtk_tree_model_iter_n_children(gtk_combo_box_get_model(GTK_COMBO_BOX(cb)), NULL) > 0) {
+ gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(cb), 0);
+ }
+
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(cb), AIRPCAP_DECRYPTION_TYPE_STRING_NONE);
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(cb), AIRPCAP_DECRYPTION_TYPE_STRING_WIRESHARK);
+
+ if (airpcap_if_list != NULL && g_list_length(airpcap_if_list) > 0)
+ {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(cb), AIRPCAP_DECRYPTION_TYPE_STRING_AIRPCAP);
+ }
+ else
+ {
+ /* The last decryption mode was 'Driver', but no more AirPcap adapter are found */
+ if (current_text == NULL || g_ascii_strcasecmp(current_text, AIRPCAP_DECRYPTION_TYPE_STRING_AIRPCAP) == 0)
+ {
+ g_free(current_text);
+ current_text = g_strdup(AIRPCAP_DECRYPTION_TYPE_STRING_NONE);
+ }
+ }
+
+ if (g_ascii_strcasecmp(current_text, AIRPCAP_DECRYPTION_TYPE_STRING_WIRESHARK) == 0) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(cb), 1);
+ } else if (g_ascii_strcasecmp(current_text, AIRPCAP_DECRYPTION_TYPE_STRING_AIRPCAP) == 0) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(cb), 2);
+ } else { /* None / Invalid */
+ gtk_combo_box_set_active(GTK_COMBO_BOX(cb), 0);
+ }
+
+ g_free(current_text);
+}
+
+
+/*
+ * Callback for the Wireless Advanced Settings 'Apply' button.
+ */
+static void
+on_advanced_apply_bt_clicked(GtkWidget *button _U_, gpointer data)
+{
+ /* advenced window */
+ GtkWidget *airpcap_advanced_w;
+ GtkWidget *channel_cb, *channel_offset_cb;
+
+ /* widgets in the toolbar */
+ GtkWidget *toolbar,
+ *toolbar_if_lb,
+ *toolbar_channel_cb,
+ *toolbar_channel_offset_cb,
+ *toolbar_fcs_filter_cb;
+
+ /* retrieve main window */
+ airpcap_advanced_w = GTK_WIDGET(data);
+
+ /* Set the channel and offset */
+ channel_cb = GTK_WIDGET(g_object_get_data(G_OBJECT(airpcap_advanced_w),AIRPCAP_ADVANCED_CHANNEL_KEY));
+ channel_offset_cb = GTK_WIDGET(g_object_get_data(G_OBJECT(airpcap_advanced_w),AIRPCAP_ADVANCED_CHANNEL_OFFSET_KEY));
+ airpcap_channel_offset_changed_cb(channel_offset_cb, NULL);
+ airpcap_channel_changed_set_cb(channel_cb, channel_offset_cb);
+
+
+ toolbar = GTK_WIDGET(g_object_get_data(G_OBJECT(airpcap_advanced_w),AIRPCAP_TOOLBAR_KEY));
+
+ /* retrieve toolbar info */
+ toolbar_if_lb = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbar),AIRPCAP_TOOLBAR_INTERFACE_KEY));
+ toolbar_channel_cb = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbar),AIRPCAP_TOOLBAR_CHANNEL_KEY));
+ toolbar_channel_offset_cb = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbar),AIRPCAP_TOOLBAR_CHANNEL_OFFSET_KEY));
+ toolbar_fcs_filter_cb = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbar),AIRPCAP_TOOLBAR_FCS_FILTER_KEY));
+
+ /* Save the configuration (for all ) */
+ airpcap_save_selected_if_configuration(airpcap_if_selected);
+
+ /* Update toolbar (only if airpcap_if_selected is airpcap_if_active)*/
+ if ( g_ascii_strcasecmp(airpcap_if_selected->description,airpcap_if_active->description) == 0)
+ {
+ gtk_label_set_text(GTK_LABEL(toolbar_if_lb), g_strdup_printf("%s %s\t","Current Wireless Interface: #",airpcap_get_if_string_number(airpcap_if_selected)));
+ airpcap_update_channel_combo(GTK_WIDGET(toolbar_channel_cb),airpcap_if_selected);
+ airpcap_update_channel_offset_combo(airpcap_if_selected, airpcap_if_selected->channelInfo.Frequency, toolbar_channel_offset_cb, TRUE);
+ airpcap_validation_type_combo_set_by_type(toolbar_fcs_filter_cb,airpcap_if_selected->CrcValidationOn);
+ }
+}
+
+/*
+ * Callback for the OK button 'clicked' in the Advanced Wireless Settings window.
+ */
+static void
+on_advanced_ok_bt_clicked(GtkWidget *button _U_, gpointer data)
+{
+ PAirpcapHandle ad = NULL;
+ gchar ebuf[AIRPCAP_ERRBUF_SIZE];
+
+ /* Retrieve object data */
+ GtkWidget *airpcap_advanced_w = GTK_WIDGET(data);
+
+ if (airpcap_if_selected == NULL) { /* There's not much we can do. */
+ gtk_widget_destroy(airpcap_advanced_w);
+ return;
+ }
+
+ on_advanced_apply_bt_clicked(button, data);
+
+ /* Stop blinking our LED */
+ ad = airpcap_if_open(airpcap_if_selected->name, ebuf);
+ if (ad)
+ {
+ g_source_remove(airpcap_if_selected->tag);
+ airpcap_if_turn_led_on(ad, 0);
+ airpcap_if_selected->blinking = FALSE;
+ airpcap_if_selected->led = TRUE;
+ airpcap_if_close(ad);
+ }
+
+ /* Remove GLIB timeout */
+ g_source_remove(airpcap_if_selected->tag);
+
+ gtk_widget_destroy(airpcap_advanced_w);
+}
+
+/*
+ * Callback for the CANCEL button 'clicked' in the Advanced Wireless Settings window.
+ */
+static void
+on_advanced_cancel_bt_clicked(GtkWidget *button _U_, gpointer data)
+{
+ PAirpcapHandle ad = NULL;
+ gchar ebuf[AIRPCAP_ERRBUF_SIZE];
+
+ /* Retrieve object data */
+ GtkWidget *airpcap_advanced_w;
+ GtkWidget *channel_combo;
+ GtkWidget *capture_combo;
+ GtkWidget *crc_check;
+ GtkWidget *wrong_crc_combo;
+ GtkWidget *blink_bt;
+ GtkWidget *interface_combo;
+ GtkWidget *cancel_bt;
+ GtkWidget *ok_bt;
+
+ /* widgets in the toolbar */
+ GtkWidget *toolbar,
+ *toolbar_if_lb,
+ *toolbar_channel_cb,
+ *toolbar_wrong_crc_cb,
+ *advanced_bt;
+
+ /* Retrieve the GUI object pointers */
+ airpcap_advanced_w = GTK_WIDGET(data);
+ interface_combo = GTK_WIDGET(g_object_get_data(G_OBJECT(airpcap_advanced_w),AIRPCAP_ADVANCED_INTERFACE_KEY));
+ channel_combo = GTK_WIDGET(g_object_get_data(G_OBJECT(airpcap_advanced_w),AIRPCAP_ADVANCED_CHANNEL_KEY));
+ capture_combo = GTK_WIDGET(g_object_get_data(G_OBJECT(airpcap_advanced_w),AIRPCAP_ADVANCED_LINK_TYPE_KEY));
+ crc_check = GTK_WIDGET(g_object_get_data(G_OBJECT(airpcap_advanced_w),AIRPCAP_ADVANCED_FCS_CHECK_KEY));
+ wrong_crc_combo = GTK_WIDGET(g_object_get_data(G_OBJECT(airpcap_advanced_w),AIRPCAP_ADVANCED_FCS_FILTER_KEY));
+ blink_bt = GTK_WIDGET(g_object_get_data(G_OBJECT(airpcap_advanced_w),AIRPCAP_ADVANCED_BLINK_KEY));
+ cancel_bt = GTK_WIDGET(g_object_get_data(G_OBJECT(airpcap_advanced_w),AIRPCAP_ADVANCED_CANCEL_KEY));
+ ok_bt = GTK_WIDGET(g_object_get_data(G_OBJECT(airpcap_advanced_w),AIRPCAP_ADVANCED_OK_KEY));
+ advanced_bt = GTK_WIDGET(g_object_get_data(G_OBJECT(airpcap_advanced_w),AIRPCAP_ADVANCED_KEY));
+
+ toolbar = GTK_WIDGET(g_object_get_data(G_OBJECT(airpcap_advanced_w),AIRPCAP_TOOLBAR_KEY));
+
+ /* retrieve toolbar info */
+ toolbar_if_lb = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbar),AIRPCAP_TOOLBAR_INTERFACE_KEY));
+ toolbar_channel_cb = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbar),AIRPCAP_TOOLBAR_CHANNEL_KEY));
+ toolbar_wrong_crc_cb = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbar),AIRPCAP_TOOLBAR_FCS_FILTER_KEY));
+
+ /* Stop blinking ALL leds (go through the airpcap_if_list) */
+ if (airpcap_if_selected != NULL)
+ {
+ ad = airpcap_if_open(airpcap_if_selected->name, ebuf);
+ if (ad)
+ {
+ g_source_remove(airpcap_if_selected->tag);
+ airpcap_if_turn_led_on(ad, 0);
+ airpcap_if_selected->blinking = FALSE;
+ airpcap_if_selected->led = TRUE;
+ airpcap_if_close(ad);
+ }
+ }
+
+ /* reload the configuration!!! Configuration has not been saved but
+ the corresponding structure has been modified probably...*/
+ if (!airpcap_if_selected->saved)
+ {
+ airpcap_load_selected_if_configuration(airpcap_if_selected);
+ }
+
+ gtk_widget_destroy(airpcap_advanced_w);
+}
+
+
+/* Called to create the airpcap settings' window */
+void
+display_airpcap_advanced_cb(GtkWidget *w _U_, gpointer data)
+{
+ GtkWidget *airpcap_advanced_w;
+ GtkWidget *main_box;
+ GtkWidget *settings_sub_box;
+ GtkWidget *interface_fr;
+ GtkWidget *interface_al;
+ GtkWidget *interface_sub_h_box;
+ GtkWidget *interface_name_lb;
+ GtkWidget *blink_bt;
+ GtkWidget *interface_frame_lb;
+ GtkWidget *basic_parameters_fr;
+ GtkWidget *basic_parameters_al;
+ GtkWidget *basic_parameters_tb;
+ GtkWidget *channel_lb;
+ GtkWidget *channel_offset_lb;
+ GtkWidget *capture_type_lb;
+ GtkWidget *channel_cb;
+ GtkWidget *channel_offset_cb;
+ GtkWidget *capture_type_cb;
+ GtkWidget *fcs_ck;
+ GtkWidget *basic_parameters_fcs_h_box;
+ GtkWidget *basic_parameters_fcs_filter_lb;
+ GtkWidget *fcs_filter_cb;
+ GtkWidget *basic_parameters_frame_lb;
+ GtkWidget *low_buttons_h_box;
+ GtkWidget *left_h_button_box;
+ GtkWidget *right_h_button_box;
+ GtkWidget *ok_bt;
+ GtkWidget *apply_bt;
+ GtkWidget *cancel_bt;
+
+ /* widgets in the toolbar */
+ GtkWidget *toolbar,
+ *toolbar_if_lb,
+ *toolbar_channel_cb,
+ *toolbar_wrong_crc_cb;
+
+ /* user data - RETRIEVE pointers of toolbar widgets */
+ toolbar = GTK_WIDGET(data);
+ toolbar_if_lb = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbar),AIRPCAP_TOOLBAR_INTERFACE_KEY));
+ toolbar_channel_cb = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbar),AIRPCAP_TOOLBAR_CHANNEL_KEY));
+ toolbar_wrong_crc_cb = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbar),AIRPCAP_TOOLBAR_FCS_FILTER_KEY));
+
+ /* gray out the toolbar */
+ gtk_widget_set_sensitive(toolbar,FALSE);
+
+ /* main window */
+ /* global */
+
+ /* the selected is the active, for now */
+ airpcap_if_selected = airpcap_if_active;
+
+ /* Create the new window */
+ airpcap_advanced_w = dlg_window_new("Advanced Wireless Settings"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(airpcap_advanced_w), TRUE);
+
+ gtk_container_set_border_width (GTK_CONTAINER (airpcap_advanced_w), 5);
+ gtk_window_set_position (GTK_WINDOW (airpcap_advanced_w),
+ GTK_WIN_POS_CENTER);
+
+ gtk_window_set_resizable (GTK_WINDOW (airpcap_advanced_w), FALSE);
+ gtk_window_set_type_hint (GTK_WINDOW (airpcap_advanced_w), GDK_WINDOW_TYPE_HINT_DIALOG);
+
+ main_box = gtk_vbox_new (FALSE, 0);
+ gtk_widget_set_name (main_box, "main_box");
+ gtk_widget_show (main_box);
+ gtk_container_add (GTK_CONTAINER (airpcap_advanced_w), main_box);
+
+ settings_sub_box = gtk_vbox_new (FALSE, 0);
+ gtk_widget_set_name (settings_sub_box, "settings_sub_box");
+ gtk_widget_show (settings_sub_box);
+ gtk_box_pack_start (GTK_BOX (main_box), settings_sub_box, FALSE, TRUE, 0);
+
+ interface_fr = gtk_frame_new (NULL);
+ gtk_widget_set_name (interface_fr, "interface_fr");
+ gtk_widget_show (interface_fr);
+ gtk_box_pack_start (GTK_BOX (settings_sub_box), interface_fr, FALSE, FALSE,
+ 0);
+ gtk_container_set_border_width (GTK_CONTAINER (interface_fr), 10);
+
+ interface_al = gtk_alignment_new (0.5, 0.5, 1, 1);
+ gtk_widget_set_name (interface_al, "interface_al");
+ gtk_widget_show (interface_al);
+ gtk_container_add (GTK_CONTAINER (interface_fr), interface_al);
+ gtk_alignment_set_padding (GTK_ALIGNMENT (interface_al), 5, 5, 0, 0);
+
+ interface_sub_h_box = gtk_hbox_new (FALSE, 0);
+ gtk_widget_set_name (interface_sub_h_box, "interface_sub_h_box");
+ gtk_widget_show (interface_sub_h_box);
+ gtk_container_add (GTK_CONTAINER (interface_al), interface_sub_h_box);
+ gtk_container_set_border_width (GTK_CONTAINER (interface_sub_h_box), 5);
+
+ /* Fill the interface_box */
+ if (airpcap_if_active != NULL)
+ {
+ interface_name_lb = gtk_label_new(airpcap_if_active->description);
+ }
+ else
+ {
+ interface_name_lb = gtk_label_new("No airpcap interface found!");
+ gtk_widget_set_sensitive(main_box,FALSE);
+ }
+
+ gtk_widget_set_name (interface_name_lb, "interface_name_lb");
+ gtk_widget_show (interface_name_lb);
+ gtk_box_pack_start (GTK_BOX (interface_sub_h_box), interface_name_lb, TRUE,
+ FALSE, 0);
+
+ /* If it is NOT the 'Any' Interface */
+ if (!airpcap_if_is_any(airpcap_if_selected))
+ {
+ blink_bt = gtk_button_new_with_mnemonic ("Blink Led");
+ }
+ else /* It is the any interface, so it doesn't make sense to have 'Blink' button... */
+ {
+ blink_bt = gtk_button_new_with_mnemonic ("What's This?");
+ }
+ gtk_widget_set_name (blink_bt, "blink_bt");
+ gtk_widget_show (blink_bt);
+ gtk_box_pack_end (GTK_BOX (interface_sub_h_box), blink_bt, FALSE, FALSE, 0);
+
+ interface_frame_lb = gtk_label_new ("<b>Interface</b>");
+ gtk_widget_set_name (interface_frame_lb, "interface_frame_lb");
+ gtk_widget_show (interface_frame_lb);
+ gtk_frame_set_label_widget (GTK_FRAME (interface_fr), interface_frame_lb);
+ gtk_label_set_use_markup (GTK_LABEL (interface_frame_lb), TRUE);
+
+ basic_parameters_fr = gtk_frame_new (NULL);
+ gtk_widget_set_name (basic_parameters_fr, "basic_parameters_fr");
+ gtk_widget_show (basic_parameters_fr);
+ gtk_box_pack_start (GTK_BOX (settings_sub_box), basic_parameters_fr, TRUE,FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (basic_parameters_fr), 10);
+
+ basic_parameters_al = gtk_alignment_new (0.5, 0.5, 1, 1);
+ gtk_widget_set_name (basic_parameters_al, "basic_parameters_al");
+ gtk_widget_show (basic_parameters_al);
+ gtk_container_add (GTK_CONTAINER (basic_parameters_fr),basic_parameters_al);
+ gtk_alignment_set_padding (GTK_ALIGNMENT (basic_parameters_al), 10, 10, 0, 0);
+
+ basic_parameters_tb = gtk_table_new (2, 3, FALSE);
+ gtk_widget_set_name (basic_parameters_tb, "basic_parameters_tb");
+ gtk_widget_show (basic_parameters_tb);
+ gtk_container_add (GTK_CONTAINER (basic_parameters_al),
+ basic_parameters_tb);
+ gtk_container_set_border_width (GTK_CONTAINER (basic_parameters_tb), 5);
+ gtk_table_set_col_spacings (GTK_TABLE (basic_parameters_tb), 20);
+
+ channel_lb = gtk_label_new ("Channel:");
+ gtk_widget_set_name (channel_lb, "channel_lb");
+ gtk_widget_show (channel_lb);
+ gtk_table_attach (GTK_TABLE (basic_parameters_tb), channel_lb, 0, 1, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (channel_lb), 0, 0.5);
+
+ capture_type_lb = gtk_label_new ("Capture Type:");
+ gtk_widget_set_name (capture_type_lb, "capture_type_lb");
+ gtk_widget_show (capture_type_lb);
+ gtk_table_attach (GTK_TABLE (basic_parameters_tb), capture_type_lb, 0, 1, 2,
+ 3, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0),
+ 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (capture_type_lb), 0, 0.5);
+
+ /* Start: Channel offset label */
+ channel_offset_lb = gtk_label_new ("Channel Offset:");
+ gtk_widget_set_name (channel_offset_lb, "channel_offset_lb");
+ gtk_widget_show (channel_offset_lb);
+ gtk_table_attach (GTK_TABLE (basic_parameters_tb), channel_offset_lb, 0, 1, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (channel_offset_lb), 0, 0.5);
+ /* End: Channel offset label */
+
+ /* Start: Channel offset combo box */
+ channel_offset_cb = gtk_combo_box_text_new();
+ gtk_widget_set_name (channel_offset_cb, "channel_offset_cb");
+
+ airpcap_update_channel_offset_combo(airpcap_if_selected, airpcap_if_selected->channelInfo.Frequency, channel_offset_cb, FALSE);
+
+ gtk_widget_show(channel_offset_cb);
+
+ gtk_table_attach (GTK_TABLE (basic_parameters_tb), channel_offset_cb, 1, 2, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ /* End: Channel offset combo box */
+
+ channel_cb = gtk_combo_box_text_new();
+ gtk_widget_set_name (channel_cb, "channel_cb");
+ gtk_widget_show (channel_cb);
+ gtk_table_attach (GTK_TABLE (basic_parameters_tb), channel_cb, 1, 2, 0, 1,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ /* Select the current channel */
+ airpcap_update_channel_combo(GTK_WIDGET(channel_cb), airpcap_if_selected);
+
+ capture_type_cb = gtk_combo_box_text_new();
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(capture_type_cb), AIRPCAP_LINK_TYPE_NAME_802_11_ONLY);
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(capture_type_cb), AIRPCAP_LINK_TYPE_NAME_802_11_PLUS_RADIO);
+ if (airpcap_get_dll_state() == AIRPCAP_DLL_OK) {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(capture_type_cb), AIRPCAP_LINK_TYPE_NAME_802_11_PLUS_PPI);
+ }
+
+ gtk_widget_set_name (capture_type_cb, "capture_type_cb");
+ gtk_widget_show (capture_type_cb);
+ gtk_table_attach (GTK_TABLE (basic_parameters_tb), capture_type_cb, 1, 2, 2,
+ 3, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ /* Current interface value */
+ if (airpcap_if_selected != NULL)
+ {
+ if (airpcap_if_selected->linkType == AIRPCAP_LT_802_11_PLUS_RADIO){
+ gtk_combo_box_set_active(GTK_COMBO_BOX(capture_type_cb), AIRPCAP_LINK_TYPE_NUM_802_11_PLUS_RADIO);
+ }else if (airpcap_if_selected->linkType == AIRPCAP_LT_802_11_PLUS_PPI){
+ gtk_combo_box_set_active(GTK_COMBO_BOX(capture_type_cb), AIRPCAP_LINK_TYPE_NUM_802_11_PLUS_PPI);
+ } else {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(capture_type_cb), AIRPCAP_LINK_TYPE_NUM_802_11_ONLY);
+ }
+ }
+
+ fcs_ck = gtk_check_button_new_with_label ("Include 802.11 FCS in Frames");
+
+ gtk_widget_set_name (fcs_ck, "fcs_ck");
+
+ /* Fcs Presence check box */
+ if (airpcap_if_selected != NULL)
+ {
+ if (airpcap_if_selected->IsFcsPresent)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fcs_ck),TRUE);
+ else
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fcs_ck),FALSE);
+ }
+
+ gtk_widget_show (fcs_ck);
+ gtk_table_attach (GTK_TABLE (basic_parameters_tb), fcs_ck, 2, 3, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ basic_parameters_fcs_h_box = gtk_hbox_new (FALSE, 1);
+ gtk_widget_set_name (basic_parameters_fcs_h_box,
+ "basic_parameters_fcs_h_box");
+ gtk_widget_show (basic_parameters_fcs_h_box);
+ gtk_table_attach (GTK_TABLE (basic_parameters_tb),
+ basic_parameters_fcs_h_box, 2, 3, 2, 3,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (GTK_FILL), 3, 0);
+
+ basic_parameters_fcs_filter_lb = gtk_label_new ("FCS Filter:");
+ gtk_widget_set_name (basic_parameters_fcs_filter_lb,
+ "basic_parameters_fcs_filter_lb");
+ gtk_widget_show (basic_parameters_fcs_filter_lb);
+ gtk_box_pack_start (GTK_BOX (basic_parameters_fcs_h_box),
+ basic_parameters_fcs_filter_lb, FALSE, FALSE, 0);
+
+ fcs_filter_cb = gtk_combo_box_text_new();
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(fcs_filter_cb), airpcap_get_validation_name(AIRPCAP_VT_ACCEPT_EVERYTHING));
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(fcs_filter_cb), airpcap_get_validation_name(AIRPCAP_VT_ACCEPT_CORRECT_FRAMES));
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(fcs_filter_cb), airpcap_get_validation_name(AIRPCAP_VT_ACCEPT_CORRUPT_FRAMES));
+ gtk_combo_box_set_active(GTK_COMBO_BOX(fcs_filter_cb), 0);
+ gtk_widget_set_name (fcs_filter_cb, "fcs_filter_cb");
+ gtk_widget_show (fcs_filter_cb);
+ gtk_box_pack_start (GTK_BOX (basic_parameters_fcs_h_box), fcs_filter_cb,
+ FALSE, FALSE, 0);
+ gtk_widget_set_size_request (fcs_filter_cb, 112, -1);
+
+ if (airpcap_if_selected != NULL)
+ {
+ airpcap_validation_type_combo_set_by_type(fcs_filter_cb, airpcap_if_selected->CrcValidationOn);
+ }
+
+ basic_parameters_frame_lb = gtk_label_new ("<b>Basic Parameters</b>");
+ gtk_widget_set_name (basic_parameters_frame_lb,
+ "basic_parameters_frame_lb");
+ gtk_widget_show (basic_parameters_frame_lb);
+
+ gtk_frame_set_label_widget (GTK_FRAME (basic_parameters_fr),basic_parameters_frame_lb);
+ gtk_label_set_use_markup (GTK_LABEL (basic_parameters_frame_lb), TRUE);
+
+ low_buttons_h_box = gtk_hbox_new (FALSE, 0);
+ gtk_widget_set_name (low_buttons_h_box, "low_buttons_h_box");
+ gtk_widget_show (low_buttons_h_box);
+ gtk_box_pack_end (GTK_BOX (main_box), low_buttons_h_box, FALSE, FALSE, 0);
+
+ left_h_button_box = gtk_hbutton_box_new ();
+ gtk_widget_set_name (left_h_button_box, "left_h_button_box");
+ gtk_widget_show (left_h_button_box);
+ gtk_box_pack_start (GTK_BOX (low_buttons_h_box), left_h_button_box, FALSE,
+ FALSE, 0);
+
+ right_h_button_box = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_CANCEL, NULL);
+ gtk_widget_show (right_h_button_box);
+ gtk_box_pack_end (GTK_BOX (low_buttons_h_box), right_h_button_box, FALSE,
+ FALSE, 0);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (right_h_button_box),
+ GTK_BUTTONBOX_END);
+
+ ok_bt = g_object_get_data(G_OBJECT(right_h_button_box), GTK_STOCK_OK);
+ apply_bt = g_object_get_data(G_OBJECT(right_h_button_box), GTK_STOCK_APPLY);
+ cancel_bt = g_object_get_data(G_OBJECT(right_h_button_box), GTK_STOCK_CANCEL);
+
+ /* Connect the callbacks */
+ g_signal_connect (airpcap_advanced_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect (airpcap_advanced_w, "destroy", G_CALLBACK(on_airpcap_advanced_destroy), airpcap_advanced_w);
+
+ if (!airpcap_if_is_any(airpcap_if_selected))
+ {
+ g_signal_connect (blink_bt, "clicked", G_CALLBACK(on_blink_bt_clicked), NULL);
+ }
+ else
+ {
+ g_signal_connect (blink_bt, "clicked", G_CALLBACK(on_what_s_this_bt_clicked), NULL);
+ }
+
+ g_signal_connect (channel_cb, "changed", G_CALLBACK(airpcap_channel_changed_noset_cb), channel_offset_cb);
+ /* We don't attach the channel offset combo because we don't want it changing anything yet. */
+ g_signal_connect (capture_type_cb, "changed", G_CALLBACK(on_capture_type_cb_changed), NULL);
+ g_signal_connect (fcs_ck, "toggled", G_CALLBACK(on_fcs_ck_toggled), NULL);
+ g_signal_connect (fcs_filter_cb, "changed", G_CALLBACK(on_fcs_filter_cb_changed), NULL);
+ g_signal_connect (apply_bt, "clicked", G_CALLBACK(on_advanced_apply_bt_clicked), airpcap_advanced_w);
+ g_signal_connect (ok_bt,"clicked", G_CALLBACK(on_advanced_ok_bt_clicked), airpcap_advanced_w);
+ g_signal_connect (cancel_bt,"clicked", G_CALLBACK(on_advanced_cancel_bt_clicked), airpcap_advanced_w);
+
+ /* Different because the window will be closed ... */
+ /*window_set_cancel_button(airpcap_advanced_w, ok_bt, window_cancel_button_cb);
+ window_set_cancel_button(airpcap_advanced_w, cancel_bt, window_cancel_button_cb);*/
+
+
+ /* Store pointers to all widgets, for use by lookup_widget(). */
+ g_object_set_data (G_OBJECT(airpcap_advanced_w), AIRPCAP_ADVANCED_BLINK_KEY, blink_bt);
+ g_object_set_data (G_OBJECT(airpcap_advanced_w), AIRPCAP_ADVANCED_CHANNEL_KEY,channel_cb);
+ g_object_set_data (G_OBJECT(airpcap_advanced_w), AIRPCAP_ADVANCED_CHANNEL_OFFSET_KEY, channel_offset_cb);
+ g_object_set_data (G_OBJECT(airpcap_advanced_w), AIRPCAP_ADVANCED_LINK_TYPE_KEY,capture_type_cb);
+ g_object_set_data (G_OBJECT(airpcap_advanced_w), AIRPCAP_ADVANCED_FCS_CHECK_KEY, fcs_ck);
+ g_object_set_data (G_OBJECT(airpcap_advanced_w), AIRPCAP_ADVANCED_FCS_FILTER_KEY, fcs_filter_cb);
+ g_object_set_data (G_OBJECT(airpcap_advanced_w), AIRPCAP_ADVANCED_OK_KEY, ok_bt);
+ g_object_set_data (G_OBJECT(airpcap_advanced_w), AIRPCAP_ADVANCED_CANCEL_KEY, cancel_bt);
+
+ /*
+ * I will need the toolbar and the main widget in some callback,
+ * so I will add the toolbar pointer to the airpcap_advanced_w
+ */
+ g_object_set_data(G_OBJECT(airpcap_advanced_w),AIRPCAP_TOOLBAR_KEY,toolbar);
+
+ /* At the end, so that it appears completely all together ... */
+ gtk_widget_show (airpcap_advanced_w);
+}
+
+/*
+ * Callback for the OK button 'clicked' in the Decryption Key Management window.
+ */
+static void
+on_key_management_ok_bt_clicked(GtkWidget *button, gpointer data)
+{
+ /* advanced window */
+ GtkWidget *key_management_w;
+
+ /* retrieve main window */
+ key_management_w = GTK_WIDGET(data);
+
+ /* Apply the current decryption preferences */
+ on_key_management_apply_bt_clicked(button, data);
+
+ /* Save the preferences to preferences file!!! */
+ write_prefs_to_file();
+
+ gtk_widget_destroy(key_management_w);
+}
+
+/*
+ * Callback for the CANCEL button 'clicked' in the Decryption Key Management window.
+ */
+static void
+on_key_management_cancel_bt_clicked(GtkWidget *button _U_, gpointer data)
+{
+ /* Retrieve object data */
+ GtkWidget *key_management_w;
+ GtkWidget *cancel_bt;
+ GtkWidget *ok_bt;
+ GtkListStore *key_list_store;
+
+ /* widgets in the toolbar */
+ GtkWidget *toolbar,
+ *toolbar_decryption_ck,
+ *key_management_bt;
+
+ /* Retrieve the GUI object pointers */
+ key_management_w = GTK_WIDGET(data);
+ cancel_bt = GTK_WIDGET(g_object_get_data(G_OBJECT(key_management_w),AIRPCAP_ADVANCED_CANCEL_KEY));
+ ok_bt = GTK_WIDGET(g_object_get_data(G_OBJECT(key_management_w),AIRPCAP_ADVANCED_OK_KEY));
+ key_list_store = GTK_LIST_STORE(g_object_get_data(G_OBJECT(key_management_w),AIRPCAP_ADVANCED_KEYLIST_KEY));
+ key_management_bt = GTK_WIDGET(g_object_get_data(G_OBJECT(key_management_w),AIRPCAP_ADVANCED_KEY));
+
+ toolbar = GTK_WIDGET(g_object_get_data(G_OBJECT(key_management_w),AIRPCAP_TOOLBAR_KEY));
+
+ /* retrieve toolbar info */
+ toolbar_decryption_ck = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbar),AIRPCAP_TOOLBAR_DECRYPTION_KEY));
+
+ gtk_widget_destroy(key_management_w);
+}
+
+/* Called to create the key management window */
+void
+display_airpcap_key_management_cb(GtkWidget *w _U_, gpointer data)
+{
+ GtkWidget *key_management_w;
+ GtkWidget *main_box;
+ GtkWidget *keys_fr;
+ GtkWidget *keys_al;
+ GtkWidget *keys_h_sub_box;
+ GtkWidget *decryption_mode_tb;
+ GtkWidget *decryption_mode_lb;
+ GtkWidget *decryption_mode_cb;
+ GtkWidget *keys_v_sub_box;
+ GtkWidget *keys_scrolled_w;
+ GtkListStore *key_list_store;
+ GtkWidget *key_list;
+ GtkWidget *key_v_button_box;
+ GtkWidget *add_new_key_bt;
+ GtkWidget *remove_key_bt;
+ GtkWidget *edit_key_bt;
+ GtkWidget *move_key_up_bt;
+ GtkWidget *move_key_down_bt;
+ GtkWidget *keys_frame_lb;
+ GtkWidget *low_buttons_h_box;
+ GtkWidget *left_h_button_box;
+ GtkWidget *right_h_button_box;
+ GtkWidget *ok_bt;
+ GtkWidget *apply_bt;
+ GtkWidget *cancel_bt;
+
+ /* widgets in the toolbar */
+ GtkWidget *toolbar,
+ *toolbar_decryption_ck;
+
+ /* key list */
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSortable *sortable;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+
+ /* Selected row/column structure */
+ airpcap_key_ls_selected_info_t *key_ls_selected_item;
+ key_ls_selected_item = (airpcap_key_ls_selected_info_t*)g_malloc(sizeof(airpcap_key_ls_selected_info_t));
+ key_ls_selected_item->row = NO_ROW_SELECTED;
+
+ /* user data - RETRIEVE pointers of toolbar widgets */
+ toolbar = GTK_WIDGET(data);
+ toolbar_decryption_ck = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbar),AIRPCAP_TOOLBAR_DECRYPTION_KEY));
+
+ /* gray out the toolbar */
+ gtk_widget_set_sensitive(toolbar,FALSE);
+
+ /* main window */
+ /* global */
+
+ /* the selected is the active, for now */
+ airpcap_if_selected = airpcap_if_active;
+
+ /* Create the new window */
+ key_management_w = dlg_window_new("Decryption Key Management"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(key_management_w), TRUE);
+
+ gtk_container_set_border_width (GTK_CONTAINER (key_management_w), 5);
+ gtk_window_set_position (GTK_WINDOW (key_management_w),
+ GTK_WIN_POS_CENTER);
+
+ gtk_window_set_resizable (GTK_WINDOW (key_management_w), FALSE);
+ gtk_window_set_type_hint (GTK_WINDOW (key_management_w), GDK_WINDOW_TYPE_HINT_DIALOG);
+
+ main_box = gtk_vbox_new (FALSE, 0);
+ gtk_widget_set_name (main_box, "main_box");
+ gtk_widget_show (main_box);
+ gtk_container_add (GTK_CONTAINER (key_management_w), main_box);
+
+ keys_fr = gtk_frame_new (NULL);
+ gtk_widget_set_name (keys_fr, "keys_fr");
+ gtk_widget_show (keys_fr);
+ gtk_box_pack_start (GTK_BOX (main_box), keys_fr, FALSE, FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (keys_fr), 10);
+
+ keys_al = gtk_alignment_new (0.5, 0.5, 1, 1);
+ gtk_widget_set_name (keys_al, "keys_al");
+ gtk_widget_show (keys_al);
+ gtk_container_add (GTK_CONTAINER (keys_fr), keys_al);
+ gtk_container_set_border_width (GTK_CONTAINER (keys_al), 5);
+
+ gtk_alignment_set_padding (GTK_ALIGNMENT (keys_al), 0, 0, 12, 0);
+
+ keys_h_sub_box = gtk_vbox_new (FALSE, 0);
+ gtk_widget_set_name (keys_h_sub_box, "keys_h_sub_box");
+ gtk_widget_show (keys_h_sub_box);
+ gtk_container_add (GTK_CONTAINER (keys_al), keys_h_sub_box);
+
+ decryption_mode_tb = gtk_table_new (1, 2, FALSE);
+ gtk_widget_set_name (decryption_mode_tb, "decryption_mode_tb");
+ gtk_widget_show (decryption_mode_tb);
+ gtk_box_pack_start (GTK_BOX (keys_h_sub_box), decryption_mode_tb, FALSE,
+ FALSE, 0);
+ gtk_table_set_col_spacings (GTK_TABLE (decryption_mode_tb), 6);
+
+ decryption_mode_lb = gtk_label_new ("Select Decryption Mode");
+ gtk_widget_set_name (decryption_mode_lb, "decryption_mode_lb");
+ gtk_widget_show (decryption_mode_lb);
+ gtk_table_attach (GTK_TABLE (decryption_mode_tb), decryption_mode_lb, 1,
+ 2, 0, 1, (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (decryption_mode_lb), 0, 0.5);
+
+ decryption_mode_cb = gtk_combo_box_text_new();
+ update_decryption_mode_list(decryption_mode_cb);
+ gtk_widget_set_name (decryption_mode_cb, "decryption_mode_cb");
+ gtk_widget_show (decryption_mode_cb);
+ gtk_table_attach (GTK_TABLE (decryption_mode_tb), decryption_mode_cb, 0,
+ 1, 0, 1, (GtkAttachOptions) (0), (GtkAttachOptions) (0),
+ 0, 0);
+ gtk_widget_set_size_request (decryption_mode_cb, 83, -1);
+
+ /* Set correct decryption mode!!!! */
+ update_decryption_mode(decryption_mode_cb);
+
+ keys_v_sub_box = gtk_hbox_new (FALSE, 0);
+ gtk_widget_set_name (keys_v_sub_box, "keys_v_sub_box");
+ gtk_widget_show (keys_v_sub_box);
+ gtk_box_pack_start (GTK_BOX (keys_h_sub_box), keys_v_sub_box, TRUE, TRUE, 0);
+
+ keys_scrolled_w = gtk_scrolled_window_new (NULL, NULL);
+ gtk_widget_set_name (keys_scrolled_w, "keys_scrolled_w");
+ gtk_widget_show (keys_scrolled_w);
+ gtk_box_pack_start (GTK_BOX (keys_v_sub_box), keys_scrolled_w, TRUE, TRUE,
+ 0);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (keys_scrolled_w), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+
+
+
+ /* Create the store */
+ key_list_store = gtk_list_store_new(KL_NUM_COLS,
+ G_TYPE_STRING, /* Type */
+ G_TYPE_STRING /* Key */
+ , G_TYPE_STRING /* SSID */
+ );
+
+ /* Create a view */
+ key_list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(key_list_store));
+
+ sortable = GTK_TREE_SORTABLE(key_list_store);
+
+ /* Speed up the list display */
+ gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(key_list), TRUE);
+
+ /* Setup the sortable columns */
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(key_list), FALSE);
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref(G_OBJECT(key_list_store));
+
+ /*
+ * Create the first column packet, associating the "text" attribute of the
+ * cell_renderer to the first column of the model
+ */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Type", renderer,
+ "text", KL_COL_TYPE, NULL);
+ gtk_tree_view_column_set_sort_column_id(column, KL_COL_TYPE);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+ gtk_tree_view_column_set_fixed_width(column, 100);
+ /* Add the column to the view. */
+ gtk_tree_view_append_column(GTK_TREE_VIEW(key_list), column);
+
+ /* Key */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Key", renderer,
+ "text", KL_COL_KEY, NULL);
+ gtk_tree_view_column_set_sort_column_id(column, KL_COL_KEY);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 120);
+ gtk_tree_view_column_set_fixed_width(column, 200);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(key_list), column);
+
+ /* SSID */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("SSID", renderer,
+ "text", KL_COL_SSID,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, KL_COL_SSID);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_column_set_fixed_width(column, 150);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(key_list), column);
+
+ /* Now enable the sorting of each column */
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(key_list), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(key_list), TRUE);
+
+ /* Setup the selection handler */
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(key_list));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+
+ gtk_widget_show (key_list);
+
+ gtk_container_add (GTK_CONTAINER (keys_scrolled_w), key_list);
+
+ key_v_button_box = gtk_vbutton_box_new ();
+ gtk_widget_set_name (key_v_button_box, "key_v_button_box");
+ gtk_widget_show (key_v_button_box);
+ gtk_box_pack_start (GTK_BOX (keys_v_sub_box), key_v_button_box, FALSE, TRUE,
+ 0);
+
+ add_new_key_bt = gtk_button_new_from_stock(GTK_STOCK_NEW);
+ gtk_widget_set_name (add_new_key_bt, "add_new_key_bt");
+ gtk_widget_show (add_new_key_bt);
+ gtk_container_add (GTK_CONTAINER (key_v_button_box), add_new_key_bt);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default (add_new_key_bt, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS (add_new_key_bt, GTK_CAN_DEFAULT);
+#endif
+
+ edit_key_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_EDIT);
+ gtk_widget_set_name (edit_key_bt, "edit_key_bt");
+ gtk_widget_show (edit_key_bt);
+ gtk_container_add (GTK_CONTAINER (key_v_button_box), edit_key_bt);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default (edit_key_bt, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS (edit_key_bt, GTK_CAN_DEFAULT);
+#endif
+
+ remove_key_bt = gtk_button_new_from_stock(GTK_STOCK_DELETE);
+ gtk_widget_set_name (remove_key_bt, "remove_key_bt");
+ gtk_widget_show (remove_key_bt);
+ gtk_container_add (GTK_CONTAINER (key_v_button_box), remove_key_bt);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default (remove_key_bt, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS (remove_key_bt, GTK_CAN_DEFAULT);
+#endif
+
+ move_key_up_bt = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
+ gtk_widget_set_name (move_key_up_bt, "move_key_up_bt");
+ gtk_widget_show (move_key_up_bt);
+ gtk_container_add (GTK_CONTAINER (key_v_button_box), move_key_up_bt);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default (move_key_up_bt, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS (move_key_up_bt, GTK_CAN_DEFAULT);
+#endif
+
+ move_key_down_bt = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
+ gtk_widget_set_name (move_key_down_bt, "move_key_down_bt");
+ gtk_widget_show (move_key_down_bt);
+ gtk_container_add (GTK_CONTAINER (key_v_button_box), move_key_down_bt);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default (move_key_down_bt, GTK_CAN_DEFAULT);
+#else
+ GTK_WIDGET_SET_FLAGS (move_key_down_bt, GTK_CAN_DEFAULT);
+#endif
+
+ keys_frame_lb = gtk_label_new ("<b>Decryption Keys</b>");
+ gtk_widget_set_name (keys_frame_lb, "keys_frame_lb");
+ gtk_widget_show (keys_frame_lb);
+
+ gtk_frame_set_label_widget (GTK_FRAME (keys_fr), keys_frame_lb);
+ gtk_label_set_use_markup (GTK_LABEL (keys_frame_lb), TRUE);
+
+ low_buttons_h_box = gtk_hbox_new (FALSE, 0);
+ gtk_widget_set_name (low_buttons_h_box, "low_buttons_h_box");
+ gtk_widget_show (low_buttons_h_box);
+ gtk_box_pack_end (GTK_BOX (main_box), low_buttons_h_box, FALSE, FALSE, 0);
+
+ left_h_button_box = gtk_hbutton_box_new ();
+ gtk_widget_set_name (left_h_button_box, "left_h_button_box");
+ gtk_widget_show (left_h_button_box);
+ gtk_box_pack_start (GTK_BOX (low_buttons_h_box), left_h_button_box, FALSE,
+ FALSE, 0);
+
+ right_h_button_box = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_CANCEL, NULL);
+ gtk_widget_set_name (right_h_button_box, "right_h_button_box");
+ gtk_widget_show (right_h_button_box);
+ gtk_box_pack_end (GTK_BOX (low_buttons_h_box), right_h_button_box, FALSE,
+ FALSE, 0);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (right_h_button_box),
+ GTK_BUTTONBOX_END);
+
+ ok_bt = g_object_get_data(G_OBJECT(right_h_button_box), GTK_STOCK_OK);
+ apply_bt = g_object_get_data(G_OBJECT(right_h_button_box), GTK_STOCK_APPLY);
+ cancel_bt = g_object_get_data(G_OBJECT(right_h_button_box), GTK_STOCK_CANCEL);
+
+ /* Connect the callbacks */
+ g_signal_connect (key_management_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect (key_management_w, "destroy", G_CALLBACK(on_key_management_destroy), key_management_w);
+ g_signal_connect (add_new_key_bt, "clicked", G_CALLBACK(on_add_new_key_bt_clicked), key_management_w);
+ g_signal_connect (remove_key_bt, "clicked", G_CALLBACK(on_remove_key_bt_clicked), key_management_w);
+ g_signal_connect (edit_key_bt, "clicked", G_CALLBACK(on_edit_key_bt_clicked), key_management_w);
+ g_signal_connect (move_key_up_bt, "clicked", G_CALLBACK(on_move_key_up_bt_clicked), key_list);
+ g_signal_connect (move_key_down_bt, "clicked", G_CALLBACK(on_move_key_down_bt_clicked), key_list);
+ g_signal_connect (apply_bt, "clicked", G_CALLBACK(on_key_management_apply_bt_clicked), key_management_w);
+ g_signal_connect (ok_bt, "clicked", G_CALLBACK(on_key_management_ok_bt_clicked), key_management_w);
+ g_signal_connect (cancel_bt, "clicked", G_CALLBACK(on_key_management_cancel_bt_clicked), key_management_w);
+ g_signal_connect (selection, "changed", G_CALLBACK(on_key_list_select_row), key_management_w);
+ g_signal_connect (key_list_store, "rows_reordered", G_CALLBACK(on_key_list_reorder), key_management_w);
+
+ /* Different because the window will be closed ... */
+ /*window_set_cancel_button(key_management_w, ok_bt, window_cancel_button_cb);
+ window_set_cancel_button(key_management_w, cancel_bt, window_cancel_button_cb);*/
+
+ g_object_set_data (G_OBJECT(key_management_w), AIRPCAP_ADVANCED_EDIT_KEY_SELECTION_KEY,selection);
+
+ /* Store pointers to all widgets, for use by lookup_widget(). */
+ g_object_set_data (G_OBJECT(key_management_w), AIRPCAP_ADVANCED_DECRYPTION_MODE_KEY, decryption_mode_cb);
+ g_object_set_data (G_OBJECT(key_management_w), AIRPCAP_ADVANCED_KEYLIST_KEY, key_list_store);
+ g_object_set_data (G_OBJECT(key_management_w), AIRPCAP_ADVANCED_OK_KEY, ok_bt);
+ g_object_set_data (G_OBJECT(key_management_w), AIRPCAP_ADVANCED_CANCEL_KEY, cancel_bt);
+
+ /* Enable / disable buttons */
+ g_object_set_data (G_OBJECT(key_management_w), AIRPCAP_KEY_MGMT_NEW_KEY, add_new_key_bt);
+ g_object_set_data (G_OBJECT(key_management_w), AIRPCAP_KEY_MGMT_EDIT_KEY, edit_key_bt);
+ g_object_set_data (G_OBJECT(key_management_w), AIRPCAP_KEY_MGMT_DELETE_KEY, remove_key_bt);
+ g_object_set_data (G_OBJECT(key_management_w), AIRPCAP_KEY_MGMT_UP_KEY, move_key_up_bt);
+ g_object_set_data (G_OBJECT(key_management_w), AIRPCAP_KEY_MGMT_DOWN_KEY, move_key_down_bt);
+
+ /*
+ * I will need the toolbar and the main widget in some callback,
+ * so I will add the toolbar pointer to the key_management_w
+ */
+ g_object_set_data(G_OBJECT(key_management_w),AIRPCAP_TOOLBAR_KEY,toolbar);
+ g_object_set_data (G_OBJECT(key_management_w), AIRPCAP_TOOLBAR_DECRYPTION_KEY, toolbar_decryption_ck);
+
+ /* FIRST OF ALL, CHECK THE KEY COLLECTIONS */
+ /*
+ * This will read the decryption keys from the preferences file, and will store
+ * them into the registry...
+ */
+ if (!airpcap_check_decryption_keys(airpcap_if_list))
+ {
+ /* Ask the user what to do ...*/
+ airpcap_keys_check_w(key_management_w,NULL);
+ }
+ else /* Keys from lists are equals, or Wireshark has got no keys */
+ {
+ airpcap_load_decryption_keys(airpcap_if_list);
+ airpcap_fill_key_list(key_list_store);
+ /* At the end, so that it appears completely all together ... */
+ gtk_widget_show (key_management_w);
+ }
+
+ gtk_tree_model_get_iter_first(GTK_TREE_MODEL(key_list_store), &iter);
+ gtk_tree_selection_select_iter(selection, &iter);
+}
+
+
+static void
+on_keys_check_cancel_bt_clicked (GtkWidget *button _U_, gpointer user_data)
+{
+ GtkWidget *key_management_w;
+ GtkWidget *keys_check_w;
+ GtkListStore *key_list_store;
+
+ keys_check_w = GTK_WIDGET(user_data);
+
+ key_management_w = g_object_get_data(G_OBJECT(keys_check_w),AIRPCAP_CHECK_WINDOW_KEY);
+
+ /* w may be NULL if airpcap_keys_check_w() has been called while Wireshark was loading,
+ and is not NULL if it was called when the Key Management widget has been clicked */
+ if (key_management_w != NULL)
+ {
+ /* ... */
+ key_list_store = g_object_get_data(G_OBJECT(key_management_w),AIRPCAP_ADVANCED_KEYLIST_KEY);
+ airpcap_fill_key_list(key_list_store);
+ gtk_widget_show (key_management_w);
+ }
+
+ gtk_widget_destroy(keys_check_w);
+}
+
+static void
+on_merge_bt_clicked (GtkWidget* button _U_, gpointer user_data)
+{
+ GtkWidget *key_management_w;
+ GtkWidget *keys_check_w;
+ GtkListStore *key_list_store;
+
+ guint n_adapters = 0;
+ guint n_wireshark_keys = 0;
+ guint n_driver_keys = 0;
+ guint n_curr_adapter_keys = 0;
+ guint n_total_keys = 0;
+ guint n_merged_keys = 0;
+ guint i = 0;
+
+ GList* wireshark_keys=NULL;
+ GList* driver_keys=NULL;
+ GList* current_adapter_keys=NULL;
+ GList* merged_list = NULL;
+ GList* merged_list_tmp = NULL;
+
+ airpcap_if_info_t* curr_adapter;
+
+ keys_check_w = GTK_WIDGET(user_data);
+
+ key_management_w = g_object_get_data(G_OBJECT(keys_check_w),AIRPCAP_CHECK_WINDOW_KEY);
+
+ n_adapters = g_list_length(airpcap_if_list);
+
+ /* Retrieve Wireshark keys */
+ wireshark_keys = get_wireshark_keys();
+ n_wireshark_keys = g_list_length(wireshark_keys);
+ n_total_keys += n_wireshark_keys;
+
+ merged_list = merge_key_list(wireshark_keys,NULL);
+
+ /* Retrieve AirPcap driver's keys */
+ driver_keys = get_airpcap_driver_keys();
+ n_driver_keys = g_list_length(driver_keys);
+ n_total_keys += n_driver_keys;
+
+ merged_list = merge_key_list(merged_list,driver_keys);
+
+ /* NOW wireshark_keys and driver_keys ARE no more needed... at the end, we will have to free them! */
+ for (i = 0; i<n_adapters; i++)
+ {
+ curr_adapter = (airpcap_if_info_t*)g_list_nth_data(airpcap_if_list,i);
+ current_adapter_keys = get_airpcap_device_keys(curr_adapter);
+ n_curr_adapter_keys = g_list_length(current_adapter_keys);
+
+ merged_list_tmp = merged_list;
+ merged_list = merge_key_list(merged_list_tmp,current_adapter_keys);
+ free_key_list(merged_list_tmp);
+
+ n_total_keys += n_curr_adapter_keys;
+ }
+
+ n_merged_keys = g_list_length(merged_list);
+
+ /* Set up this new list as default for Wireshark and Adapters... */
+ airpcap_save_decryption_keys(merged_list,airpcap_if_list);
+
+ /* Write the preferences to the preferences file */
+ write_prefs_to_file();
+
+ free_key_list(wireshark_keys);
+ free_key_list(driver_keys);
+
+ gtk_widget_destroy(keys_check_w);
+
+ /* w may be NULL if airpcap_keys_check_w() has been called while Wireshark was loading,
+ and is not NULL if it was called when the Key Management widget has been clicked */
+ if (key_management_w != NULL)
+ {
+ /* ... */
+ key_list_store = g_object_get_data(G_OBJECT(key_management_w),AIRPCAP_ADVANCED_KEYLIST_KEY);
+ airpcap_fill_key_list(key_list_store);
+ gtk_widget_show (key_management_w);
+ }
+}
+
+static void
+on_keep_bt_clicked (GtkWidget *button _U_, gpointer user_data)
+{
+ GtkWidget *key_management_w;
+ GtkWidget *keys_check_w;
+ GtkListStore *key_list_store=NULL;
+
+ GList* wireshark_keys=NULL;
+ guint n_wireshark_keys = 0;
+
+ GList* merged_keys=NULL;
+ guint n_merged_keys = 0;
+
+ guint n_adapters=0;
+ guint n_total_keys=0;
+
+ keys_check_w = GTK_WIDGET(user_data);
+
+ key_management_w = g_object_get_data(G_OBJECT(keys_check_w),AIRPCAP_CHECK_WINDOW_KEY);
+
+ n_adapters = g_list_length(airpcap_if_list);
+
+ /* Retrieve Wireshark keys */
+ wireshark_keys = get_wireshark_keys();
+ n_wireshark_keys = g_list_length(wireshark_keys);
+ n_total_keys += n_wireshark_keys;
+
+ merged_keys = merge_key_list(wireshark_keys,NULL);
+ n_merged_keys = g_list_length(merged_keys);
+
+ /* Set up this new list as default for Wireshark and Adapters... */
+ airpcap_save_decryption_keys(merged_keys,airpcap_if_list);
+
+ /* Write the preferences to the preferences file (here is not needed, by the way)*/
+ write_prefs_to_file();
+
+ /* Free the memory */
+ free_key_list(wireshark_keys);
+
+ /* Close the window */
+ gtk_widget_destroy(keys_check_w);
+
+ /* w may be NULL if airpcap_keys_check_w() has been called while Wireshark was loading,
+ and is not NULL if it was called when the Key Management widget has been clicked */
+ if (key_management_w != NULL)
+ {
+ /* ... */
+ key_list_store = g_object_get_data(G_OBJECT(key_management_w),AIRPCAP_ADVANCED_KEYLIST_KEY);
+ airpcap_fill_key_list(key_list_store);
+ gtk_widget_show (key_management_w);
+ }
+}
+
+static void
+on_import_bt_clicked (GtkWidget* button _U_, gpointer user_data)
+{
+ GtkWidget *key_management_w;
+ GtkWidget *keys_check_w;
+ GtkListStore *key_list_store;
+
+ guint n_adapters = 0;
+ guint n_wireshark_keys = 0;
+ guint n_driver_keys = 0;
+ guint n_curr_adapter_keys = 0;
+ guint n_total_keys = 0;
+ guint n_merged_keys = 0;
+ guint i = 0;
+
+ GList* wireshark_keys=NULL;
+ GList* driver_keys=NULL;
+ GList* current_adapter_keys=NULL;
+ GList* merged_list = NULL;
+ GList* merged_list_tmp = NULL;
+
+ airpcap_if_info_t* curr_adapter;
+
+ keys_check_w = GTK_WIDGET(user_data);
+
+ key_management_w = g_object_get_data(G_OBJECT(keys_check_w),AIRPCAP_CHECK_WINDOW_KEY);
+
+ n_adapters = g_list_length(airpcap_if_list);
+
+ wireshark_keys = get_wireshark_keys();
+ n_wireshark_keys = g_list_length(wireshark_keys);
+ n_total_keys += n_wireshark_keys;
+
+ /* Retrieve AirPcap driver's keys */
+ driver_keys = get_airpcap_driver_keys();
+ n_driver_keys = g_list_length(driver_keys);
+ n_total_keys += n_driver_keys;
+
+ merged_list = merge_key_list(merged_list,driver_keys);
+
+ /* NOW wireshark_keys IS no more needed... at the end, we will have to free it! */
+ for (i = 0; i<n_adapters; i++)
+ {
+ curr_adapter = (airpcap_if_info_t*)g_list_nth_data(airpcap_if_list,i);
+ current_adapter_keys = get_airpcap_device_keys(curr_adapter);
+ n_curr_adapter_keys = g_list_length(current_adapter_keys);
+
+ merged_list_tmp = merged_list;
+ merged_list = merge_key_list(merged_list_tmp,current_adapter_keys);
+ free_key_list(merged_list_tmp);
+
+ n_total_keys += n_curr_adapter_keys;
+ }
+
+ n_merged_keys = g_list_length(merged_list);
+
+ /* Set up this new list as default for Wireshark and Adapters... */
+ airpcap_save_decryption_keys(merged_list,airpcap_if_list);
+
+ /* Write the preferences to the preferences file */
+ write_prefs_to_file();
+
+ free_key_list(wireshark_keys);
+ free_key_list(driver_keys);
+
+ gtk_widget_destroy(keys_check_w);
+
+ /* w may be NULL if airpcap_keys_check_w() has been called while Wireshark was loading,
+ and is not NULL if it was called when the Key Management widget has been clicked */
+ if (key_management_w != NULL)
+ {
+ /* ... */
+ key_list_store = g_object_get_data(G_OBJECT(key_management_w),AIRPCAP_ADVANCED_KEYLIST_KEY);
+ airpcap_fill_key_list(key_list_store);
+ gtk_widget_show (key_management_w);
+ }
+}
+
+static void
+on_ignore_bt_clicked (GtkWidget* button _U_, gpointer user_data)
+{
+ GtkWidget *key_management_w;
+ GtkWidget *keys_check_w;
+ GtkListStore *key_list_store;
+
+ keys_check_w = GTK_WIDGET(user_data);
+
+ key_management_w = g_object_get_data(G_OBJECT(keys_check_w),AIRPCAP_CHECK_WINDOW_KEY);
+
+ /* w may be NULL if airpcap_keys_check_w() has been called while Wireshark was loading,
+ and is not NULL if it was called when the Key Management widget has been clicked */
+ if (key_management_w != NULL)
+ {
+ /* ... */
+ key_list_store = g_object_get_data(G_OBJECT(key_management_w),AIRPCAP_ADVANCED_KEYLIST_KEY);
+ airpcap_fill_key_list(key_list_store);
+ gtk_widget_show (key_management_w);
+ }
+
+ gtk_widget_destroy(keys_check_w);
+}
+
+static void
+on_keys_check_ok_bt_clicked (GtkWidget *button _U_, gpointer user_data)
+{
+ GtkWidget *key_management_w;
+ GtkWidget *keys_check_w;
+
+ GtkWidget *merge_rb,
+ *keep_rb,
+ *import_rb,
+ *ignore_rb;
+
+ keys_check_w = GTK_WIDGET(user_data);
+
+ key_management_w = g_object_get_data(G_OBJECT(keys_check_w),AIRPCAP_CHECK_WINDOW_KEY);
+ merge_rb = g_object_get_data(G_OBJECT(keys_check_w),AIRPCAP_CHECK_WINDOW_RADIO_MERGE_KEY);
+ keep_rb = g_object_get_data(G_OBJECT(keys_check_w),AIRPCAP_CHECK_WINDOW_RADIO_KEEP_KEY);
+ import_rb = g_object_get_data(G_OBJECT(keys_check_w),AIRPCAP_CHECK_WINDOW_RADIO_IMPORT_KEY);
+ ignore_rb = g_object_get_data(G_OBJECT(keys_check_w),AIRPCAP_CHECK_WINDOW_RADIO_IGNORE_KEY);
+
+ /* Find out which radio button is selected and call the correct function */
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(merge_rb)))
+ on_merge_bt_clicked (merge_rb,keys_check_w);
+ else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(keep_rb)))
+ on_keep_bt_clicked (keep_rb,keys_check_w);
+ else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(import_rb)))
+ on_import_bt_clicked (import_rb,keys_check_w);
+ else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ignore_rb)))
+ on_ignore_bt_clicked (ignore_rb,keys_check_w);
+ else on_keys_check_cancel_bt_clicked(NULL,keys_check_w);
+}
+
+static void
+on_keys_check_w_destroy (GtkWidget *w _U_, gpointer user_data)
+{
+ gtk_widget_set_sensitive(top_level,TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(user_data),TRUE);
+}
+
+/*
+ * Dialog box that appears whenever keys are not consistent between Wireshark and AirPcap
+ */
+void
+airpcap_keys_check_w(GtkWidget *w, gpointer data _U_)
+{
+ GtkWidget *keys_check_w;
+ GtkWidget *main_v_box;
+ GtkWidget *warning_lb;
+ GtkWidget *radio_tb;
+ GtkWidget *keep_rb;
+ GSList *radio_bt_group = NULL;
+ GtkWidget *merge_rb;
+ GtkWidget *import_rb;
+ GtkWidget *ignore_rb;
+ GtkWidget *keep_lb;
+ GtkWidget *merge_lb;
+ GtkWidget *import_lb;
+ GtkWidget *ignore_lb;
+ GtkWidget *low_h_button_box;
+ GtkWidget *ok_bt;
+ GtkWidget *cancel_bt;
+
+ keys_check_w = window_new (GTK_WINDOW_TOPLEVEL, "Decryption Key Warning");
+ gtk_widget_set_name (keys_check_w, "keys_check_w");
+ gtk_window_set_resizable (GTK_WINDOW (keys_check_w), FALSE);
+
+ main_v_box = gtk_vbox_new (FALSE, 0);
+ gtk_widget_set_name (main_v_box, "main_v_box");
+ gtk_widget_show (main_v_box);
+ gtk_container_add (GTK_CONTAINER (keys_check_w), main_v_box);
+
+ warning_lb = gtk_label_new("<b>WARNING!</b> Decryption keys specified in Wireshark's preferences file differ from those specified for the AirPcap adapter(s). You can choose to:");
+ gtk_label_set_use_markup (GTK_LABEL (warning_lb), TRUE);
+ gtk_widget_set_name (warning_lb, "warning_lb");
+ gtk_widget_show (warning_lb);
+ gtk_box_pack_start (GTK_BOX (main_v_box), warning_lb, FALSE, FALSE, 0);
+ gtk_label_set_justify (GTK_LABEL (warning_lb), GTK_JUSTIFY_CENTER);
+ gtk_label_set_line_wrap (GTK_LABEL (warning_lb), TRUE);
+
+ radio_tb = gtk_table_new (4, 2, FALSE);
+ gtk_widget_set_name (radio_tb, "radio_tb");
+ gtk_widget_show (radio_tb);
+ gtk_box_pack_start (GTK_BOX (main_v_box), radio_tb, TRUE, FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (radio_tb), 5);
+ gtk_table_set_col_spacings (GTK_TABLE (radio_tb), 8);
+
+ keep_rb = gtk_radio_button_new_with_mnemonic (NULL, "Keep");
+ gtk_widget_set_name (keep_rb, "keep_rb");
+ gtk_widget_show (keep_rb);
+ gtk_table_attach (GTK_TABLE (radio_tb), keep_rb, 0, 1, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_radio_button_set_group (GTK_RADIO_BUTTON (keep_rb), radio_bt_group);
+ radio_bt_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (keep_rb));
+
+ merge_rb = gtk_radio_button_new_with_mnemonic (NULL, "Merge");
+ gtk_widget_set_name (merge_rb, "merge_rb");
+ gtk_widget_show (merge_rb);
+ gtk_table_attach (GTK_TABLE (radio_tb), merge_rb, 0, 1, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_radio_button_set_group (GTK_RADIO_BUTTON (merge_rb), radio_bt_group);
+ radio_bt_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (merge_rb));
+
+ import_rb = gtk_radio_button_new_with_mnemonic (NULL, "Import");
+ gtk_widget_set_name (import_rb, "import_rb");
+ gtk_widget_show (import_rb);
+ gtk_table_attach (GTK_TABLE (radio_tb), import_rb, 0, 1, 2, 3,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_radio_button_set_group (GTK_RADIO_BUTTON (import_rb), radio_bt_group);
+ radio_bt_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (import_rb));
+
+ ignore_rb = gtk_radio_button_new_with_mnemonic (NULL, "Ignore");
+ gtk_widget_set_name (ignore_rb, "ignore_rb");
+ gtk_widget_show (ignore_rb);
+ gtk_table_attach (GTK_TABLE (radio_tb), ignore_rb, 0, 1, 3, 4,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_radio_button_set_group (GTK_RADIO_BUTTON (ignore_rb), radio_bt_group);
+ radio_bt_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (ignore_rb));
+
+ keep_lb =
+ gtk_label_new
+ ("Use Wireshark keys, thus overwriting AirPcap adapter(s) ones.");
+ gtk_widget_set_name (keep_lb, "keep_lb");
+ gtk_widget_show (keep_lb);
+ gtk_table_attach (GTK_TABLE (radio_tb), keep_lb, 1, 2, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (keep_lb), 0, 0.5);
+
+ merge_lb = gtk_label_new ("Merge Wireshark and AirPcap adapter(s) keys.");
+ gtk_widget_set_name (merge_lb, "merge_lb");
+ gtk_widget_show (merge_lb);
+ gtk_table_attach (GTK_TABLE (radio_tb), merge_lb, 1, 2, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (merge_lb), 0, 0.5);
+
+ import_lb =
+ gtk_label_new
+ ("Use AirPcap adapter(s) keys, thus overwriting Wireshark ones.");
+ gtk_widget_set_name (import_lb, "import_lb");
+ gtk_widget_show (import_lb);
+ gtk_table_attach (GTK_TABLE (radio_tb), import_lb, 1, 2, 2, 3,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (import_lb), 0, 0.5);
+
+ ignore_lb =
+ gtk_label_new
+ ("Keep using different set of keys. Remember that in this case, this dialog box will appear whenever you will attempt to modify/add/remove decryption keys.");
+ gtk_widget_set_name (ignore_lb, "ignore_lb");
+ gtk_widget_show (ignore_lb);
+ gtk_table_attach (GTK_TABLE (radio_tb), ignore_lb, 1, 2, 3, 4,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_label_set_line_wrap (GTK_LABEL (ignore_lb), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (ignore_lb), 0, 0.5);
+
+ low_h_button_box = gtk_hbutton_box_new ();
+ gtk_widget_set_name (low_h_button_box, "low_h_button_box");
+ gtk_widget_show (low_h_button_box);
+ gtk_box_pack_start (GTK_BOX (main_v_box), low_h_button_box, FALSE, FALSE,
+ 0);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (low_h_button_box),
+ GTK_BUTTONBOX_SPREAD);
+
+ ok_bt = gtk_button_new_with_mnemonic ("OK");
+ gtk_widget_set_name (ok_bt, "ok_bt");
+ gtk_widget_show (ok_bt);
+ gtk_container_add (GTK_CONTAINER (low_h_button_box), ok_bt);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default (ok_bt, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS (ok_bt, GTK_CAN_DEFAULT);
+#endif
+
+ cancel_bt = gtk_button_new_with_mnemonic ("Cancel");
+ gtk_widget_set_name (cancel_bt, "cancel_bt");
+ gtk_widget_show (cancel_bt);
+ gtk_container_add (GTK_CONTAINER (low_h_button_box), cancel_bt);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default (cancel_bt, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS (cancel_bt, GTK_CAN_DEFAULT);
+#endif
+
+ /* Store pointers to all widgets, for use by lookup_widget(). */
+ g_signal_connect (ok_bt, "clicked", G_CALLBACK(on_keys_check_ok_bt_clicked), keys_check_w);
+ g_signal_connect (cancel_bt, "clicked", G_CALLBACK(on_keys_check_cancel_bt_clicked), keys_check_w);
+ g_signal_connect (keys_check_w, "destroy", G_CALLBACK(on_keys_check_w_destroy), keys_check_w);
+
+ /* Store pointers to all widgets, for use by lookup_widget(). */
+ g_object_set_data(G_OBJECT(keys_check_w),AIRPCAP_CHECK_WINDOW_KEY,w);
+ g_object_set_data(G_OBJECT(keys_check_w),AIRPCAP_CHECK_WINDOW_RADIO_MERGE_KEY,merge_rb);
+ g_object_set_data(G_OBJECT(keys_check_w),AIRPCAP_CHECK_WINDOW_RADIO_KEEP_KEY,keep_rb);
+ g_object_set_data(G_OBJECT(keys_check_w),AIRPCAP_CHECK_WINDOW_RADIO_IMPORT_KEY,import_rb);
+ g_object_set_data(G_OBJECT(keys_check_w),AIRPCAP_CHECK_WINDOW_RADIO_IGNORE_KEY,ignore_rb);
+ g_object_set_data(G_OBJECT(keys_check_w),AIRPCAP_CHECK_WINDOW_RADIO_GROUP_KEY,radio_bt_group);
+
+ gtk_widget_set_sensitive(top_level,FALSE);
+ gtk_widget_show(keys_check_w);
+}
+
+
+#endif /* HAVE_AIRPCAP */
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/gtk/airpcap_dlg.h b/ui/gtk/airpcap_dlg.h
new file mode 100644
index 0000000000..4ca9fb7d24
--- /dev/null
+++ b/ui/gtk/airpcap_dlg.h
@@ -0,0 +1,65 @@
+/* airpcap_dlg.h
+ * Declarations of routines for the "Airpcap" dialog
+ *
+ * $Id$
+ *
+ * Giorgio Tino <giorgio.tino@cacetech.com>
+ * Copyright (c) CACE Technologies, LLC 2006
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __AIRPCAP_DLG_H__
+#define __AIRPCAP_DLG_H__
+
+#define AIRPCAP_ADVANCED_FROM_TOOLBAR 0
+#define AIRPCAP_ADVANCED_FROM_OPTIONS 1
+
+/*
+ * Creates the list of available decryption modes, depending on the adapters found
+ */
+void update_decryption_mode_list(GtkWidget *w);
+
+/*
+ * Selects the current decryption mode in the given combo box
+ */
+void update_decryption_mode(GtkWidget *w);
+
+/*
+ * Turns the decryption on or off
+ */
+void on_decryption_mode_cb_changed(GtkWidget *w, gpointer data _U_);
+
+/** Create a "Airpcap" dialog box caused by a button click.
+ *
+ * @param widget parent widget
+ * @param construct_args_ptr parameters to construct the dialog (construct_args_t)
+ */
+void display_airpcap_advanced_cb(GtkWidget *widget, gpointer construct_args_ptr);
+
+/* Called to create the key management window */
+void display_airpcap_key_management_cb(GtkWidget *w, gpointer data);
+
+/**/
+/*
+ * Dialog box that appears whenever keys are not consistent between wieshark and airpcap
+ */
+void airpcap_keys_check_w(GtkWidget *w, gpointer data);
+
+#endif
diff --git a/ui/gtk/airpcap_gui_utils.c b/ui/gtk/airpcap_gui_utils.c
new file mode 100644
index 0000000000..9e2902fb8d
--- /dev/null
+++ b/ui/gtk/airpcap_gui_utils.c
@@ -0,0 +1,1090 @@
+/* airpcap_gui_utils.c
+ *
+ * $Id$
+ *
+ * Giorgio Tino <giorgio.tino@cacetech.com>
+ * Copyright (c) CACE Technologies, LLC 2006
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2000 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_AIRPCAP
+
+#include <gtk/gtk.h>
+#include <glib.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <epan/filesystem.h>
+#include <epan/strutil.h>
+#include <epan/frequency-utils.h>
+#include <epan/crypt/airpdcap_ws.h>
+
+#include "../simple_dialog.h"
+
+#include "ui/gtk/main.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dfilter_expr_dlg.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/old-gtk-compat.h"
+
+#include <airpcap.h>
+#include "airpcap_loader.h"
+#include "airpcap_gui_utils.h"
+
+
+/* Controls the releay of settings back to the adapter. */
+gboolean change_airpcap_settings = FALSE;
+
+/*
+ * Set up the airpcap toolbar for the new capture interface
+ */
+void
+airpcap_set_toolbar_start_capture(airpcap_if_info_t* if_info)
+{
+ GtkWidget *airpcap_toolbar_label;
+ GtkWidget *toolbar_channel_cb;
+ GtkWidget *airpcap_toolbar_channel_lb;
+ GtkWidget *airpcap_toolbar_channel_offset;
+ GtkWidget *airpcap_toolbar_channel_offset_lb;
+ GtkWidget *airpcap_toolbar_button;
+ GtkWidget *airpcap_toolbar_fcs;
+ GtkWidget *airpcap_toolbar_fcs_lb;
+ GtkWidget *airpcap_toolbar_decryption;
+ GtkWidget *airpcap_toolbar_decryption_lb;
+ GtkWidget *airpcap_toolbar_keys_button;
+
+ gchar *if_label_text;
+
+ airpcap_toolbar_label = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_INTERFACE_KEY);
+ toolbar_channel_cb = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_CHANNEL_KEY);
+ airpcap_toolbar_channel_lb = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_CHANNEL_LABEL_KEY);
+ airpcap_toolbar_channel_offset = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_CHANNEL_OFFSET_KEY);
+ airpcap_toolbar_channel_offset_lb = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_CHANNEL_OFFSET_LABEL_KEY);
+ airpcap_toolbar_fcs = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_FCS_FILTER_KEY);
+ airpcap_toolbar_fcs_lb = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_FCS_FILTER_LABEL_KEY);
+ airpcap_toolbar_button = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_ADVANCED_KEY);
+ airpcap_toolbar_decryption = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_DECRYPTION_KEY);
+ airpcap_toolbar_decryption_lb = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_DECRYPTION_LABEL_KEY);
+ airpcap_toolbar_keys_button = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_KEY_MANAGEMENT_KEY);
+
+ /* The current interface is an airpcap interface */
+ if(if_info != NULL)
+ {
+ gtk_widget_set_sensitive(airpcap_tb,TRUE);
+ gtk_widget_set_sensitive(airpcap_toolbar_label,TRUE);
+ gtk_widget_set_sensitive(toolbar_channel_cb,TRUE);
+ gtk_widget_set_sensitive(airpcap_toolbar_channel_lb,TRUE);
+ gtk_widget_set_sensitive(airpcap_toolbar_channel_offset,TRUE);
+ gtk_widget_set_sensitive(airpcap_toolbar_channel_offset_lb,TRUE);
+ gtk_widget_set_sensitive(airpcap_toolbar_fcs,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_fcs_lb,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_button,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_button,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_decryption,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_decryption_lb,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_keys_button,FALSE);
+
+ /*decryption check box*/
+ g_signal_handlers_block_by_func (airpcap_toolbar_decryption,airpcap_toolbar_encryption_cb, airpcap_tb);
+ if(if_info->DecryptionOn == AIRPCAP_DECRYPTION_ON)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(airpcap_toolbar_decryption),TRUE);
+ else
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(airpcap_toolbar_decryption),FALSE);
+ g_signal_handlers_unblock_by_func (airpcap_toolbar_decryption,airpcap_toolbar_encryption_cb, airpcap_tb);
+
+ if_label_text = g_strdup_printf("Current Wireless Interface: #%s", airpcap_get_if_string_number(if_info));
+ gtk_label_set_text(GTK_LABEL(airpcap_toolbar_label),if_label_text);
+ g_free(if_label_text);
+
+ change_airpcap_settings = FALSE;
+ if (if_info->pSupportedChannels != NULL && if_info->numSupportedChannels > 0){
+ guint i = 0;
+
+ for (; i<if_info->numSupportedChannels; i++){
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(toolbar_channel_cb), ieee80211_mhz_to_str(if_info->pSupportedChannels[i].Frequency));
+ }
+ }
+
+ airpcap_update_channel_combo(GTK_WIDGET(toolbar_channel_cb),if_info);
+ airpcap_update_channel_offset_combo(if_info, if_info->channelInfo.Frequency, airpcap_toolbar_channel_offset, TRUE);
+ change_airpcap_settings = TRUE;
+ }
+ else /* Current interface is NOT an AirPcap one... */
+ {
+ gtk_widget_set_sensitive(airpcap_tb,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_label,FALSE);
+ gtk_widget_set_sensitive(toolbar_channel_cb,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_channel_lb,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_channel_offset,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_channel_offset_lb,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_fcs,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_fcs_lb,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_button,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_button,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_decryption,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_decryption_lb,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_keys_button,FALSE);
+ }
+}
+
+/*
+ * Set up the airpcap toolbar for the new capture interface
+ */
+void
+airpcap_set_toolbar_stop_capture(airpcap_if_info_t* if_info)
+{
+ GtkWidget *airpcap_toolbar_crc_filter_combo;
+ GtkWidget *airpcap_toolbar_label;
+ GtkWidget *toolbar_channel_cb;
+ GtkWidget *airpcap_toolbar_channel_lb;
+ GtkWidget *airpcap_toolbar_channel_offset;
+ GtkWidget *airpcap_toolbar_channel_offset_lb;
+ GtkWidget *airpcap_toolbar_button;
+ GtkWidget *airpcap_toolbar_fcs;
+ GtkWidget *airpcap_toolbar_fcs_lb;
+ GtkWidget *airpcap_toolbar_decryption;
+ GtkWidget *airpcap_toolbar_decryption_lb;
+ GtkWidget *airpcap_toolbar_keys_button;
+
+ gchar *if_label_text;
+
+ airpcap_toolbar_crc_filter_combo = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_FCS_FILTER_KEY);
+ airpcap_toolbar_label = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_INTERFACE_KEY);
+ toolbar_channel_cb = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_CHANNEL_KEY);
+ airpcap_toolbar_channel_lb = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_CHANNEL_LABEL_KEY);
+ airpcap_toolbar_channel_offset = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_CHANNEL_OFFSET_KEY);
+ airpcap_toolbar_channel_offset_lb = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_CHANNEL_OFFSET_LABEL_KEY);
+ airpcap_toolbar_fcs = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_FCS_FILTER_KEY);
+ airpcap_toolbar_fcs_lb = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_FCS_FILTER_LABEL_KEY);
+ airpcap_toolbar_button = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_ADVANCED_KEY);
+ airpcap_toolbar_decryption = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_DECRYPTION_KEY);
+ airpcap_toolbar_decryption_lb = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_DECRYPTION_LABEL_KEY);
+ airpcap_toolbar_keys_button = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_KEY_MANAGEMENT_KEY);
+
+ /* The current interface is an airpcap interface */
+ if(if_info != NULL)
+ {
+ gtk_widget_set_sensitive(airpcap_tb,TRUE);
+ gtk_widget_set_sensitive(airpcap_toolbar_label,TRUE);
+ gtk_widget_set_sensitive(toolbar_channel_cb,TRUE);
+ gtk_widget_set_sensitive(airpcap_toolbar_channel_lb,TRUE);
+ gtk_widget_set_sensitive(airpcap_toolbar_channel_offset,TRUE);
+ gtk_widget_set_sensitive(airpcap_toolbar_channel_offset_lb,TRUE);
+ gtk_widget_set_sensitive(airpcap_toolbar_fcs,TRUE);
+ gtk_widget_set_sensitive(airpcap_toolbar_fcs_lb,TRUE);
+ gtk_widget_set_sensitive(airpcap_toolbar_button,TRUE);
+ gtk_widget_set_sensitive(airpcap_toolbar_crc_filter_combo,TRUE);
+ gtk_widget_set_sensitive(airpcap_toolbar_decryption,TRUE);
+ gtk_widget_set_sensitive(airpcap_toolbar_decryption_lb,TRUE);
+ gtk_widget_set_sensitive(airpcap_toolbar_keys_button,TRUE);
+ airpcap_validation_type_combo_set_by_type(airpcap_toolbar_crc_filter_combo, if_info->CrcValidationOn);
+
+ /*decription check box*/
+ g_signal_handlers_block_by_func (airpcap_toolbar_decryption,airpcap_toolbar_encryption_cb, airpcap_tb);
+ if(if_info->DecryptionOn == AIRPCAP_DECRYPTION_ON)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(airpcap_toolbar_decryption),TRUE);
+ else
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(airpcap_toolbar_decryption),FALSE);
+ g_signal_handlers_unblock_by_func (airpcap_toolbar_decryption,airpcap_toolbar_encryption_cb, airpcap_tb);
+
+ if_label_text = g_strdup_printf("Current Wireless Interface: #%s", airpcap_get_if_string_number(if_info));
+ gtk_label_set_text(GTK_LABEL(airpcap_toolbar_label),if_label_text);
+ g_free(if_label_text);
+
+ change_airpcap_settings = FALSE;
+ if (if_info->pSupportedChannels != NULL && if_info->numSupportedChannels > 0){
+ guint i = 0;
+
+ for (; i<if_info->numSupportedChannels; i++){
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(toolbar_channel_cb), ieee80211_mhz_to_str(if_info->pSupportedChannels[i].Frequency));
+ }
+ }
+
+ airpcap_update_channel_combo(GTK_WIDGET(toolbar_channel_cb),if_info);
+ airpcap_update_channel_offset_combo(if_info, if_info->channelInfo.Frequency, airpcap_toolbar_channel_offset, TRUE);
+ change_airpcap_settings = TRUE;
+ }
+ else
+ {
+ gtk_widget_set_sensitive(airpcap_tb,TRUE);
+ gtk_widget_set_sensitive(airpcap_toolbar_label,FALSE);
+ gtk_widget_set_sensitive(toolbar_channel_cb,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_channel_lb,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_channel_offset,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_channel_offset_lb,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_fcs,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_fcs_lb,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_button,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_crc_filter_combo,FALSE);
+ gtk_widget_set_sensitive(airpcap_toolbar_decryption,TRUE);
+ gtk_widget_set_sensitive(airpcap_toolbar_decryption_lb,TRUE);
+ gtk_widget_set_sensitive(airpcap_toolbar_keys_button,TRUE);
+ change_airpcap_settings = FALSE;
+ }
+}
+
+/*
+ * Add a key (string) to the given list
+ */
+void
+airpcap_add_key_to_list(GtkListStore *key_list_store, gchar* type, gchar* key, gchar* ssid)
+{
+ GtkTreeIter iter;
+
+ gtk_list_store_insert_with_values(key_list_store , &iter, G_MAXINT,
+ KL_COL_TYPE, type,
+ KL_COL_KEY, key,
+ KL_COL_SSID, ssid,
+ -1);
+}
+
+/*
+ * Fill the list with the keys. BEWARE! At this point, Wireshark and Drivers
+ * keys should be EQUALS! But is better to load keys from Wireshark, because
+ * the driver is not always present, and maybe that cannot support some keys
+ * (i.e. the WPA problem)
+ */
+void
+airpcap_fill_key_list(GtkListStore *key_list_store)
+{
+ gchar* s = NULL;
+ unsigned int i,n;
+ airpcap_if_info_t* fake_if_info;
+ GList* wireshark_key_list=NULL;
+ decryption_key_t* curr_key = NULL;
+ GtkTreeIter iter;
+
+ n = 0;
+
+ fake_if_info = airpcap_driver_fake_if_info_new();
+
+ /* We can retrieve the driver's key list (i.e. we have the right .dll)*/
+ wireshark_key_list = get_wireshark_keys();
+ n = g_list_length(wireshark_key_list);
+
+ for(i = 0; i < n; i++)
+ {
+ curr_key = (decryption_key_t*)g_list_nth_data(wireshark_key_list,i);
+
+ if(curr_key->type == AIRPDCAP_KEY_TYPE_WEP)
+ {
+ gtk_list_store_insert_with_values(key_list_store , &iter, G_MAXINT,
+ KL_COL_TYPE, AIRPCAP_WEP_KEY_STRING,
+ KL_COL_KEY, curr_key->key->str,
+ KL_COL_SSID, "",
+ -1);
+ }
+ else if(curr_key->type == AIRPDCAP_KEY_TYPE_WPA_PWD)
+ {
+ if(curr_key->ssid != NULL)
+ s = format_uri(curr_key->ssid, ":");
+ else
+ s = "";
+
+ gtk_list_store_insert_with_values(key_list_store , &iter, G_MAXINT,
+ KL_COL_TYPE, AIRPCAP_WPA_PWD_KEY_STRING,
+ KL_COL_KEY, curr_key->key->str,
+ KL_COL_SSID, s,
+ -1);
+
+ }
+ else if(curr_key->type == AIRPDCAP_KEY_TYPE_WPA_PMK)
+ {
+ gtk_list_store_insert_with_values(key_list_store , &iter, G_MAXINT,
+ KL_COL_TYPE, AIRPCAP_WPA_BIN_KEY_STRING,
+ KL_COL_KEY, curr_key->key->str,
+ KL_COL_SSID, "",
+ -1);
+
+ }
+ }
+
+ airpcap_if_info_free(fake_if_info);
+ return;
+}
+
+/*
+ * Function used to retrieve the AirpcapValidationType given the string name.
+ */
+AirpcapValidationType
+airpcap_get_validation_type(const gchar* name)
+{
+ if(!(g_ascii_strcasecmp(AIRPCAP_VALIDATION_TYPE_NAME_ALL,name)))
+ {
+ return AIRPCAP_VT_ACCEPT_EVERYTHING;
+ }
+ else if(!(g_ascii_strcasecmp(AIRPCAP_VALIDATION_TYPE_NAME_CORRECT,name)))
+ {
+ return AIRPCAP_VT_ACCEPT_CORRECT_FRAMES;
+ }
+ else if(!(g_ascii_strcasecmp(AIRPCAP_VALIDATION_TYPE_NAME_CORRUPT,name)))
+ {
+ return AIRPCAP_VT_ACCEPT_CORRUPT_FRAMES;
+ }
+ return AIRPCAP_VT_UNKNOWN;
+}
+
+/*
+ * Function used to retrieve the string name given an AirpcapValidationType,
+ * or NULL in case of error
+ */
+gchar*
+airpcap_get_validation_name(AirpcapValidationType vt)
+{
+ if(vt == AIRPCAP_VT_ACCEPT_EVERYTHING)
+ {
+ return AIRPCAP_VALIDATION_TYPE_NAME_ALL;
+ }
+ else if(vt == AIRPCAP_VT_ACCEPT_CORRECT_FRAMES)
+ {
+ return AIRPCAP_VALIDATION_TYPE_NAME_CORRECT;
+ }
+ else if(vt == AIRPCAP_VT_ACCEPT_CORRUPT_FRAMES)
+ {
+ return AIRPCAP_VALIDATION_TYPE_NAME_CORRUPT;
+ }
+ else if(vt == AIRPCAP_VT_UNKNOWN)
+ {
+ return AIRPCAP_VALIDATION_TYPE_NAME_UNKNOWN;
+ }
+ return NULL;
+}
+
+/*
+ * Return an appropriate combo box entry number for the given an AirpcapValidationType,
+ * defaulting to 0
+ */
+gint
+airpcap_get_validation_combo_entry(AirpcapValidationType vt)
+{
+ switch (vt) {
+ case AIRPCAP_VT_ACCEPT_CORRECT_FRAMES:
+ return 1;
+ break;
+ case AIRPCAP_VT_ACCEPT_CORRUPT_FRAMES:
+ return 2;
+ break;
+ default:
+ return 0;
+ break;
+ }
+}
+
+/*
+ * Returns the AirpcapLinkType corresponding to the given string name.
+ */
+AirpcapLinkType
+airpcap_get_link_type(const gchar* name)
+{
+ if(!(g_ascii_strcasecmp(AIRPCAP_LINK_TYPE_NAME_802_11_ONLY,name))){
+ return AIRPCAP_LT_802_11;
+ }else if(!(g_ascii_strcasecmp(AIRPCAP_LINK_TYPE_NAME_802_11_PLUS_RADIO,name))){
+ return AIRPCAP_LT_802_11_PLUS_RADIO;
+ }else if(!(g_ascii_strcasecmp(AIRPCAP_LINK_TYPE_NAME_802_11_PLUS_PPI,name))){
+ return AIRPCAP_LT_802_11_PLUS_PPI;
+ }else{
+ return AIRPCAP_LT_UNKNOWN;
+ }
+}
+
+/*
+ * Returns the string name corresponding to the given AirpcapLinkType, or
+ * NULL in case of error.
+ */
+gchar*
+airpcap_get_link_name(AirpcapLinkType lt)
+{
+ if(lt == AIRPCAP_LT_802_11){
+ return AIRPCAP_LINK_TYPE_NAME_802_11_ONLY;
+ }else if(lt == AIRPCAP_LT_802_11_PLUS_RADIO){
+ return AIRPCAP_LINK_TYPE_NAME_802_11_PLUS_RADIO;
+ }else if(lt == AIRPCAP_LT_802_11_PLUS_PPI){
+ return AIRPCAP_LINK_TYPE_NAME_802_11_PLUS_PPI;
+ }else if(lt == AIRPCAP_LT_UNKNOWN){
+ return AIRPCAP_LINK_TYPE_NAME_UNKNOWN;
+ }
+ return NULL;
+}
+
+/*
+ * Sets the entry of the validation combo using the AirpcapValidationType.
+ */
+void
+airpcap_validation_type_combo_set_by_type(GtkWidget* c, AirpcapValidationType type)
+{
+ gtk_combo_box_set_active(GTK_COMBO_BOX(c), airpcap_get_validation_combo_entry(type));
+}
+
+/*
+ * Returns the string corresponding to the given guint (1-14, for channel only)
+ */
+gchar*
+airpcap_get_channel_name(guint n)
+{
+ return g_strdup_printf("%d",n);
+}
+
+
+/*
+ * Set the combo box entry string given a channel frequency
+ */
+void
+airpcap_channel_combo_set_by_frequency(GtkWidget* cb, guint chan_freq)
+{
+ guint i;
+
+ for (i = 0; i < airpcap_if_selected->numSupportedChannels; i++) {
+ if (airpcap_if_selected->pSupportedChannels[i].Frequency == chan_freq) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(cb), i);
+ break;
+ }
+ }
+}
+
+/*
+ * Change channel of Airpcap Adapter
+ */
+static gboolean
+airpcap_update_frequency_and_offset(airpcap_if_info_t* if_info)
+{
+ gchar ebuf[AIRPCAP_ERRBUF_SIZE];
+ PAirpcapHandle ad;
+ gboolean return_value = FALSE;
+
+ if (if_info != NULL){
+ ad = airpcap_if_open(if_info->name, ebuf);
+
+ if(ad != NULL) {
+ return_value = airpcap_if_set_device_channel_ex(ad,if_info->channelInfo);
+ airpcap_if_close(ad);
+ }
+ }
+
+ return return_value;
+}
+
+/*
+ * Changed callback for the channel combobox - common routine
+ */
+static void
+airpcap_channel_changed_common(GtkWidget *channel_cb, gpointer channel_offset_cb, gboolean set)
+{
+ gint cur_chan_idx;
+
+ if (channel_cb && channel_offset_cb && change_airpcap_settings && airpcap_if_active) {
+ cur_chan_idx = gtk_combo_box_get_active(GTK_COMBO_BOX(channel_cb));
+ if (cur_chan_idx >= 0 && cur_chan_idx < (gint) airpcap_if_active->numSupportedChannels) {
+ if (set) {
+ airpcap_if_active->channelInfo.Frequency = airpcap_if_active->pSupportedChannels[cur_chan_idx].Frequency;
+ }
+ airpcap_update_channel_offset_combo(airpcap_if_active,
+ airpcap_if_active->channelInfo.Frequency,
+ GTK_WIDGET(channel_offset_cb), set);
+ }
+ }
+}
+
+/*
+ * Changed callback for the channel combobox - set channel and offset
+ */
+void
+airpcap_channel_changed_set_cb(GtkWidget *channel_cb, gpointer channel_offset_cb)
+{
+ airpcap_channel_changed_common(channel_cb, channel_offset_cb, TRUE);
+}
+
+/*
+ * Changed callback for the channel combobox - don't set channel and offset
+ */
+void
+airpcap_channel_changed_noset_cb(GtkWidget *channel_cb, gpointer channel_offset_cb)
+{
+ airpcap_channel_changed_common(channel_cb, channel_offset_cb, FALSE);
+}
+
+static int
+airpcap_get_selected_channel_offset(GtkWidget *channel_offset_cb) {
+ int offset;
+ gchar *off_str;
+ int retval = 0;
+
+
+ if (channel_offset_cb == NULL || !gtk_widget_get_sensitive(channel_offset_cb)) {
+ return 0;
+ }
+
+ off_str = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(channel_offset_cb));
+ if (off_str && (g_ascii_strcasecmp("", off_str)))
+ {
+ if (airpcap_if_selected != NULL)
+ {
+ sscanf(off_str, "%d", &offset);
+ if (offset >= -1 && offset <= 1) {
+ retval = offset;
+ }
+ }
+ }
+ g_free(off_str);
+ return retval;
+}
+
+/*
+ * Changed callback for the channel offset combobox
+ */
+void
+airpcap_channel_offset_changed_cb(GtkWidget *channel_offset_cb, gpointer data _U_)
+{
+ airpcap_if_selected->channelInfo.ExtChannel = airpcap_get_selected_channel_offset(channel_offset_cb);
+ airpcap_if_selected->saved = FALSE;
+ change_airpcap_settings = TRUE;
+ if (!airpcap_update_frequency_and_offset(airpcap_if_selected)){
+ simple_dialog(ESD_TYPE_ERROR,ESD_BTN_OK,
+ "Unable to set extension channel %d",
+ airpcap_if_selected->channelInfo.ExtChannel);
+ }
+}
+
+
+/*
+ * Update the channel offset of the given combobox according to the given frequency.
+ */
+void
+airpcap_update_channel_offset_combo(airpcap_if_info_t* if_info, guint chan_freq, GtkWidget *channel_offset_cb, gboolean set)
+{
+ gint current_offset;
+ gint new_offset;
+ guint i;
+ gint active_idx = 0;
+ gint idx_count = -1;
+
+ if (!if_info || airpcap_if_is_any(if_info) || if_info->pSupportedChannels == NULL || if_info->numSupportedChannels < 1) {
+ gtk_widget_set_sensitive(GTK_WIDGET(channel_offset_cb),FALSE);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(channel_offset_cb), -1);
+ return;
+ }
+
+ new_offset = current_offset = if_info->channelInfo.ExtChannel;
+
+ /* Clear out the list */
+ while (gtk_tree_model_iter_n_children(gtk_combo_box_get_model(GTK_COMBO_BOX(channel_offset_cb)), NULL) > 0) {
+ gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(channel_offset_cb), 0);
+ }
+
+ gtk_widget_set_sensitive(GTK_WIDGET(channel_offset_cb), TRUE);
+
+ for (i = 0; i < if_info->numSupportedChannels; i++) {
+ if (if_info->pSupportedChannels[i].Frequency == chan_freq) {
+
+ /* If we can't be low or high, nudge the offset to 0 */
+ if (current_offset == -1 && !(if_info->pSupportedChannels[i].Flags & FLAG_CAN_BE_LOW)) {
+ new_offset = 0;
+ } else if (current_offset == 1 && !(if_info->pSupportedChannels[i].Flags & FLAG_CAN_BE_HIGH)) {
+ new_offset = 0;
+ }
+
+ if ((if_info->pSupportedChannels[i].Flags & FLAG_CAN_BE_LOW)) {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(channel_offset_cb), "-1");
+ idx_count++;
+ if (new_offset == -1) {
+ active_idx = idx_count;
+ }
+ }
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(channel_offset_cb), "0");
+ idx_count++;
+ if (new_offset == 0) {
+ active_idx = idx_count;
+ }
+ if ((if_info->pSupportedChannels[i].Flags & FLAG_CAN_BE_HIGH)){
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(channel_offset_cb), "+1");
+ idx_count++;
+ if (new_offset == 1) {
+ active_idx = idx_count;
+ }
+ }
+ break;
+ }
+ }
+
+ gtk_combo_box_set_active(GTK_COMBO_BOX(channel_offset_cb), active_idx);
+
+
+ if (set) {
+ change_airpcap_settings = TRUE;
+
+ if_info->channelInfo.ExtChannel = new_offset;
+ if (!airpcap_update_frequency_and_offset(if_info)){
+ simple_dialog(ESD_TYPE_ERROR,ESD_BTN_OK,"Adapter failed to be set with the following settings: Frequency - %d Extension Channel - %d", if_info->channelInfo.Frequency, if_info->channelInfo.ExtChannel);
+ }
+ }
+
+ if (idx_count < 1) {
+ gtk_widget_set_sensitive(channel_offset_cb, FALSE);
+ }
+}
+
+/*
+ * Returns '1' if this is the "Any" adapter, '0' otherwise
+ */
+int
+airpcap_if_is_any(airpcap_if_info_t* if_info)
+{
+ if(g_ascii_strcasecmp(if_info->name,AIRPCAP_DEVICE_ANY_EXTRACT_STRING)==0)
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * Update channel combo box. If the airpcap interface is "Any", the combo box will be disabled.
+ */
+void
+airpcap_update_channel_combo(GtkWidget* channel_cb, airpcap_if_info_t* if_info)
+{
+ if(!if_info || airpcap_if_is_any(if_info) || !airpcap_if_selected)
+ {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(channel_cb), -1);
+ change_airpcap_settings = FALSE;
+ gtk_widget_set_sensitive(GTK_WIDGET(channel_cb),FALSE);
+ }
+ else
+ {
+ while (gtk_tree_model_iter_n_children(gtk_combo_box_get_model(GTK_COMBO_BOX(channel_cb)), NULL) > 0) {
+ gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(channel_cb), 0);
+ }
+
+ if (if_info != NULL && if_info->pSupportedChannels != NULL && if_info->numSupportedChannels > 0){
+ guint i;
+ for (i = 0; i<(if_info->numSupportedChannels); i++){
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(channel_cb), ieee80211_mhz_to_str(airpcap_if_selected->pSupportedChannels[i].Frequency));
+ }
+ }
+
+ airpcap_channel_combo_set_by_frequency(channel_cb, if_info->channelInfo.Frequency);
+ change_airpcap_settings = TRUE;
+ gtk_widget_set_sensitive(GTK_WIDGET(channel_cb), TRUE);
+ }
+}
+
+/*
+ * Takes the keys from the GtkList widget, and add them to the interface list
+ */
+static void
+airpcap_add_keys_to_driver_from_list(GtkListStore *key_list_store, airpcap_if_info_t *fake_if_info)
+{
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ GtkTreeModel *model = GTK_TREE_MODEL(key_list_store);
+
+ /* airpcap stuff */
+ guint i, j;
+ gchar s[3];
+ PAirpcapKeysCollection KeysCollection;
+ guint KeysCollectionSize;
+ guint8 KeyByte;
+
+ guint keys_in_list = 0;
+
+ gchar *row_type, *row_key; /* SSID not needed for AirPcap */
+ size_t key_len;
+
+ if(fake_if_info == NULL)
+ return;
+
+ keys_in_list = gtk_tree_model_iter_n_children(model, NULL);
+
+ /*
+ * Save the encryption keys, if we have any of them
+ */
+ KeysCollectionSize = 0;
+
+ /*
+ * Calculate the size of the keys collection
+ */
+ KeysCollectionSize = sizeof(AirpcapKeysCollection) + keys_in_list * sizeof(AirpcapKey);
+
+ /*
+ * Allocate the collection
+ */
+ KeysCollection = (PAirpcapKeysCollection)g_malloc(KeysCollectionSize);
+
+ /*
+ * Populate the key collection
+ */
+ KeysCollection->nKeys = keys_in_list;
+
+ for(i = 0; i < keys_in_list; i++)
+ {
+ path = gtk_tree_path_new_from_indices(i, -1);
+ gtk_tree_model_get_iter(model, &iter, path);
+ gtk_tree_path_free(path);
+ gtk_tree_model_get(model, &iter,
+ KL_COL_TYPE, &row_type,
+ KL_COL_KEY, &row_key,
+ -1);
+
+ if(g_ascii_strcasecmp(row_type,AIRPCAP_WEP_KEY_STRING) == 0)
+ KeysCollection->Keys[i].KeyType = AIRPDCAP_KEY_TYPE_WEP;
+ else if(g_ascii_strcasecmp(row_type,AIRPCAP_WPA_PWD_KEY_STRING) == 0)
+ KeysCollection->Keys[i].KeyType = AIRPDCAP_KEY_TYPE_WPA_PWD;
+ else if(g_ascii_strcasecmp(row_type,AIRPCAP_WPA_BIN_KEY_STRING) == 0)
+ KeysCollection->Keys[i].KeyType = AIRPDCAP_KEY_TYPE_WPA_PMK;
+
+ /* Retrieve the Item corresponding to the i-th key */
+ key_len = strlen(row_key);
+
+ KeysCollection->Keys[i].KeyLen = (guint) key_len / 2;
+ memset(&KeysCollection->Keys[i].KeyData, 0, sizeof(KeysCollection->Keys[i].KeyData));
+
+ /* Key must be saved in adifferent way, depending on its type... */
+ if(KeysCollection->Keys[i].KeyType == AIRPDCAP_KEY_TYPE_WEP)
+ {
+ for(j = 0 ; j < key_len; j += 2)
+ {
+ s[0] = row_key[j];
+ s[1] = row_key[j+1];
+ s[2] = '\0';
+ KeyByte = (guint8)strtol(s, NULL, 16);
+ KeysCollection->Keys[i].KeyData[j / 2] = KeyByte;
+ }
+ }
+ g_free(row_type);
+ g_free(row_key);
+ }
+
+ /*
+ * Free the old adapter key collection!
+ */
+ if(fake_if_info->keysCollection != NULL)
+ g_free(fake_if_info->keysCollection);
+
+ /*
+ * Set this collection ad the new one
+ */
+ fake_if_info->keysCollection = KeysCollection;
+ fake_if_info->keysCollectionSize = KeysCollectionSize;
+ return;
+}
+
+/*
+ * This function will take the current keys (widget list), specified for the
+ * current adapter, and save them as default for ALL the others.
+ */
+void
+airpcap_read_and_save_decryption_keys_from_list_store(GtkListStore* key_list_store, airpcap_if_info_t* info_if, GList* if_list)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model = GTK_TREE_MODEL(key_list_store);
+ gboolean items_left;
+ gint if_n = 0;
+ gint i = 0;
+ gint r = 0;
+ airpcap_if_info_t* curr_if = NULL;
+ airpcap_if_info_t* fake_info_if = NULL;
+ GList* key_list=NULL;
+
+ char* tmp_type = NULL;
+ char* tmp_key = NULL;
+ char* tmp_ssid = "";
+
+ decryption_key_t* tmp_dk=NULL;
+
+ /*
+ * Save the keys for Wireshark...
+ */
+
+ /* Create a list of keys from the list store */
+ for (items_left = gtk_tree_model_get_iter_first (model, &iter);
+ items_left;
+ items_left = gtk_tree_model_iter_next (model, &iter)) {
+
+ gtk_tree_model_get(model, &iter,
+ KL_COL_TYPE, &tmp_type,
+ KL_COL_KEY, &tmp_key,
+ KL_COL_SSID, &tmp_ssid,
+ -1);
+
+ if(g_ascii_strcasecmp(tmp_type, AIRPCAP_WEP_KEY_STRING) == 0)
+ {
+ tmp_dk = (decryption_key_t*)g_malloc(sizeof(decryption_key_t));
+ tmp_dk->key = g_string_new(tmp_key);
+ tmp_dk->ssid = NULL;
+ tmp_dk->type = AIRPDCAP_KEY_TYPE_WEP;
+ tmp_dk->bits = (guint) tmp_dk->key->len * 4;
+ key_list = g_list_append(key_list,tmp_dk);
+ }
+ else if(g_ascii_strcasecmp(tmp_type, AIRPCAP_WPA_PWD_KEY_STRING) == 0)
+ {
+ tmp_dk = (decryption_key_t*)g_malloc(sizeof(decryption_key_t));
+ tmp_dk->key = g_string_new(tmp_key);
+ tmp_dk->ssid = g_byte_array_new();
+ uri_str_to_bytes(tmp_ssid, tmp_dk->ssid);
+ tmp_dk->type = AIRPDCAP_KEY_TYPE_WPA_PWD;
+ tmp_dk->bits = 256;
+ key_list = g_list_append(key_list,tmp_dk);
+ }
+ else if(g_ascii_strcasecmp(tmp_type, AIRPCAP_WPA_BIN_KEY_STRING) == 0)
+ {
+ tmp_dk = (decryption_key_t*)g_malloc(sizeof(decryption_key_t));
+ tmp_dk->key = g_string_new(tmp_key);
+ tmp_dk->ssid = NULL; /* No SSID in this case */
+ tmp_dk->type = AIRPDCAP_KEY_TYPE_WPA_PMK;
+ tmp_dk->bits = 256;
+ key_list = g_list_append(key_list,tmp_dk);
+ }
+ g_free(tmp_type);
+ g_free(tmp_ssid);
+ }
+
+ r = save_wlan_wireshark_wep_keys(key_list);
+ /* The key_list has been freed!!! */
+
+ /*
+ * Save the key list for driver.
+ */
+ if( (if_list == NULL) || (info_if == NULL) ) return;
+
+ fake_info_if = airpcap_driver_fake_if_info_new();
+
+ airpcap_add_keys_to_driver_from_list(key_list_store,fake_info_if);
+ airpcap_save_driver_if_configuration(fake_info_if);
+ airpcap_if_info_free(fake_info_if);
+
+ if_n = g_list_length(if_list);
+
+ /* For all the adapters in the list, empty the key list */
+ for(i = 0; i < if_n; i++)
+ {
+ curr_if = (airpcap_if_info_t*)g_list_nth_data(if_list,i);
+
+ if(curr_if != NULL)
+ {
+ /* XXX - Set an empty collection */
+ airpcap_if_clear_decryption_settings(curr_if);
+
+ /* Save to registry */
+ airpcap_save_selected_if_configuration(curr_if);
+ }
+ }
+}
+
+/*
+ * This function will load from the preferences file ALL the
+ * keys (WEP, WPA and WPA_BIN) and will set them as default for
+ * each adapter. To do this, it will save the keys in the registry...
+ * A check will be performed, to make sure that keys found in
+ * registry and keys found in Wireshark preferences are the same. If not,
+ * the user will be asked to choose if use all keys (merge them),
+ * or use Wireshark preferences ones. In the last case, registry keys will
+ * be overwritten for all the connected AirPcap adapters.
+ * In the first case, adapters will use their own keys, but those
+ * keys will not be accessible via Wireshark...
+ */
+gboolean
+airpcap_check_decryption_keys(GList* if_list)
+{
+ gint if_n = 0;
+ gint i = 0;
+ gint n_adapters_keys = 0;
+ gint n_driver_keys = 0;
+ gint n_wireshark_keys = 0;
+ airpcap_if_info_t* curr_if = NULL;
+
+ GList* wireshark_key_list;
+ GList* driver_key_list;
+ GList* curr_adapter_key_list;
+
+ gboolean equals = TRUE;
+ gboolean adapters_keys_equals=TRUE;
+
+ /*
+ * If no AirPcap interface is found, return TRUE, so Wireshark
+ * will use HIS OWN keys.
+ */
+ if(if_list == NULL)
+ return TRUE;
+
+ if_n = g_list_length(if_list);
+
+ /* Get Wireshark preferences keys */
+ wireshark_key_list = get_wireshark_keys();
+ n_wireshark_keys = g_list_length(wireshark_key_list);
+
+ /* Retrieve AirPcap driver's keys */
+ driver_key_list = get_airpcap_driver_keys();
+ n_driver_keys = g_list_length(driver_key_list);
+
+ equals &= key_lists_are_equal(wireshark_key_list,driver_key_list);
+
+ for(i = 0; i < if_n; i++)
+ {
+ curr_if = (airpcap_if_info_t*)g_list_nth_data(if_list,i);
+ curr_adapter_key_list = get_airpcap_device_keys(curr_if);
+ n_adapters_keys += g_list_length(curr_adapter_key_list);
+ adapters_keys_equals &= key_lists_are_equal(wireshark_key_list,curr_adapter_key_list);
+ }
+
+ if(n_adapters_keys != 0) /* If for some reason at least one specific key has been found */
+ equals &= adapters_keys_equals; /* */
+
+ if(n_driver_keys == 0) /* No keys set in any of the AirPcap adapters... */
+ return TRUE; /* Use Wireshark keys and set them ad default for airpcap devices */
+
+ return equals;
+}
+
+/*
+ * This function will load from the preferences file ALL the
+ * keys (WEP, WPA_PWD and WPA_BIN) and will set them as default for
+ * each adapter. To do this, it will save the keys in the registry...
+ * A check will be performed, to make sure that keys found in
+ * registry and keys found in Wireshark preferences are the same. If not,
+ * the user will be asked to choose if use all keys (merge them),
+ * or use Wireshark preferences ones. In the last case, registry keys will
+ * be overwritten for all the connected AirPcap adapters.
+ * In the first case, adapters will use their own keys, but those
+ * keys will not be accessible via Wireshark...
+ */
+void
+airpcap_load_decryption_keys(GList* if_list)
+{
+ gint if_n = 0;
+ gint i = 0;
+ airpcap_if_info_t* curr_if = NULL;
+
+ if(if_list == NULL) return;
+
+ if_n = g_list_length(if_list);
+
+ for(i = 0; i < if_n; i++)
+ {
+ curr_if = (airpcap_if_info_t*)g_list_nth_data(if_list,i);
+ load_wlan_driver_wep_keys();
+ }
+}
+
+/*
+ * This function will set the gibven GList of decryption_key_t structures
+ * as the defoult for both Wireshark and the AirPcap adapters...
+ */
+void
+airpcap_save_decryption_keys(GList* key_list, GList* adapters_list)
+{
+ gint if_n = 0;
+ gint key_n = 0;
+ gint i = 0;
+ airpcap_if_info_t* curr_if = NULL;
+ GList* empty_key_list = NULL;
+
+ if( (key_list == NULL) || (adapters_list == NULL)) return;
+
+ if_n = g_list_length(adapters_list);
+ key_n = g_list_length(key_list);
+
+ /* Set the driver's global list of keys. */
+ write_wlan_driver_wep_keys_to_registry(key_list);
+
+ /* Empty the key list for each interface */
+ for(i = 0; i < if_n; i++)
+ {
+ curr_if = (airpcap_if_info_t*)g_list_nth_data(adapters_list,i);
+ write_wlan_wep_keys_to_registry(curr_if,empty_key_list);
+ }
+
+ /*
+ * This will set the keys of the current adapter as Wireshark default...
+ * Now all the adapters have the same keys, so curr_if is ok as any other...
+ */
+ save_wlan_wireshark_wep_keys(key_list);
+}
+
+/*
+ * This function is used to enable/disable the toolbar widgets
+ * depending on the type of interface selected... Not the whole
+ * toolbar must be grayed/enabled ... Only some widgets...
+ */
+void
+airpcap_enable_toolbar_widgets(GtkWidget* w, gboolean en)
+{
+ GtkWidget *toolbar_tb,
+ *if_description_lb,
+ *toolbar_channel_cb,
+ *channel_lb,
+ *channel_offset_cb,
+ *channel_offset_lb,
+ *fcs_cb,
+ *fcs_lb,
+ *advanced_bt;
+
+ if(w == NULL)
+ return;
+
+ toolbar_tb = w;
+
+ if_description_lb = g_object_get_data(G_OBJECT(toolbar_tb),AIRPCAP_TOOLBAR_INTERFACE_KEY);
+ channel_lb = g_object_get_data(G_OBJECT(toolbar_tb),AIRPCAP_TOOLBAR_CHANNEL_LABEL_KEY);
+ toolbar_channel_cb = g_object_get_data(G_OBJECT(toolbar_tb),AIRPCAP_TOOLBAR_CHANNEL_KEY);
+ channel_offset_cb = g_object_get_data(G_OBJECT(toolbar_tb),AIRPCAP_TOOLBAR_CHANNEL_OFFSET_KEY);
+ channel_offset_lb = g_object_get_data(G_OBJECT(toolbar_tb),AIRPCAP_TOOLBAR_CHANNEL_OFFSET_LABEL_KEY);
+ fcs_lb = g_object_get_data(G_OBJECT(toolbar_tb),AIRPCAP_TOOLBAR_FCS_FILTER_LABEL_KEY);
+ fcs_cb = g_object_get_data(G_OBJECT(toolbar_tb),AIRPCAP_TOOLBAR_FCS_FILTER_KEY);
+ advanced_bt = g_object_get_data(G_OBJECT(toolbar_tb),AIRPCAP_TOOLBAR_ADVANCED_KEY);
+
+
+ if(if_description_lb != NULL)
+ gtk_widget_set_sensitive(if_description_lb,en);
+ if(channel_lb != NULL)
+ gtk_widget_set_sensitive(channel_lb,en);
+ if(toolbar_channel_cb != NULL)
+ gtk_widget_set_sensitive(toolbar_channel_cb,en);
+ if(channel_offset_cb != NULL)
+ gtk_widget_set_sensitive(channel_offset_cb,en);
+ if(channel_offset_lb != NULL)
+ gtk_widget_set_sensitive(channel_offset_lb,en);
+ if(fcs_lb != NULL)
+ gtk_widget_set_sensitive(fcs_lb,en);
+ if(fcs_cb != NULL)
+ gtk_widget_set_sensitive(fcs_cb,en);
+ if(advanced_bt != NULL)
+ gtk_widget_set_sensitive(advanced_bt,en);
+
+ return;
+}
+
+#endif /* HAVE_AIRPCAP */
+
+/*
+ * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/gtk/airpcap_gui_utils.h b/ui/gtk/airpcap_gui_utils.h
new file mode 100644
index 0000000000..fb84a25d66
--- /dev/null
+++ b/ui/gtk/airpcap_gui_utils.h
@@ -0,0 +1,221 @@
+/* airpcap_utils.h
+ * Declarations of utility routines for the "Airpcap" dialog widgets
+ *
+ * $Id$
+ *
+ * Giorgio Tino <giorgio.tino@cacetech.com>
+ * Copyright (c) CACE Technologies, LLC 2006
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __AIRPCAP_GUI_UTILS_H__
+#define __AIRPCAP_GUI_UTILS_H__
+
+#define AIRPCAP_VALIDATION_TYPE_NAME_ALL "All Frames"
+#define AIRPCAP_VALIDATION_TYPE_NAME_CORRECT "Valid Frames"
+#define AIRPCAP_VALIDATION_TYPE_NAME_CORRUPT "Invalid Frames"
+#define AIRPCAP_VALIDATION_TYPE_NAME_UNKNOWN "Unknown"
+
+#define AIRPCAP_LINK_TYPE_NAME_802_11_ONLY "802.11 Only"
+#define AIRPCAP_LINK_TYPE_NAME_802_11_PLUS_RADIO "802.11 + Radio"
+#define AIRPCAP_LINK_TYPE_NAME_802_11_PLUS_PPI "802.11 + PPI"
+#define AIRPCAP_LINK_TYPE_NAME_UNKNOWN "Unknown"
+
+#define AIRPCAP_LINK_TYPE_NUM_802_11_ONLY 0
+#define AIRPCAP_LINK_TYPE_NUM_802_11_PLUS_RADIO 1
+#define AIRPCAP_LINK_TYPE_NUM_802_11_PLUS_PPI 2
+
+#define AIRPCAP_DECRYPTION_TYPE_STRING_WIRESHARK "Wireshark"
+#define AIRPCAP_DECRYPTION_TYPE_STRING_AIRPCAP "Driver"
+#define AIRPCAP_DECRYPTION_TYPE_STRING_NONE "None"
+
+#define NO_ROW_SELECTED -1
+
+/* Key list columns */
+enum {
+ KL_COL_TYPE,
+ KL_COL_KEY,
+ KL_COL_SSID,
+ KL_NUM_COLS
+};
+
+/* Controls the releay of settings back to the adapter. */
+extern gboolean change_airpcap_settings;
+
+/*
+ * set up the airpcap toolbar for the new capture interface
+ */
+void
+airpcap_set_toolbar_start_capture(airpcap_if_info_t* if_info);
+
+/*
+ * Set up the airpcap toolbar for the new capture interface
+ */
+void
+airpcap_set_toolbar_stop_capture(airpcap_if_info_t* if_info);
+
+/*
+ * Add a key (string) to the given list
+ */
+void
+airpcap_add_key_to_list(GtkListStore *key_list_store, gchar* type, gchar* key, gchar* ssid);
+
+/*
+ * Fill the list with the keys
+ */
+void
+airpcap_fill_key_list(GtkListStore *key_list_store);
+
+/*
+ * Function used to retrieve the AirpcapValidationType given the string name.
+ */
+AirpcapValidationType
+airpcap_get_validation_type(const gchar* name);
+
+/*
+ * Function used to retrieve the string name given an AirpcapValidationType.
+ */
+gchar*
+airpcap_get_validation_name(AirpcapValidationType vt);
+
+/*
+ * Return an appropriate combo box entry number for the given an AirpcapValidationType.
+ */
+gint
+airpcap_get_validation_combo_entry(AirpcapValidationType vt);
+
+/*
+ * Returns the AirpcapLinkType corresponding to the given string name.
+ */
+AirpcapLinkType
+airpcap_get_link_type(const gchar* name);
+
+/*
+ * Returns the string name corresponding to the given AirpcapLinkType.
+ */
+gchar*
+airpcap_get_link_name(AirpcapLinkType lt);
+
+/*
+ * Sets the entry of the validation combo using the AirpcapValidationType.
+ */
+void
+airpcap_validation_type_combo_set_by_type(GtkWidget* c,AirpcapValidationType type);
+
+/*
+ * Update channel offset combo box to 'offset'.
+ */
+void
+airpcap_update_channel_offset_combo(airpcap_if_info_t* if_info, guint32 ch_freq, GtkWidget *channel_offset_cb, gboolean set);
+
+
+/*
+ * Retrieve the guint corresponding to the given string (channel only, handle with care!)
+ */
+gchar*
+airpcap_get_channel_name(guint n);
+
+/*
+ * Set the combo box entry string given an guint channel number
+ */
+void
+airpcap_channel_combo_set_by_frequency(GtkWidget* w,guint channel);
+
+/** Respond to the user changing the channel combo box.
+ * Update the active interface channel and update the offset
+ * combo box.
+ * Requires AirPcap globals.
+ *
+ * @param channel_cb The channel GtkComboBox
+ * @param channel_offset_cb The channel offset GtkComboBox
+ */
+void
+airpcap_channel_changed_set_cb(GtkWidget *channel_cb, gpointer channel_offset_cb);
+
+/** Respond to the user changing the channel combo box.
+ * Update the offset combo box but not the channel.
+ * Requires AirPcap globals.
+ *
+ * @param channel_cb The channel GtkComboBox
+ * @param channel_offset_cb The channel offset GtkComboBox
+ */
+void
+airpcap_channel_changed_noset_cb(GtkWidget *channel_cb, gpointer channel_offset_cb);
+
+/** Respond to the user changing the channel offset combo box.
+ * Update the active interface channel offset.
+ * Requires AirPcap globals.
+ *
+ * @param channel_offset_cb The channel offset GtkComboBox
+ * @param data Unused
+ */
+void
+airpcap_channel_offset_changed_cb(GtkWidget *channel_offset_cb, gpointer data);
+
+/*
+ * Returns '1' if this is the "Any" adapter, '0' otherwise
+ */
+int
+airpcap_if_is_any(airpcap_if_info_t* if_info);
+
+/*
+ * Update channel combo box. If the airpcap interface is "Any", the combo box will be disabled.
+ */
+void
+airpcap_update_channel_combo(GtkWidget* channel_cb, airpcap_if_info_t* if_info);
+
+/*
+ * This function will take the current keys (widget list), specified for the
+ * current adapter, and save them as default for ALL the others.
+ */
+void
+airpcap_read_and_save_decryption_keys_from_list_store(GtkListStore* key_list_store, airpcap_if_info_t* info_if, GList* if_list);
+
+/*
+ * This function will load from the preferences file ALL the
+ * keys (WEP, WPA and WPA_BIN) and will set them as default for
+ * each adapter. To do this, it will save the keys in the registry...
+ */
+void
+airpcap_load_decryption_keys(GList* if_list);
+
+/*
+ * This function will load from the preferences file ALL the
+ * keys (WEP, WPA and WPA_BIN) and will set them as default for
+ * each adapter. To do this, it will save the keys in the registry...
+ */
+gboolean
+airpcap_check_decryption_keys(GList* if_list);
+
+/*
+ * This function will set the gibven GList of decryption_key_t structures
+ * as the defoult for both Wireshark and the AirPcap adapters...
+ */
+void
+airpcap_save_decryption_keys(GList* key_list, GList* adapters_list);
+
+/*
+ * This function is used to enable/disable the toolbar widgets
+ * depending on the type of interface selected...
+ */
+void
+airpcap_enable_toolbar_widgets(GtkWidget* w, gboolean en);
+
+#endif
diff --git a/ui/gtk/ansi_a_stat.c b/ui/gtk/ansi_a_stat.c
new file mode 100644
index 0000000000..8b1eccd9e8
--- /dev/null
+++ b/ui/gtk/ansi_a_stat.c
@@ -0,0 +1,373 @@
+/* ansi_a_stat.c
+ *
+ * Copyright 2003, Michael Lum <mlum [AT] telostech.com>
+ * In association with Telos Technology Inc.
+ *
+ * MUCH code modified from service_response_time_table.c.
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * This TAP provides statistics for the ANSI A-Interface:
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+#include <string.h>
+
+#include "epan/packet_info.h"
+#include "epan/epan.h"
+#include "epan/value_string.h"
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-bssap.h>
+#include <epan/dissectors/packet-ansi_a.h>
+
+#include "../stat_menu.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/gui_utils.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+enum
+{
+ IEI_COLUMN,
+ MSG_NAME_COLUMN,
+ COUNT_COLUMN,
+ N_COLUMN /* The number of columns */
+};
+
+typedef struct _ansi_a_stat_dlg_t {
+ GtkWidget *win;
+ GtkWidget *scrolled_win;
+ GtkWidget *table;
+} ansi_a_stat_dlg_t;
+
+typedef struct _ansi_a_stat_t {
+ int bsmap_message_type[0xff];
+ int dtap_message_type[0xff];
+} ansi_a_stat_t;
+
+
+static ansi_a_stat_dlg_t dlg_bsmap;
+static ansi_a_stat_dlg_t dlg_dtap;
+static ansi_a_stat_t ansi_a_stat;
+
+
+static void
+ansi_a_stat_reset(
+ void *tapdata)
+{
+ ansi_a_stat_t *stat_p = tapdata;
+
+ memset(stat_p, 0, sizeof(ansi_a_stat_t));
+}
+
+
+static gboolean
+ansi_a_stat_packet(
+ void *tapdata,
+ packet_info *pinfo _U_,
+ epan_dissect_t *edt _U_,
+ const void *data)
+{
+ ansi_a_stat_t *stat_p = tapdata;
+ const ansi_a_tap_rec_t *data_p = data;
+
+ switch (data_p->pdu_type)
+ {
+ case BSSAP_PDU_TYPE_BSMAP:
+ stat_p->bsmap_message_type[data_p->message_type]++;
+ break;
+
+ case BSSAP_PDU_TYPE_DTAP:
+ stat_p->dtap_message_type[data_p->message_type]++;
+ break;
+
+ default:
+ /*
+ * unknown PDU type !!!
+ */
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+
+static void
+ansi_a_stat_draw(
+ void *tapdata)
+{
+ ansi_a_stat_t *stat_p = tapdata;
+ int i;
+ GtkListStore *list_store;
+ GtkTreeIter iter;
+
+ if (dlg_bsmap.win && tapdata)
+ {
+ list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (dlg_bsmap.table))); /* Get store */
+
+ i = 0;
+ while (ansi_a_bsmap_strings[i].strptr){
+ /* Creates a new row at position. iter will be changed to point to this new row.
+ * If position is larger than the number of rows on the list, then the new row will be appended to the list.
+ * The row will be filled with the values given to this function.
+ * :
+ * should generally be preferred when inserting rows in a sorted list store.
+ */
+ gtk_list_store_insert_with_values( list_store , &iter, G_MAXINT,
+ IEI_COLUMN, ansi_a_bsmap_strings[i].value,
+ MSG_NAME_COLUMN, (char *)ansi_a_bsmap_strings[i].strptr,
+ COUNT_COLUMN, stat_p->bsmap_message_type[ansi_a_bsmap_strings[i].value],
+ -1);
+ i++;
+ }
+ }
+
+ if (dlg_dtap.win && tapdata){
+ i = 0;
+ list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (dlg_dtap.table))); /* Get store */
+
+
+ while (ansi_a_dtap_strings[i].strptr){
+ /* Creates a new row at position. iter will be changed to point to this new row.
+ * If position is larger than the number of rows on the list, then the new row will be appended to the list.
+ * The row will be filled with the values given to this function.
+ * :
+ * should generally be preferred when inserting rows in a sorted list store.
+ */
+ gtk_list_store_insert_with_values( list_store , &iter, G_MAXINT,
+ IEI_COLUMN, ansi_a_dtap_strings[i].value,
+ MSG_NAME_COLUMN, (char *)ansi_a_dtap_strings[i].strptr,
+ COUNT_COLUMN, stat_p->dtap_message_type[ansi_a_dtap_strings[i].value],
+ -1);
+
+ i++;
+ }
+ }
+}
+
+static void
+ansi_a_stat_gtk_win_destroy_cb(
+ GtkWindow *win _U_,
+ gpointer user_data )
+{
+ memset((void *) user_data, 0, sizeof(ansi_a_stat_dlg_t));
+}
+
+
+/* Create list */
+static
+GtkWidget* create_list(void)
+{
+
+ GtkListStore *list_store;
+ GtkWidget *list;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSortable *sortable;
+ GtkTreeView *list_view;
+ GtkTreeSelection *selection;
+
+ /* Create the store */
+ list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX */
+ G_TYPE_UINT, /* IEI */
+ G_TYPE_STRING, /* Message Name */
+ G_TYPE_UINT); /* Count */
+
+ /* Create a view */
+ list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
+
+ list_view = GTK_TREE_VIEW(list);
+ sortable = GTK_TREE_SORTABLE(list_store);
+
+ /* Speed up the list display */
+ gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
+
+ /* Setup the sortable columns */
+ gtk_tree_sortable_set_sort_column_id(sortable, IEI_COLUMN, GTK_SORT_ASCENDING);
+ gtk_tree_view_set_headers_clickable(list_view, FALSE);
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref (G_OBJECT (list_store));
+
+ /*
+ * Create the first column packet, associating the "text" attribute of the
+ * cell_renderer to the first column of the model
+ */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("IEI", renderer,
+ "text", IEI_COLUMN,
+ NULL);
+
+ gtk_tree_view_column_set_cell_data_func(column, renderer, present_as_hex_func,
+ GINT_TO_POINTER(IEI_COLUMN), NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, IEI_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 50);
+
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Second column.. Message Name. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Message Name", renderer,
+ "text", MSG_NAME_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, MSG_NAME_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 280);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Third column.. Count. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Count", renderer,
+ "text", COUNT_COLUMN,
+ NULL);
+
+
+ gtk_tree_view_column_set_sort_column_id(column, COUNT_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 50);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Now enable the sorting of each column */
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
+
+ /* Setup the selection handler */
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+
+ return list;
+
+}
+static void
+ansi_a_stat_gtk_win_create(
+ ansi_a_stat_dlg_t *dlg_p,
+ const char *title)
+{
+ GtkWidget *vbox;
+ GtkWidget *bt_close;
+ GtkWidget *bbox;
+
+
+ dlg_p->win= dlg_window_new(title); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(dlg_p->win), TRUE);
+
+ gtk_window_set_default_size(GTK_WINDOW(dlg_p->win), 480, 450);
+
+ vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(dlg_p->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ dlg_p->scrolled_win = scrolled_window_new(NULL, NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), dlg_p->scrolled_win, TRUE, TRUE, 0);
+
+ dlg_p->table = create_list();
+ gtk_widget_show(dlg_p->table);
+
+
+ gtk_container_add(GTK_CONTAINER(dlg_p->scrolled_win), dlg_p->table);
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ bt_close = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(dlg_p->win, bt_close, window_cancel_button_cb);
+
+ g_signal_connect(dlg_p->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(dlg_p->win, "destroy", G_CALLBACK(ansi_a_stat_gtk_win_destroy_cb), dlg_p);
+
+ gtk_widget_show_all(dlg_p->win);
+ window_present(dlg_p->win);
+}
+
+void
+ansi_a_stat_gtk_bsmap_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ /*
+ * if the window is already open, bring it to front
+ */
+ if (dlg_bsmap.win)
+ {
+ gdk_window_raise(gtk_widget_get_window(dlg_bsmap.win));
+ return;
+ }
+
+ ansi_a_stat_gtk_win_create(&dlg_bsmap, "ANSI A-I/F BSMAP Statistics");
+ ansi_a_stat_draw(&ansi_a_stat);
+}
+
+void
+ansi_a_stat_gtk_dtap_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+
+ /*
+ * if the window is already open, bring it to front
+ */
+ if (dlg_dtap.win)
+ {
+ gdk_window_raise(gtk_widget_get_window(dlg_dtap.win));
+ return;
+ }
+
+ ansi_a_stat_gtk_win_create(&dlg_dtap, "ANSI A-I/F DTAP Statistics");
+ ansi_a_stat_draw(&ansi_a_stat);
+}
+
+void
+register_tap_listener_gtkansi_a_stat(void)
+{
+ GString *err_p;
+
+
+ memset((void *) &ansi_a_stat, 0, sizeof(ansi_a_stat_t));
+
+ err_p =
+ register_tap_listener("ansi_a", &ansi_a_stat, NULL, 0,
+ ansi_a_stat_reset,
+ ansi_a_stat_packet,
+ ansi_a_stat_draw);
+
+ if (err_p != NULL)
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_p->str);
+ g_string_free(err_p, TRUE);
+
+ exit(1);
+ }
+}
+
diff --git a/ui/gtk/ansi_map_stat.c b/ui/gtk/ansi_map_stat.c
new file mode 100644
index 0000000000..893707bcbc
--- /dev/null
+++ b/ui/gtk/ansi_map_stat.c
@@ -0,0 +1,375 @@
+/* ansi_map_stat.c
+ *
+ * Copyright 2003, Michael Lum <mlum [AT] telostech.com>
+ * In association with Telos Technology Inc.
+ *
+ * MUCH code modified from service_response_time_table.c.
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * This TAP provides statistics for ANSI MAP:
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+#include <string.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/value_string.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-ansi_map.h>
+
+#include "../stat_menu.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/gui_utils.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+enum
+{
+ OP_CODE_COLUMN,
+ OP_CODE_NAME_COLUMN,
+ COUNT_COLUMN,
+ TOT_BYTES_COLUMN,
+ AVG_BYTES_COLUMN,
+ N_COLUMN /* The number of columns */
+};
+
+typedef struct _ansi_map_stat_dlg_t {
+ GtkWidget *win;
+ GtkWidget *scrolled_win;
+ GtkWidget *table;
+} ansi_map_stat_dlg_t;
+
+typedef struct _ansi_map_stat_t {
+ int message_type[ANSI_MAP_MAX_NUM_MESSAGE_TYPES];
+ double size[ANSI_MAP_MAX_NUM_MESSAGE_TYPES];
+} ansi_map_stat_t;
+
+static ansi_map_stat_dlg_t dlg;
+static ansi_map_stat_t ansi_a_stat;
+
+/* Create list */
+static
+GtkWidget* create_list(void)
+{
+
+ GtkListStore *list_store;
+ GtkWidget *list;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSortable *sortable;
+ GtkTreeView *list_view;
+ GtkTreeSelection *selection;
+
+ /* Create the store */
+ list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX*/
+ G_TYPE_UINT, /* Op Code */
+ G_TYPE_STRING, /* Operation Name */
+ G_TYPE_UINT, /* Count */
+ G_TYPE_UINT, /* Total Bytes */
+ G_TYPE_FLOAT); /* Avg Bytes */
+
+ /* Create a view */
+ list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
+
+ list_view = GTK_TREE_VIEW(list);
+ sortable = GTK_TREE_SORTABLE(list_store);
+
+ /* Speed up the list display */
+ gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
+
+ /* Setup the sortable columns */
+ gtk_tree_sortable_set_sort_column_id(sortable, OP_CODE_COLUMN, GTK_SORT_ASCENDING);
+ gtk_tree_view_set_headers_clickable(list_view, FALSE);
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref (G_OBJECT (list_store));
+
+ /*
+ * Create the first column packet, associating the "text" attribute of the
+ * cell_renderer to the first column of the model
+ */
+ /* 1:st column */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Op Code", renderer,
+ "text", OP_CODE_COLUMN,
+ NULL);
+
+ gtk_tree_view_column_set_cell_data_func(column, renderer, present_as_hex_func,
+ GINT_TO_POINTER(OP_CODE_COLUMN), NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, OP_CODE_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 2:nd column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Operation Name", renderer,
+ "text", OP_CODE_NAME_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, OP_CODE_NAME_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 290);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 3:d column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Count", renderer,
+ "text", COUNT_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, COUNT_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 50);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 4:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Total Bytes", renderer,
+ "text", TOT_BYTES_COLUMN,
+ NULL);
+
+
+ gtk_tree_view_column_set_sort_column_id(column, TOT_BYTES_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 10:th column.. Avg Bytes. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Avg Bytes", renderer,
+ "text", AVG_BYTES_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
+ GINT_TO_POINTER(AVG_BYTES_COLUMN), NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, AVG_BYTES_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 50);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Now enable the sorting of each column */
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
+
+ /* Setup the selection handler */
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+
+ return list;
+
+}
+
+static void
+ansi_map_stat_reset(
+ void *tapdata)
+{
+ ansi_map_stat_t *stat_p = tapdata;
+
+ memset(stat_p, 0, sizeof(ansi_map_stat_t));
+}
+
+
+static gboolean
+ansi_map_stat_packet(
+ void *tapdata,
+ packet_info *pinfo _U_,
+ epan_dissect_t *edt _U_,
+ const void *data)
+{
+ ansi_map_stat_t *stat_p = tapdata;
+ const ansi_map_tap_rec_t *data_p = data;
+
+#if 0 /* always false because message_type is 8 bit value */
+ if (data_p->message_type >= ANSI_MAP_MAX_NUM_MESSAGE_TYPES)
+ {
+ /*
+ * unknown PDU type !!!
+ */
+ return(FALSE);
+ }
+#endif
+
+ stat_p->message_type[data_p->message_type]++;
+ stat_p->size[data_p->message_type] += data_p->size;
+
+ return(TRUE);
+}
+
+
+static void
+ansi_map_stat_draw(
+ void *tapdata)
+{
+ ansi_map_stat_t *stat_p = tapdata;
+ int i;
+ float avg;
+ GtkListStore *list_store;
+ GtkTreeIter iter;
+
+ if (dlg.win && tapdata)
+ {
+ i = 0;
+ list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (dlg.table))); /* Get store */
+ while (ansi_map_opr_code_strings[i].strptr)
+ {
+ avg = 0.0f;
+ if (stat_p->message_type[ansi_map_opr_code_strings[i].value] !=0 ){
+ avg = (float)stat_p->size[ansi_map_opr_code_strings[i].value]/(float)stat_p->message_type[ansi_map_opr_code_strings[i].value];
+ }
+ /* Creates a new row at position. iter will be changed to point to this new row.
+ * If position is larger than the number of rows on the list, then the new row will be appended to the list.
+ * The row will be filled with the values given to this function.
+ * :
+ * should generally be preferred when inserting rows in a sorted list store.
+ */
+ gtk_list_store_insert_with_values( list_store , &iter, G_MAXINT,
+ OP_CODE_COLUMN, ansi_map_opr_code_strings[i].value,
+ OP_CODE_NAME_COLUMN, ansi_map_opr_code_strings[i].strptr,
+ COUNT_COLUMN, (guint)stat_p->message_type[ansi_map_opr_code_strings[i].value],
+ TOT_BYTES_COLUMN, (guint)stat_p->size[ansi_map_opr_code_strings[i].value],
+ AVG_BYTES_COLUMN, avg,
+ -1);
+ i++;
+ }
+ }
+}
+
+
+
+
+static void
+ansi_map_stat_gtk_win_destroy_cb(
+ GtkWindow *win _U_,
+ gpointer user_data)
+{
+ memset((void *) user_data, 0, sizeof(ansi_map_stat_dlg_t));
+}
+
+
+static void
+ansi_map_stat_gtk_win_create(
+ ansi_map_stat_dlg_t *dlg_p,
+ const char *title)
+{
+ GtkWidget *vbox;
+ GtkWidget *bt_close;
+ GtkWidget *bbox;
+
+
+ dlg_p->win= dlg_window_new(title); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(dlg_p->win), TRUE);
+
+ gtk_window_set_default_size(GTK_WINDOW(dlg_p->win), 500, 450);
+
+ vbox = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(dlg_p->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ dlg_p->scrolled_win = scrolled_window_new(NULL, NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), dlg_p->scrolled_win, TRUE, TRUE, 0);
+
+ dlg_p->table = create_list();
+
+ gtk_container_add(GTK_CONTAINER(dlg_p->scrolled_win), dlg_p->table);
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ bt_close = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+
+ window_set_cancel_button(dlg_p->win, bt_close, window_cancel_button_cb);
+
+ g_signal_connect(dlg_p->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(dlg_p->win, "destroy", G_CALLBACK(ansi_map_stat_gtk_win_destroy_cb), dlg_p);
+
+ gtk_widget_show_all(dlg_p->win);
+ window_present(dlg_p->win);
+}
+
+void
+ansi_map_stat_gtk_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ /*
+ * if the window is already open, bring it to front
+ */
+ if (dlg.win){
+ gdk_window_raise(gtk_widget_get_window(dlg.win));
+ return;
+ }
+
+ ansi_map_stat_gtk_win_create(&dlg, "ANSI MAP Operation Statistics");
+ ansi_map_stat_draw(&ansi_a_stat);
+}
+
+
+static void
+ansi_map_stat_gtk_init(
+ const char *optarg _U_,
+ void* userdata _U_ )
+{
+ ansi_map_stat_gtk_cb(NULL, NULL);
+}
+
+
+void
+register_tap_listener_gtkansi_map_stat(void)
+{
+ GString *err_p;
+
+
+ memset((void *) &ansi_a_stat, 0, sizeof(ansi_map_stat_t));
+
+ err_p =
+ register_tap_listener("ansi_map", &ansi_a_stat, NULL, 0,
+ ansi_map_stat_reset,
+ ansi_map_stat_packet,
+ ansi_map_stat_draw);
+
+ if (err_p != NULL)
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_p->str);
+ g_string_free(err_p, TRUE);
+
+ exit(1);
+ }
+ register_stat_cmd_arg("ansi_map", ansi_map_stat_gtk_init,NULL);
+}
diff --git a/ui/gtk/bootp_stat.c b/ui/gtk/bootp_stat.c
new file mode 100644
index 0000000000..5b4e9f7c6a
--- /dev/null
+++ b/ui/gtk/bootp_stat.c
@@ -0,0 +1,300 @@
+/* bootp_stat.c
+ * boop_stat 2003 Jean-Michel FAYARD
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* #define DEBUG do{ printf("%s:%d ",__FILE__,__LINE__);} while(0); */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+#include <string.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/tap.h>
+
+#include "../simple_dialog.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/main.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+typedef const char* bootp_info_value_t;
+
+/* used to keep track of the statictics for an entire program interface */
+typedef struct _dhcp_stats_t {
+ char *filter;
+ GtkWidget *win;
+ GHashTable *hash;
+ GtkWidget *table_message_type;
+ guint index; /* Number of to display */
+} dhcpstat_t;
+/* used to keep track of a single DHCP message type */
+typedef struct _dhcp_message_type_t {
+ const char *name;
+ guint32 packets;
+ GtkWidget *widget;/* label in which we print the number of packets */
+ dhcpstat_t *sp; /* entire program interface */
+} dhcp_message_type_t;
+
+static void
+dhcp_free_hash( gpointer key _U_ , gpointer value, gpointer user_data _U_ )
+{
+ g_free(value);
+}
+static void
+dhcp_reset_hash(gchar *key _U_ , dhcp_message_type_t *data, gpointer ptr _U_ )
+{
+ data->packets = 0;
+}
+
+/* Update the entry corresponding to the number of packets of a special DHCP Message Type
+ * or create it if it don't exist.
+ */
+static void
+dhcp_draw_message_type(gchar *key _U_, dhcp_message_type_t *data, gchar * unused _U_ )
+{
+ char string_buff[256];
+
+ if ((data==NULL) || (data->packets==0))
+ return;
+ if (data->widget==NULL){ /* create an entry in the table */
+ GtkWidget *tmp;
+ int x = 2*((data->sp->index) % 2);
+ int y = (data->sp->index) /2;
+
+
+ /* Maybe we should display the hexadecimal value ? */
+ /* g_snprintf(string_buff, sizeof(string_buff), "%s (0X%x)", data->name, *key); */
+ tmp = gtk_label_new( data->name /* string_buff */ );
+ gtk_table_attach_defaults(GTK_TABLE(data->sp->table_message_type), tmp, x, x+1, y, y+1);
+ gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
+ gtk_widget_show(tmp);
+
+ g_snprintf( string_buff, sizeof(string_buff), "%9d", data->packets );
+ data->widget = gtk_label_new( string_buff );
+ gtk_table_attach_defaults(GTK_TABLE(data->sp->table_message_type), data->widget, x+1, x+2, y, y+1);
+ gtk_label_set_justify(GTK_LABEL(data->widget), GTK_JUSTIFY_LEFT);
+ gtk_widget_show( data->widget );
+
+ data->sp->index++;
+ } else {
+ /* Just update the label string */
+ g_snprintf( string_buff, sizeof(string_buff), "%9d", data->packets );
+ gtk_label_set_text( GTK_LABEL(data->widget), string_buff);
+ }
+}
+static void
+dhcpstat_reset(void *psp)
+{
+ dhcpstat_t *sp=psp;
+ g_hash_table_foreach( sp->hash, (GHFunc)dhcp_reset_hash, NULL);
+}
+static int
+dhcpstat_packet(void *psp, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *pri)
+{
+ dhcpstat_t *sp=psp;
+ const bootp_info_value_t value=pri;
+ dhcp_message_type_t *sc;
+
+ if (sp==NULL)
+ return 0;
+ sc = g_hash_table_lookup(
+ sp->hash,
+ value);
+ if (!sc) {
+ /*g_warning("%s:%d What's Wrong for %s, doc ?", __FILE__, __LINE__, value);*/
+ sc = g_malloc( sizeof(dhcp_message_type_t) );
+ sc -> packets = 1;
+ sc -> name = value;
+ sc -> widget=NULL;
+ sc -> sp = sp;
+ g_hash_table_insert(
+ sp->hash,
+ (gpointer) value,
+ sc);
+ } else {
+ /*g_warning("sc(%s)->packets++", sc->name);*/
+ sc->packets++;
+ }
+ return 1;
+}
+
+
+static void
+dhcpstat_draw(void *psp)
+{
+ dhcpstat_t *sp=psp;
+ guint idx;
+
+ idx=sp->index;
+ g_hash_table_foreach( sp->hash, (GHFunc) dhcp_draw_message_type, NULL );
+ if (idx != sp->index){
+ /* We have inserted a new entry corresponding to a status code ,
+ * let's resize the table */
+ gtk_table_resize ( GTK_TABLE(sp->table_message_type), sp->index % 2 , 4);
+ }
+
+}
+
+
+
+/* since the gtk2 implementation of tap is multithreaded we must protect
+ * remove_tap_listener() from modifying the list while draw_tap_listener()
+ * is running. the other protected block is in main.c
+ *
+ * there should not be any other critical regions in gtk2
+ */
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ dhcpstat_t *sp=(dhcpstat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(sp);
+ unprotect_thread_critical_region();
+
+ g_free(sp->filter);
+ g_hash_table_foreach( sp->hash, (GHFunc)dhcp_free_hash, NULL);
+ g_hash_table_destroy( sp->hash);
+ g_free(sp);
+}
+
+
+/* When called, this function will create a new instance of gtk2-dhcpstat.
+ */
+static void
+dhcpstat_init(const char *optarg, void *userdata _U_)
+{
+ dhcpstat_t *sp;
+ const char *filter=NULL;
+ char *title=NULL;
+ GString *error_string;
+ GtkWidget *message_type_fr;
+ GtkWidget *vbox;
+ GtkWidget *bt_close;
+ GtkWidget *bbox;
+
+ if (strncmp (optarg, "bootp,stat,", 11) == 0){
+ filter=optarg+11;
+ } else {
+ filter=NULL;
+ }
+
+ sp = g_malloc( sizeof(dhcpstat_t) );
+ sp->hash = g_hash_table_new( g_str_hash, g_str_equal);
+ if(filter){
+ sp->filter=g_strdup(filter);
+ title=g_strdup_printf("DHCP statistics with filter: %s", filter);
+ } else {
+ sp->filter=NULL;
+ title=g_strdup("DHCP statistics");
+ }
+
+ /* transient_for top_level */
+ sp->win= dlg_window_new(title);
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(sp->win), TRUE);
+ g_free(title);
+
+ vbox = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(sp->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ /* Status Codes frame */
+ message_type_fr = gtk_frame_new("DHCP Message Type");
+ gtk_container_add(GTK_CONTAINER(vbox), message_type_fr);
+ gtk_widget_show(message_type_fr);
+
+ sp->table_message_type = gtk_table_new( 0, 4, FALSE);
+ gtk_table_set_col_spacings( GTK_TABLE(sp->table_message_type), 10);
+ gtk_container_add( GTK_CONTAINER( message_type_fr), sp->table_message_type);
+ gtk_container_set_border_width( GTK_CONTAINER(sp->table_message_type) , 10);
+ sp->index = 0; /* Nothing to display yet */
+
+
+ error_string = register_tap_listener(
+ "bootp",
+ sp,
+ filter,
+ 0,
+ dhcpstat_reset,
+ dhcpstat_packet,
+ dhcpstat_draw);
+ if (error_string){
+ /* error, we failed to attach to the tap. clean up */
+ simple_dialog( ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str );
+ g_free(sp->filter);
+ g_free(sp);
+ g_string_free(error_string, TRUE);
+ return ;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ bt_close = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(sp->win, bt_close, window_cancel_button_cb);
+
+ g_signal_connect(sp->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(sp->win, "destroy", G_CALLBACK(win_destroy_cb), sp);
+
+ gtk_widget_show_all(sp->win);
+
+ window_present(sp->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(sp->win));
+}
+
+static tap_param bootp_stat_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg dhcp_stat_dlg = {
+ "BOOTP-DHCP Packet Counter",
+ "bootp,stat",
+ dhcpstat_init,
+ -1,
+ G_N_ELEMENTS(bootp_stat_params),
+ bootp_stat_params
+};
+
+void
+register_tap_listener_gtkdhcpstat(void)
+{
+ register_dfilter_stat(&dhcp_stat_dlg, "BOOTP-DHCP",
+ REGISTER_STAT_GROUP_UNSORTED);
+}
+
+
+void bootp_dhcp_stat_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &dhcp_stat_dlg);
+}
+
diff --git a/ui/gtk/camel_counter.c b/ui/gtk/camel_counter.c
new file mode 100644
index 0000000000..66c3dd4d95
--- /dev/null
+++ b/ui/gtk/camel_counter.c
@@ -0,0 +1,242 @@
+/* camel_counter.c
+ * camel message counter for Wireshark
+ * Copyright 2006 Florent Drouin (based on h225_counter.c from Lars Roland)
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <string.h>
+#include <gtk/gtk.h>
+
+#include <epan/epan.h>
+#include <epan/packet_info.h>
+#include <epan/value_string.h>
+#include <epan/tap.h>
+#include <epan/packet.h>
+#include <epan/asn1.h>
+#include <epan/camel-persistentdata.h>
+
+#include "../stat_menu.h"
+#include "../timestats.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/main.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/gui_stat_util.h"
+#include "ui/gtk/tap_param_dlg.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+static void gtk_camelcounter_reset(void *phs);
+static int gtk_camelcounter_packet(void *phs,
+ packet_info *pinfo _U_,
+ epan_dissect_t *edt _U_,
+ const void *phi);
+static void gtk_camelcounter_draw(void *phs);
+static void win_destroy_cb(GtkWindow *win _U_, gpointer data);
+static void gtk_camelcounter_init(const char *optarg, void *userdata _U_);
+void register_tap_listener_gtk_camelcounter(void);
+
+/* following values represent the size of their valuestring arrays */
+
+struct camelcounter_t {
+ GtkWidget *win;
+ GtkWidget *vbox;
+ char *filter;
+ GtkWidget *scrolled_window;
+ GtkTreeView *table;
+ guint32 camel_msg[camel_MAX_NUM_OPR_CODES];
+};
+
+static void gtk_camelcounter_reset(void *phs)
+{
+ struct camelcounter_t * p_counter= ( struct camelcounter_t *) phs;
+ int i;
+
+ /* Erase Message Type count */
+ for(i=0;i<camel_MAX_NUM_OPR_CODES;i++) {
+ p_counter->camel_msg[i]=0;
+ }
+}
+
+
+/*
+ * If there is a valid camel operation, increase the value in the array of counter
+ */
+static int gtk_camelcounter_packet(void *phs,
+ packet_info *pinfo _U_,
+ epan_dissect_t *edt _U_,
+ const void *phi)
+{
+ struct camelcounter_t * p_counter =(struct camelcounter_t *)phs;
+ const struct camelsrt_info_t * pi=phi;
+ if (pi->opcode != 255)
+ p_counter->camel_msg[pi->opcode]++;
+
+ return 1;
+}
+
+static void gtk_camelcounter_draw(void *phs)
+{
+ struct camelcounter_t *p_counter=(struct camelcounter_t *)phs;
+ int i;
+ char str[256];
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ /* Now print Message and Reason Counter Table */
+ /* clear list before printing */
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(p_counter->table));
+ gtk_list_store_clear(store);
+
+ for(i=0;i<camel_MAX_NUM_OPR_CODES;i++) {
+ /* Message counter */
+ if(p_counter->camel_msg[i]!=0) {
+ g_snprintf(str, 256, "Request %s", val_to_str(i,camel_opr_code_strings,"Unknown message "));
+
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, str,
+ 1, p_counter->camel_msg[i],
+ -1);
+ }
+ } /* Message Type */
+}
+
+static void win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ struct camelcounter_t *hs=(struct camelcounter_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(hs);
+ unprotect_thread_critical_region();
+
+ if(hs->filter){
+ g_free(hs->filter);
+ hs->filter=NULL;
+ }
+ g_free(hs);
+}
+
+static const stat_column titles[]={
+ {G_TYPE_STRING, LEFT, "Message Type or Reason"},
+ {G_TYPE_UINT, RIGHT, "Count" }
+};
+
+static void gtk_camelcounter_init(const char *optarg, void *userdata _U_)
+{
+ struct camelcounter_t *p_camelcounter;
+ const char *filter=NULL;
+ GString *error_string;
+ GtkWidget *bbox;
+ GtkWidget *close_bt;
+
+ if(strncmp(optarg,"camel,counter,",14) == 0){
+ filter=optarg+14;
+ } else {
+ filter=NULL;
+ }
+
+ p_camelcounter=g_malloc(sizeof(struct camelcounter_t));
+ p_camelcounter->filter=g_strdup(filter);
+
+ gtk_camelcounter_reset(p_camelcounter);
+
+ /* transient_for top_level */
+ p_camelcounter->win=dlg_window_new("Wireshark: CAMEL counters");
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(p_camelcounter->win), TRUE);
+
+ gtk_window_set_default_size(GTK_WINDOW(p_camelcounter->win), 500, 300);
+
+ p_camelcounter->vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(p_camelcounter->vbox), 12);
+
+ init_main_stat_window(p_camelcounter->win, p_camelcounter->vbox, "CAMEL Messages Counters", filter);
+
+ /* init a scrolled window*/
+ p_camelcounter->scrolled_window = scrolled_window_new(NULL, NULL);
+
+ p_camelcounter->table = create_stat_table(p_camelcounter->scrolled_window, p_camelcounter->vbox, 2, titles);
+
+ error_string=register_tap_listener("CAMEL", p_camelcounter, filter, 0,
+ gtk_camelcounter_reset,
+ gtk_camelcounter_packet,
+ gtk_camelcounter_draw);
+
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(p_camelcounter);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_end(GTK_BOX(p_camelcounter->vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(p_camelcounter->win, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(p_camelcounter->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(p_camelcounter->win, "destroy", G_CALLBACK(win_destroy_cb), p_camelcounter);
+
+ gtk_widget_show_all(p_camelcounter->win);
+ window_present(p_camelcounter->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(p_camelcounter->win));
+}
+
+static tap_param camel_counter_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg camel_counter_dlg = {
+ "CAMEL Messages and Response Status",
+ "camel,counter",
+ gtk_camelcounter_init,
+ -1,
+ G_N_ELEMENTS(camel_counter_params),
+ camel_counter_params
+};
+
+void /* Next line mandatory */
+register_tap_listener_gtk_camelcounter(void)
+{
+ register_dfilter_stat(&camel_counter_dlg, "_GSM/CAMEL",
+ REGISTER_STAT_GROUP_TELEPHONY);
+
+}
+
+void camel_counter_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &camel_counter_dlg);
+}
+
diff --git a/ui/gtk/camel_srt.c b/ui/gtk/camel_srt.c
new file mode 100644
index 0000000000..6b0546e728
--- /dev/null
+++ b/ui/gtk/camel_srt.c
@@ -0,0 +1,258 @@
+/* camel_srt.c
+ * camel Service Response Time statistics for Wireshark
+ * Copyright 2006 Florent Drouin (based on h225_ras_srt.c from Lars Roland)
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <string.h>
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/value_string.h>
+#include <epan/tap.h>
+#include <epan/packet.h>
+#include <epan/asn1.h>
+#include <epan/camel-persistentdata.h>
+
+#include "../timestats.h"
+#include "../simple_dialog.h"
+#include "../file.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/main.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/gui_stat_util.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/service_response_time_table.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+/* used to keep track of the statistics for an entire program interface */
+struct camelsrt_t {
+ GtkWidget *win;
+ srt_stat_table camel_srt_table;
+};
+
+static void camelsrt_set_title(struct camelsrt_t * p_camelsrt);
+static void camelsrt_reset(void *phs);
+static int camelsrt_packet(void *phs,
+ packet_info *pinfo _U_,
+ epan_dissect_t *edt _U_,
+ const void *phi);
+
+static void camelsrt_draw(void *phs);
+static void win_destroy_cb(GtkWindow *win _U_, gpointer data);
+static void gtk_camelsrt_init(const char *optarg, void *userdata _U_);
+void register_tap_listener_gtk_camelsrt(void);
+
+/*
+ *
+ */
+static void camelsrt_set_title(struct camelsrt_t * p_camelsrt)
+{
+ char * title;
+ title = g_strdup_printf("CAMEL Service Response Time statistics: %s",
+ cf_get_display_name(&cfile));
+ gtk_window_set_title(GTK_WINDOW(p_camelsrt->win), title);
+ g_free(title);
+}
+
+static void camelsrt_reset(void *phs)
+{
+ struct camelsrt_t *hs=(struct camelsrt_t *)phs;
+ reset_srt_table_data(&hs->camel_srt_table);
+ camelsrt_set_title(hs);
+}
+
+/*
+ * Count the delta time between Request and Response
+ * As we can make several measurement per message, we use a boolean array for the category
+ * Then, if the measurement is provided, check if it is valid, and update the table
+ */
+static int camelsrt_packet(void *phs,
+ packet_info *pinfo _U_,
+ epan_dissect_t *edt _U_,
+ const void *phi)
+{
+ struct camelsrt_t *hs=(struct camelsrt_t *)phs;
+ const struct camelsrt_info_t * pi=phi;
+ int i;
+
+ for (i=1; i<NB_CAMELSRT_CATEGORY; i++) {
+ if ( pi->bool_msginfo[i] &&
+ pi->msginfo[i].is_delta_time
+ && pi->msginfo[i].request_available
+ && !pi->msginfo[i].is_duplicate ) {
+
+ add_srt_table_data(&hs->camel_srt_table, i, &pi->msginfo[i].req_time, pinfo);
+
+ }
+ } /* category */
+ return 1;
+}
+
+
+static void camelsrt_draw(void *phs)
+{
+ struct camelsrt_t *hs=(struct camelsrt_t *)phs;
+ draw_srt_table_data(&hs->camel_srt_table);
+}
+
+/*
+ * Routine for Display
+ */
+static void win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ struct camelsrt_t *hs=(struct camelsrt_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(hs);
+ unprotect_thread_critical_region();
+
+ free_srt_table_data(&hs->camel_srt_table);
+ g_free(hs);
+}
+
+static void gtk_camelsrt_init(const char *optarg, void *userdata _U_)
+{
+ struct camelsrt_t * p_camelsrt;
+ const char *filter=NULL;
+
+ GtkWidget *cmd_label;
+ GtkWidget *main_label;
+ GtkWidget *filter_label;
+ char *filter_string;
+ GString *error_string;
+ GtkWidget *vbox;
+ GtkWidget *bbox;
+ GtkWidget *close_bt;
+ int i;
+
+ if(strncmp(optarg,"camel,srt,",10) == 0){
+ filter=optarg+10;
+ } else {
+ filter=NULL;
+ }
+
+ p_camelsrt=g_malloc(sizeof(struct camelsrt_t));
+
+ p_camelsrt->win= dlg_window_new("camel-srt"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(p_camelsrt->win), TRUE);
+
+ gtk_window_set_default_size(GTK_WINDOW(p_camelsrt->win), 550, 400);
+ camelsrt_set_title(p_camelsrt);
+
+ vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(p_camelsrt->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ main_label=gtk_label_new("CAMEL Service Response Time statistics");
+ gtk_box_pack_start(GTK_BOX(vbox), main_label, FALSE, FALSE, 0);
+ gtk_widget_show(main_label);
+
+ filter_string = g_strdup_printf("Filter: %s",filter ? filter : "");
+ filter_label=gtk_label_new(filter_string);
+ g_free(filter_string);
+ gtk_label_set_line_wrap(GTK_LABEL(filter_label), TRUE);
+ gtk_box_pack_start(GTK_BOX(vbox), filter_label, FALSE, FALSE, 0);
+ gtk_widget_show(filter_label);
+
+ cmd_label=gtk_label_new("CAMEL Commands");
+ gtk_box_pack_start(GTK_BOX(vbox), cmd_label, FALSE, FALSE, 0);
+ gtk_widget_show(cmd_label);
+
+ /* We must display TOP LEVEL Widget before calling init_srt_table() */
+ gtk_widget_show_all(p_camelsrt->win);
+
+ init_srt_table(&p_camelsrt->camel_srt_table, NB_CAMELSRT_CATEGORY, vbox, NULL);
+ for(i=0 ;i<NB_CAMELSRT_CATEGORY; i++) {
+ init_srt_table_row(&p_camelsrt->camel_srt_table, i,
+ val_to_str(i,camelSRTtype_naming,"Unknown"));
+ }
+
+ error_string=register_tap_listener("CAMEL",
+ p_camelsrt,
+ filter,
+ 0,
+ camelsrt_reset,
+ camelsrt_packet,
+ camelsrt_draw);
+
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(p_camelsrt);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(p_camelsrt->win, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(p_camelsrt->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(p_camelsrt->win, "destroy", G_CALLBACK(win_destroy_cb), p_camelsrt);
+
+ gtk_widget_show_all(p_camelsrt->win);
+ window_present(p_camelsrt->win);
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(p_camelsrt->win));
+
+}
+
+static tap_param camel_srt_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg camel_srt_dlg = {
+ "CAMEL Service Response Time",
+ "camel,srt",
+ gtk_camelsrt_init,
+ -1,
+ G_N_ELEMENTS(camel_srt_params),
+ camel_srt_params
+};
+
+void /* Next line mandatory */
+register_tap_listener_gtk_camelsrt(void)
+{
+ register_dfilter_stat(&camel_srt_dlg, "CAMEL",
+ REGISTER_STAT_GROUP_RESPONSE_TIME);
+}
+
+void camel_srt_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &camel_srt_dlg);
+}
+
diff --git a/ui/gtk/capture_dlg.c b/ui/gtk/capture_dlg.c
new file mode 100644
index 0000000000..9e3b1dd174
--- /dev/null
+++ b/ui/gtk/capture_dlg.c
@@ -0,0 +1,4363 @@
+/* capture_dlg.c
+ * Routines for packet capture windows
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_LIBPCAP
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <string.h>
+
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/addr_resolv.h>
+#include <epan/prefs.h>
+#include <epan/filesystem.h>
+
+#include "../capture.h"
+#include "../capture_ifinfo.h"
+#include "../simple_dialog.h"
+#include "../capture-pcap-util.h"
+#include "../capture_ui_utils.h"
+#include "../ringbuffer.h"
+
+#include "ui/gtk/main.h"
+#include "ui/gtk/menus.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/capture_dlg.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/file_dlg.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/capture_file_dlg.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/capture_globals.h"
+#include "ui/gtk/cfilter_combo_utils.h"
+#include "ui/gtk/capture_if_dlg.h"
+#include "ui/gtk/main_welcome.h"
+#include "ui/gtk/network_icons.h"
+
+#include "ui/gtk/keys.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+#ifdef HAVE_AIRPCAP
+#include <airpcap.h>
+#include "airpcap_loader.h"
+#include "airpcap_gui_utils.h"
+#include "airpcap_dlg.h"
+#endif
+
+/* Capture callback data keys */
+#define E_CAP_IFACE_KEY "cap_iface"
+#define E_CAP_IFACE_IP_KEY "cap_iface_ip"
+#define E_CAP_SNAP_CB_KEY "cap_snap_cb"
+#define E_CAP_LT_CBX_KEY "cap_lt_cbx"
+#define E_CAP_LT_CBX_LABEL_KEY "cap_lt_cbx_label"
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+#define E_CAP_BUFFER_SIZE_SB_KEY "cap_buffer_size_sb"
+#endif
+#define E_CAP_SNAP_SB_KEY "cap_snap_sb"
+#define E_CAP_PROMISC_KEY "cap_promisc"
+#define E_CAP_PROMISC_KEY_ALL "cap_promisc_all"
+#ifdef HAVE_PCAP_CREATE
+#define E_CAP_MONITOR_KEY "cap_monitor"
+#endif
+#define E_CAP_PCAP_NG_KEY "cap_pcap_ng"
+#define E_CAP_FILT_KEY "cap_filter_te"
+#define E_OPT_EDIT_DIALOG_PTR_KEY "cap_edit_opt_dialog"
+#define E_OPT_EDIT_CALLER_PTR_KEY "cap_edit_opt_caller"
+#define E_CAP_FILE_TE_KEY "cap_file_te"
+#define E_CAP_MULTI_FILES_ON_CB_KEY "cap_multi_files_on_cb"
+#define E_CAP_RING_FILESIZE_CB_KEY "cap_ring_filesize_cb"
+#define E_CAP_RING_FILESIZE_SB_KEY "cap_ring_filesize_sb"
+#define E_CAP_RING_FILESIZE_CBX_KEY "cap_ring_filesize_cbx"
+#define E_CAP_FILE_DURATION_CB_KEY "cap_file_duration_cb"
+#define E_CAP_FILE_DURATION_SB_KEY "cap_file_duration_sb"
+#define E_CAP_FILE_DURATION_CBX_KEY "cap_file_duration_cbx"
+#define E_CAP_RING_NBF_CB_KEY "cap_ring_nbf_cb"
+#define E_CAP_RING_NBF_SB_KEY "cap_ring_nbf_sb"
+#define E_CAP_RING_NBF_LB_KEY "cap_ring_nbf_lb"
+#define E_CAP_STOP_FILES_CB_KEY "cap_stop_files_cb"
+#define E_CAP_STOP_FILES_SB_KEY "cap_stop_files_sb"
+#define E_CAP_STOP_FILES_LB_KEY "cap_stop_files_lb"
+#define E_CAP_SYNC_KEY "cap_sync"
+#define E_CAP_AUTO_SCROLL_KEY "cap_auto_scroll"
+#define E_CAP_HIDE_INFO_KEY "cap_hide_info"
+#define E_CAP_STOP_PACKETS_CB_KEY "cap_stop_packets_cb"
+#define E_CAP_STOP_PACKETS_SB_KEY "cap_stop_packets_sb"
+#define E_CAP_STOP_PACKETS_LB_KEY "cap_stop_packets_lb"
+#define E_CAP_STOP_FILESIZE_CB_KEY "cap_stop_filesize_cb"
+#define E_CAP_STOP_FILESIZE_SB_KEY "cap_stop_filesize_sb"
+#define E_CAP_STOP_FILESIZE_CBX_KEY "cap_stop_filesize_cbx"
+#define E_CAP_STOP_DURATION_CB_KEY "cap_stop_duration_cb"
+#define E_CAP_STOP_DURATION_SB_KEY "cap_stop_duration_sb"
+#define E_CAP_STOP_DURATION_CBX_KEY "cap_stop_duration_cbx"
+#define E_CAP_M_RESOLVE_KEY "cap_m_resolve"
+#define E_CAP_N_RESOLVE_KEY "cap_n_resolve"
+#define E_CAP_T_RESOLVE_KEY "cap_t_resolve"
+
+#ifdef HAVE_PCAP_REMOTE
+#define E_CAP_IFTYPE_CBX_KEY "cap_iftype_cbx"
+#define E_CAP_IF_LIST_KEY "cap_if_list"
+#define E_CAP_DATATX_UDP_CB_KEY "cap_datatx_udp_cb"
+#define E_CAP_NOCAP_RPCAP_CB_KEY "cap_nocap_rpcap_cb"
+#define E_CAP_REMOTE_DIALOG_PTR_KEY "cap_remote_dialog"
+#define E_CAP_REMOTE_CALLER_PTR_KEY "cap_remote_caller"
+#define E_REMOTE_HOST_TE_KEY "cap_remote_host"
+#define E_REMOTE_PORT_TE_KEY "cap_remote_port"
+#define E_REMOTE_AUTH_NULL_KEY "cap_remote_auth_null"
+#define E_REMOTE_AUTH_PASSWD_KEY "cap_remote_auth_passwd"
+#define E_REMOTE_USERNAME_LB_KEY "cap_remote_username_lb"
+#define E_REMOTE_USERNAME_TE_KEY "cap_remote_username_te"
+#define E_REMOTE_PASSWD_LB_KEY "cap_remote_passwd_lb"
+#define E_REMOTE_PASSWD_TE_KEY "cap_remote_passwd_te"
+#define E_CAP_CBX_IFTYPE_VALUE_KEY "cap_cbx_iftype_value"
+#define E_CAP_CBX_PREV_IFTYPE_VALUE_KEY "cap_cbx_prev_iftype_value"
+#define E_CAP_CBX_IFTYPE_NOUPDATE_KEY "cap_cbx_iftype_noupdate"
+#define E_OPT_REMOTE_BT_KEY "cap_remote_opt_bt"
+#define E_OPT_REMOTE_DIALOG_PTR_KEY "cap_remote_opt_dialog"
+#define E_OPT_REMOTE_CALLER_PTR_KEY "cap_remote_opt_caller"
+#endif
+#ifdef HAVE_PCAP_SETSAMPLING
+#define E_CAP_SAMP_NONE_RB_KEY "cap_samp_none_rb"
+#define E_CAP_SAMP_COUNT_RB_KEY "cap_samp_count_rb"
+#define E_CAP_SAMP_COUNT_SB_KEY "cap_samp_count_sb"
+#define E_CAP_SAMP_TIMER_RB_KEY "cap_samp_timer_rb"
+#define E_CAP_SAMP_TIMER_SB_KEY "cap_samp_timer_sb"
+#endif
+
+#define DUMMY_SNAPLENGTH 65535
+#define DUMMY_NETMASK 0xFF000000
+
+enum
+{
+ COL_NAME = 0,
+ COL_ADDRESS,
+ COL_LINK
+} ;
+
+/*
+ * Keep a static pointer to the current "Capture Options" window, if
+ * any, so that if somebody tries to do "Capture:Options" while there's
+ * already a "Capture Options" window up, we just pop up the existing
+ * one, rather than creating a new one.
+ * Also: Capture:Start obtains info from the "Capture Options" window
+ * if it exists and if its creation is complete.
+ */
+static GtkWidget *cap_open_w = NULL, *opt_edit_w = NULL, *ok_bt;
+static gboolean cap_open_complete; /* valid only if cap_open_w != NULL */
+
+static GHashTable *cap_settings_history=NULL;
+static gint16 num_selected;
+static GArray *rows = NULL;
+static gint marked_row;
+
+#ifdef HAVE_PCAP_REMOTE
+static GHashTable *remote_host_list=NULL;
+static remote_options global_remote_opts;
+#endif
+
+static void
+capture_prep_file_cb(GtkWidget *file_bt, GtkWidget *file_te);
+
+static void
+select_link_type_cb(GtkWidget *w, gpointer data);
+
+#ifdef HAVE_PCAP_REMOTE
+static void
+capture_remote_cb(GtkWidget *w, gboolean focus_username);
+#endif
+
+static void
+capture_prep_adjust_sensitivity(GtkWidget *tb, gpointer parent_w);
+
+static void
+capture_prep_destroy_cb(GtkWidget *win, gpointer user_data);
+
+#ifdef HAVE_PCAP_CREATE
+static void
+capture_prep_monitor_changed_cb(GtkWidget *monitor, gpointer argp);
+#endif
+
+static gboolean
+capture_dlg_prep(gpointer parent_w);
+
+extern gint if_list_comparator_alph (const void *first_arg, const void *second_arg);
+
+static void
+make_and_fill_rows(void);
+
+/* stop the currently running capture */
+void
+capture_stop_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+#ifdef HAVE_AIRPCAP
+ airpcap_set_toolbar_stop_capture(airpcap_if_active);
+#endif
+
+ capture_stop(&global_capture_opts);
+}
+
+/* restart (stop - delete old file - start) running capture */
+void
+capture_restart_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+#ifdef HAVE_AIRPCAP
+ airpcap_set_toolbar_start_capture(airpcap_if_active);
+#endif
+
+ capture_restart(&global_capture_opts);
+}
+
+cap_settings_t
+capture_get_cap_settings (gchar *if_name)
+{
+ cap_settings_t cap_settings, *cap_settings_p;
+
+ if (cap_settings_history) {
+ cap_settings_p = (cap_settings_t *)g_hash_table_lookup(cap_settings_history, if_name);
+ } else {
+ cap_settings_p = NULL;
+ }
+
+ if (cap_settings_p) {
+ cap_settings = *cap_settings_p;
+ } else {
+ cap_settings.monitor_mode = prefs_capture_device_monitor_mode(if_name);
+ cap_settings.linktype = capture_dev_user_linktype_find(if_name);
+ }
+
+ return cap_settings;
+}
+
+enum cfc_state_t {
+ CFC_PENDING,
+ CFC_UNKNOWN,
+ CFC_VALID,
+ CFC_INVALID
+};
+
+typedef struct capture_filter_check {
+ enum cfc_state_t state;
+ gchar *filter_text;
+ GtkWidget *filter_te;
+ int dlt;
+} capture_filter_check_t;
+
+/* Valid states:
+ *
+ * Idle: filter_text = NULL, state = ?
+ * Pending: filter_text != NULL, state = CFC_PENDING
+ * Unknown: filter_text != NULL, state = CFC_UNKNOWN
+ * Known: filter_text != NULL, state = CFC_VALID || CFC_INVALID
+ *
+ * We assume that only one text entry is active at a time.
+ */
+
+/* We could make this smarter by caching results */
+capture_filter_check_t cfc_data;
+
+static GMutex *pcap_compile_mtx;
+static GCond *cfc_data_cond;
+static GMutex *cfc_data_mtx;
+
+#if 0
+#define DEBUG_SYNTAX_CHECK(state1, state2) g_warning("CF state %s -> %s : %s", state1, state2, cfc_data.filter_text)
+#else
+#define DEBUG_SYNTAX_CHECK(state1, state2)
+#endif
+
+static void *
+check_capture_filter_syntax(void *data _U_) {
+ struct bpf_program fcode;
+ int pc_err;
+
+ while (1) {
+ g_mutex_lock(cfc_data_mtx);
+ while (!cfc_data.filter_text || cfc_data.state != CFC_PENDING) {
+ /* Do we really need to use a mutex here? We only have one thread... */
+ g_cond_wait(cfc_data_cond, cfc_data_mtx);
+ }
+ cfc_data.state = CFC_UNKNOWN;
+ DEBUG_SYNTAX_CHECK("pending", "unknown");
+
+ g_mutex_unlock(cfc_data_mtx);
+ g_mutex_lock(pcap_compile_mtx);
+
+ /* pcap_compile_nopcap will not alter the filter string, so the (char *) cast is "safe" */
+ pc_err = pcap_compile_nopcap(DUMMY_SNAPLENGTH /* use a dummy snaplength for syntax-checking */,
+ cfc_data.dlt, &fcode, cfc_data.filter_text, 1 /* Do optimize */,
+ DUMMY_NETMASK /* use a dummy netmask for syntax-checking */);
+
+ g_mutex_unlock(pcap_compile_mtx);
+ g_mutex_lock(cfc_data_mtx);
+
+ if (cfc_data.state == CFC_UNKNOWN) { /* No more input came in */
+ if (pc_err) {
+ DEBUG_SYNTAX_CHECK("unknown", "known bad");
+ cfc_data.state = CFC_INVALID;
+ } else {
+ DEBUG_SYNTAX_CHECK("unknown", "known good");
+ cfc_data.state = CFC_VALID;
+ }
+ }
+ g_mutex_unlock(cfc_data_mtx);
+ }
+ return NULL;
+}
+
+static gboolean
+update_capture_filter_te(gpointer data _U_) {
+
+ g_mutex_lock(cfc_data_mtx);
+
+ if (cfc_data.filter_text && cfc_data.filter_te) {
+ if (cfc_data.state == CFC_VALID) {
+ colorize_filter_te_as_valid(cfc_data.filter_te);
+ } else if (cfc_data.state == CFC_INVALID) {
+ colorize_filter_te_as_invalid(cfc_data.filter_te);
+ } else {
+ colorize_filter_te_as_empty(cfc_data.filter_te);
+ }
+
+ if (cfc_data.state == CFC_VALID || cfc_data.state == CFC_INVALID) {
+ DEBUG_SYNTAX_CHECK("known", "idle");
+ /* Reset the current state to idle. */
+ if (cfc_data.filter_text != NULL) {
+ g_free(cfc_data.filter_text);
+ }
+ cfc_data.filter_text = NULL;
+ cfc_data.state = CFC_PENDING;
+ }
+ }
+ g_mutex_unlock(cfc_data_mtx);
+ return TRUE;
+}
+
+/** Initialize background capture filter syntax checking
+ */
+void capture_filter_init(void) {
+ cfc_data.filter_text = NULL;
+ cfc_data.filter_te = NULL;
+ cfc_data.state = CFC_PENDING;
+
+#if GLIB_CHECK_VERSION(2,31,0)
+ pcap_compile_mtx = g_malloc(sizeof(GMutex));
+ g_mutex_init(pcap_compile_mtx);
+ cfc_data_cond = g_malloc(sizeof(GCond));
+ g_cond_init(cfc_data_cond);
+ cfc_data_mtx = g_malloc(sizeof(GMutex));
+ g_mutex_init(cfc_data_mtx);
+ g_thread_new("Capture filter syntax", check_capture_filter_syntax, NULL);
+#else
+ pcap_compile_mtx = g_mutex_new();
+ cfc_data_cond = g_cond_new();
+ cfc_data_mtx = g_mutex_new();
+ g_thread_create(check_capture_filter_syntax, NULL, FALSE, NULL);
+#endif
+
+ g_timeout_add(200, update_capture_filter_te, NULL);
+}
+
+static void
+capture_filter_check_syntax_cb(GtkWidget *w _U_, gpointer user_data _U_)
+{
+ GtkWidget *filter_cm, *filter_te, *linktype_combo_box;
+ gchar *filter_text;
+ gpointer dlt_ptr;
+
+ linktype_combo_box = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CAP_LT_CBX_KEY);
+
+ if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(linktype_combo_box), &dlt_ptr)) {
+ g_assert_not_reached(); /* Programming error: somehow nothing is active */
+ }
+ if ((cfc_data.dlt = GPOINTER_TO_INT(dlt_ptr)) == -1) {
+ g_assert_not_reached(); /* Programming error: somehow managed to select an "unsupported" entry */
+ }
+
+ filter_cm = (GtkWidget *)g_object_get_data(G_OBJECT(opt_edit_w), E_CFILTER_CM_KEY);
+ if (!filter_cm)
+ return;
+ filter_te = gtk_bin_get_child(GTK_BIN(filter_cm));
+ if (!filter_te)
+ return;
+
+ filter_text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(filter_cm));
+
+ if (strlen(filter_text) == 0) {
+ colorize_filter_te_as_empty(filter_te);
+ return;
+ }
+
+ g_mutex_lock(cfc_data_mtx);
+ /* Ruthlessly clobber the current state. */
+ if (cfc_data.filter_text != NULL) {
+ g_free(cfc_data.filter_text);
+ }
+ cfc_data.filter_text = filter_text;
+ cfc_data.filter_te = filter_te;
+ cfc_data.state = CFC_PENDING;
+ DEBUG_SYNTAX_CHECK("?", "pending");
+ g_cond_signal(cfc_data_cond);
+ g_mutex_unlock(cfc_data_mtx);
+}
+
+static void
+capture_filter_destroy_cb(GtkWidget *w _U_, gpointer user_data _U_)
+{
+ g_mutex_lock(cfc_data_mtx);
+ /* Reset the current state to idle. */
+ if (cfc_data.filter_text != NULL) {
+ g_free(cfc_data.filter_text);
+ }
+ cfc_data.filter_text = NULL;
+ cfc_data.filter_te = NULL;
+ cfc_data.state = CFC_PENDING;
+ g_mutex_unlock(cfc_data_mtx);
+}
+
+#define TIME_UNIT_SECOND 0
+#define TIME_UNIT_MINUTE 1
+#define TIME_UNIT_HOUR 2
+#define TIME_UNIT_DAY 3
+#define MAX_TIME_UNITS 4
+static const char *time_unit_name[MAX_TIME_UNITS] = {
+ "second(s)",
+ "minute(s)",
+ "hour(s)",
+ "day(s)",
+};
+
+/* create one of the duration options */
+/* (and select the matching unit depending on the given value) */
+static GtkWidget *time_unit_combo_box_new(guint32 value) {
+ GtkWidget *unit_combo_box;
+ int i;
+ unit_combo_box = gtk_combo_box_text_new ();
+ for(i = 0; i < MAX_TIME_UNITS; i++) {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (unit_combo_box), time_unit_name[i]);
+ }
+ /* the selected combo_box item can't be changed, once the combo_box
+ is created, so set the matching combo_box item now */
+ /* days */
+ if(value >= 60 * 60 * 24) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(unit_combo_box), TIME_UNIT_DAY);
+ } else {
+ /* hours */
+ if(value >= 60 * 60) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(unit_combo_box), TIME_UNIT_HOUR);
+ } else {
+ /* minutes */
+ if(value >= 60) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(unit_combo_box), TIME_UNIT_MINUTE);
+ } else {
+ /* seconds */
+ gtk_combo_box_set_active(GTK_COMBO_BOX(unit_combo_box), TIME_UNIT_SECOND);
+ }
+ }
+ }
+ return unit_combo_box;
+}
+
+/* convert time value from raw to displayed (e.g. 60s -> 1min) */
+static guint32 time_unit_combo_box_convert_value(
+guint32 value)
+{
+ /* days */
+ if(value >= 60 * 60 * 24) {
+ return value / (60 * 60 * 24);
+ }
+
+ /* hours */
+ if(value >= 60 * 60) {
+ return value / (60 * 60);
+ }
+
+ /* minutes */
+ if(value >= 60) {
+ return value / 60;
+ }
+
+ /* seconds */
+ return value;
+}
+
+/* get raw value from unit and value fields */
+static guint32 time_unit_combo_box_get_value(
+GtkWidget *unit_combo_box,
+guint32 value)
+{
+ int unit;
+
+ unit = gtk_combo_box_get_active (GTK_COMBO_BOX(unit_combo_box));
+
+ switch(unit) {
+ case(TIME_UNIT_SECOND):
+ return value;
+ case(TIME_UNIT_MINUTE):
+ return value * 60;
+ case(TIME_UNIT_HOUR):
+ return value * 60 * 60;
+ case(TIME_UNIT_DAY):
+ return value * 60 * 60 * 24;
+ default:
+ g_assert_not_reached();
+ return 0;
+ }
+}
+
+
+#define SIZE_UNIT_KILOBYTES 0
+#define SIZE_UNIT_MEGABYTES 1
+#define SIZE_UNIT_GIGABYTES 2
+#define MAX_SIZE_UNITS 3
+static const char *size_unit_name[MAX_SIZE_UNITS] = {
+ "kilobyte(s)",
+ "megabyte(s)",
+ "gigabyte(s)",
+};
+
+/* create one of the size options */
+/* (and select the matching unit depending on the given value) */
+static GtkWidget *size_unit_combo_box_new(guint32 value) {
+ GtkWidget *unit_combo_box;
+ int i;
+ unit_combo_box=gtk_combo_box_text_new();
+ for(i=0;i<MAX_SIZE_UNITS;i++){
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (unit_combo_box), size_unit_name[i]);
+ }
+ /* the selected combo_box item can't be changed, once the combo_box
+ is created, so set the matching combo_box item now */
+ /* gigabytes */
+ if(value >= 1024 * 1024) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(unit_combo_box), SIZE_UNIT_GIGABYTES);
+ } else {
+ /* megabytes */
+ if(value >= 1024) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(unit_combo_box), SIZE_UNIT_MEGABYTES);
+ } else {
+ /* kilobytes */
+ gtk_combo_box_set_active(GTK_COMBO_BOX(unit_combo_box), SIZE_UNIT_KILOBYTES);
+ }
+ }
+ return unit_combo_box;
+}
+
+/* convert size value from raw to displayed (e.g. 1024 Bytes -> 1 KB) */
+static guint32 size_unit_combo_box_set_value(
+guint32 value)
+{
+ /* gigabytes */
+ if(value >= 1024 * 1024) {
+ return value / (1024 * 1024);
+ }
+
+ /* megabytes */
+ if(value >= 1024) {
+ return value / (1024);
+ }
+
+ /* kilobytes */
+ return value;
+}
+
+/* get raw value from unit and value fields */
+static guint32 size_unit_combo_box_convert_value(
+GtkWidget *unit_combo_box,
+guint32 value)
+{
+ int unit;
+
+ unit = gtk_combo_box_get_active (GTK_COMBO_BOX(unit_combo_box));
+
+ switch(unit) {
+ case(SIZE_UNIT_KILOBYTES):
+ return value;
+ case(SIZE_UNIT_MEGABYTES):
+ if(value > G_MAXINT / 1024) {
+ return 0;
+ } else {
+ return value * 1024;
+ }
+ case(SIZE_UNIT_GIGABYTES):
+ if(value > G_MAXINT / (1024 * 1024)) {
+ return 0;
+ } else {
+ return value * 1024 * 1024;
+ }
+ default:
+ g_assert_not_reached();
+ return 0;
+ }
+}
+
+#ifdef HAVE_AIRPCAP
+/*
+ * Sets the toolbar before calling the advanced dialog with for the right interface
+ */
+static void
+options_airpcap_advanced_cb(GtkWidget *w, gpointer d)
+{
+ int *from_widget;
+
+ from_widget = (gint*)g_malloc(sizeof(gint));
+ *from_widget = AIRPCAP_ADVANCED_FROM_OPTIONS;
+ g_object_set_data(G_OBJECT(airpcap_tb),AIRPCAP_ADVANCED_FROM_KEY,from_widget);
+
+ airpcap_if_active = airpcap_if_selected;
+ airpcap_enable_toolbar_widgets(airpcap_tb,FALSE);
+ display_airpcap_advanced_cb(w,d);
+}
+#endif
+
+#ifdef HAVE_PCAP_REMOTE_NEVER
+/* PCAP interface type menu item */
+struct iftype_info {
+ capture_source id;
+ const char *name;
+};
+
+/* List of available types of PCAP interface */
+static struct iftype_info iftype[] = {
+ { CAPTURE_IFLOCAL, "Local" },
+ { CAPTURE_IFREMOTE, "Remote..." }
+};
+
+#define REMOTE_HOST_START ((sizeof(iftype) / sizeof(iftype[0])) + 1)
+#define REMOTE_HOST_SEPARATOR "---"
+
+static void
+iftype_combo_box_add_remote_separators (GtkWidget *iftype_cbx)
+{
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX(iftype_cbx), REMOTE_HOST_SEPARATOR);
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX(iftype_cbx), REMOTE_HOST_SEPARATOR);
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX(iftype_cbx), "Clear list");
+}
+
+static void
+iftype_combo_box_add (GtkWidget *iftype_cbx)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ struct remote_host *rh;
+ gboolean create_new = FALSE;
+ gchar *string;
+ guint i, pos = REMOTE_HOST_START;
+
+ rh = g_hash_table_lookup (remote_host_list, g_array_index(global_capture_opts.ifaces, interface_options, 0).remote_host);
+ if (!rh) {
+ rh = g_malloc0 (sizeof (*rh));
+ if (g_hash_table_size (remote_host_list) == 0) {
+ iftype_combo_box_add_remote_separators (iftype_cbx);
+ }
+ gtk_combo_box_text_insert_text(GTK_COMBO_BOX(iftype_cbx), pos, g_array_index(global_capture_opts.ifaces, interface_options, 0).remote_host);
+ rh->remote_host = g_strdup (g_array_index(global_capture_opts.ifaces, interface_options, 0).remote_host);
+ create_new = TRUE;
+ } else {
+ model = gtk_combo_box_get_model(GTK_COMBO_BOX(iftype_cbx));
+ if (gtk_tree_model_get_iter_first(model, &iter)) {
+ /* Skip the first entries */
+ for (i = 0; i < REMOTE_HOST_START; i++)
+ gtk_tree_model_iter_next(model, &iter);
+ do {
+ gtk_tree_model_get(model, &iter, 0, &string, -1);
+ if (string) {
+ if (strcmp (g_array_index(global_capture_opts.ifaces, interface_options, 0).remote_host, string) == 0) {
+ /* Found match, show this position in combo box */
+ g_free (string);
+ break;
+ }
+ g_free (string);
+ }
+ pos++;
+ } while (gtk_tree_model_iter_next(model, &iter));
+ }
+
+ g_free (rh->remote_port);
+ g_free (rh->auth_username);
+ g_free (rh->auth_password);
+ }
+
+ rh->remote_port = g_strdup (g_array_index(global_capture_opts.ifaces, interface_options, 0).remote_port);
+ rh->auth_type = g_array_index(global_capture_opts.ifaces, interface_options, 0).auth_type;
+ rh->auth_username = g_strdup (g_array_index(global_capture_opts.ifaces, interface_options, 0).auth_username);
+ rh->auth_password = g_strdup (g_array_index(global_capture_opts.ifaces, interface_options, 0).auth_password);
+
+ if (create_new) {
+ g_hash_table_insert (remote_host_list, g_strdup (g_array_index(global_capture_opts.ifaces, interface_options, 0).remote_host), rh);
+ }
+
+ g_object_set_data(G_OBJECT(iftype_cbx), E_CAP_CBX_IFTYPE_VALUE_KEY, GINT_TO_POINTER(pos));
+ g_object_set_data(G_OBJECT(iftype_cbx), E_CAP_CBX_IFTYPE_NOUPDATE_KEY, GINT_TO_POINTER(1));
+ gtk_combo_box_set_active(GTK_COMBO_BOX(iftype_cbx), pos);
+ g_object_set_data(G_OBJECT(iftype_cbx), E_CAP_CBX_IFTYPE_NOUPDATE_KEY, GINT_TO_POINTER(0));
+}
+
+static void
+iftype_combo_box_add_remote_host (gpointer key, gpointer value _U_, gpointer user_data)
+{
+ gtk_combo_box_text_insert_text(GTK_COMBO_BOX(user_data), REMOTE_HOST_START, key);
+
+ if (g_array_index(global_capture_opts.ifaces, interface_options, 0).src_type == CAPTURE_IFREMOTE) {
+ /* Ensure we select the correct entry */
+ if (strcmp ((char *)key, g_array_index(global_capture_opts.ifaces, interface_options, 0).remote_host) == 0) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(user_data), REMOTE_HOST_START);
+ }
+ }
+}
+
+/* Fill the menu of available types of interfaces */
+static GtkWidget *
+iftype_combo_box_new(void)
+{
+ GtkWidget *iftype_cbx;
+ unsigned int i;
+
+ iftype_cbx = gtk_combo_box_text_new();
+
+ for (i = 0; i < sizeof(iftype) / sizeof(iftype[0]); i++) {
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX(iftype_cbx), iftype[i].name);
+ }
+
+ if (g_hash_table_size (remote_host_list) > 0) {
+ /* Add remote hosts */
+ iftype_combo_box_add_remote_separators (iftype_cbx);
+ g_hash_table_foreach (remote_host_list, iftype_combo_box_add_remote_host, iftype_cbx);
+ }
+
+ if (g_array_index(global_capture_opts.ifaces, interface_options, 0).src_type == CAPTURE_IFLOCAL) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(iftype_cbx), CAPTURE_IFLOCAL);
+ } else {
+ int iftype = gtk_combo_box_get_active(GTK_COMBO_BOX(iftype_cbx));
+ g_object_set_data(G_OBJECT(iftype_cbx), E_CAP_CBX_IFTYPE_VALUE_KEY, GINT_TO_POINTER(iftype));
+ }
+ g_signal_connect(iftype_cbx, "changed", G_CALLBACK(select_if_type_cb), NULL);
+
+ return iftype_cbx;
+}
+
+static gboolean
+iftype_combo_is_separator (GtkTreeModel *model, GtkTreeIter *iter, gpointer data _U_)
+{
+ gboolean result = FALSE;
+ gchar *string;
+
+ gtk_tree_model_get(model, iter, 0, &string, -1);
+ if (string) {
+ result = !strcmp (string, REMOTE_HOST_SEPARATOR);
+ g_free (string);
+ }
+
+ return result;
+
+}
+#endif
+
+#ifdef HAVE_PCAP_REMOTE
+static void
+error_list_remote_interface_cb (gpointer dialog _U_, gint btn _U_, gpointer data)
+{
+ capture_remote_cb(GTK_WIDGET(data), FALSE);
+}
+
+static void
+insert_new_rows(GList *list)
+{
+ interface_row row;
+ GtkTreeIter iter;
+ GList *if_entry;
+ if_info_t *if_info;
+ char *if_string=NULL, *temp=NULL, *snaplen_string;
+ gchar *descr;
+ if_capabilities_t *caps;
+ gint linktype_count;
+ cap_settings_t cap_settings;
+ gchar *err_str, *err_str_norfmon;
+ GSList *curr_addr;
+ int ips = 0;
+ guint i, count=0;
+ if_addr_t *addr;
+ GList *lt_entry;
+ data_link_info_t *data_link_info;
+ gchar *str = NULL, *link_type_name = NULL;
+ gboolean found = FALSE;
+ GString *ip_str;
+ GtkTreeView *if_cb;
+ GtkTreeModel *model;
+ interface_options interface_opts;
+ link_row *link = NULL;
+
+ if_cb = (GtkTreeView *) g_object_get_data(G_OBJECT(cap_open_w), E_CAP_IFACE_KEY);
+ model = gtk_tree_view_get_model(if_cb);
+ count = rows->len;
+ /* Scan through the list and build a list of strings to display. */
+ for (if_entry = g_list_first(list); if_entry != NULL; if_entry = g_list_next(if_entry)) {
+ if_info = (if_info_t *)if_entry->data;
+#ifdef HAVE_PCAP_REMOTE
+ add_interface_to_remote_list(if_info);
+#endif
+ for (i = 0; i < count; i++) {
+ row = g_array_index(rows, interface_row, i);
+ if (strcmp(row.name, if_info->name) == 0) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (found) {
+ found = FALSE;
+ continue;
+ }
+ ip_str = g_string_new("");
+ ips = 0;
+ row.name = g_strdup(if_info->name);
+ /* Is this interface hidden and, if so, should we include it
+ anyway? */
+ descr = capture_dev_user_descr_find(if_info->name);
+ if (descr != NULL) {
+ /* Yes, we have a user-supplied description; use it. */
+ if_string = g_strdup_printf("%s: %s", descr, if_info->name);
+ g_free(descr);
+ } else {
+ /* No, we don't have a user-supplied description; did we get
+ one from the OS or libpcap? */
+ if (if_info->description != NULL) {
+ /* Yes - use it. */
+ if_string = g_strdup_printf("%s: %s", if_info->description, if_info->name);
+ } else {
+ /* No. */
+ if_string = g_strdup(if_info->name);
+ }
+ } /* else descr != NULL */
+ if (if_info->loopback) {
+ row.display_name = g_strdup_printf("%s (loopback)", if_string);
+ } else {
+ row.display_name = g_strdup(if_string);
+ }
+ found = FALSE;
+ for (i = 0; i < global_capture_opts.ifaces->len; i++) {
+ interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, i);
+ if (strcmp(interface_opts.name, (char*)row.name)!=0)
+ continue;
+ else {
+ found = TRUE;
+ break;
+ }
+ }
+ if (found) {
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ row.buffer = interface_opts.buffer_size;
+#endif
+ row.pmode = interface_opts.promisc_mode;
+ row.has_snaplen = interface_opts.has_snaplen;
+ row.snaplen = interface_opts.snaplen;
+ row.cfilter = g_strdup(interface_opts.cfilter);
+ } else {
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ row.buffer = global_capture_opts.default_options.buffer_size;
+#endif
+ row.pmode = global_capture_opts.default_options.promisc_mode;
+ row.has_snaplen = global_capture_opts.default_options.has_snaplen;
+ row.snaplen = global_capture_opts.default_options.snaplen;
+ row.cfilter = g_strdup(global_capture_opts.default_options.cfilter);
+ }
+ cap_settings = capture_get_cap_settings(if_string);
+ caps = capture_get_if_capabilities(if_string, cap_settings.monitor_mode,
+ &err_str);
+ if (caps == NULL) {
+ /* Error attempting to get interface capabilities. */
+ if (cap_settings.monitor_mode) {
+ /*
+ * Perhaps this is the libpcap bug on Linux where
+ * attempting to set monitor mode with the Wireless
+ * Extensions ioctls doesn't work correctly.
+ *
+ * Try fetching the capabilities without monitor mode;
+ * if that succeeds, report the monitor-mode problem,
+ * and use the no-monitor-mode capabilities. If that
+ * fails, report that failure. In either case, force
+ * monitor mode off.
+ */
+ cap_settings.monitor_mode = FALSE;
+ caps = capture_get_if_capabilities(if_string, cap_settings.monitor_mode,
+ &err_str_norfmon);
+ if (caps == NULL) {
+ /* Epic fail. */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_str_norfmon);
+ g_free(err_str_norfmon);
+ g_free(err_str);
+ } else {
+ /*
+ * OK, it's probably that bug. Suggest using airmon-ng,
+ * just in case the adapter has a mac80211 driver and
+ * libpcap was built without libnl so that it can't
+ * use the mac80211 features to create a monitor-mode
+ * device.
+ */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s\n\n"
+ "Try using airmon-ng, as suggested by CaptureSetup/WLAN in the Wireshark Wiki.",
+ err_str);
+ g_free(err_str);
+ }
+ } else {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_str);
+ g_free(err_str);
+ }
+ }
+
+ gtk_list_store_append (GTK_LIST_STORE(model), &iter);
+ for (; (curr_addr = g_slist_nth(if_info->addrs, ips)) != NULL; ips++) {
+ if (ips != 0) {
+ g_string_append(ip_str, "\n");
+ }
+ addr = (if_addr_t *)curr_addr->data;
+
+ switch (addr->ifat_type) {
+ case IF_AT_IPv4:
+ g_string_append(ip_str, ip_to_str((guint8 *)&addr->addr.ip4_addr));
+ break;
+ case IF_AT_IPv6:
+ g_string_append(ip_str, ip6_to_str((struct e_in6_addr *)&addr->addr.ip6_addr));
+ break;
+ default:
+ /* In case we add non-IP addresses */
+ break;
+ }
+ } /* for curr_addr */
+ linktype_count = 0;
+ row.links = NULL;
+ if (caps != NULL) {
+#ifdef HAVE_PCAP_CREATE
+ row.monitor_mode_enabled = cap_settings.monitor_mode;
+ row.monitor_mode_supported = caps->can_set_rfmon;
+#endif
+ for (lt_entry = caps->data_link_types; lt_entry != NULL; lt_entry = g_list_next(lt_entry)) {
+ data_link_info = (data_link_info_t *)lt_entry->data;
+ if (data_link_info->description != NULL) {
+ str = g_strdup_printf("%s", data_link_info->description);
+ } else {
+ str = g_strdup_printf("%s (not supported)", data_link_info->name);
+ }
+ if (linktype_count == 0) {
+ link_type_name = g_strdup(str);
+ row.active_dlt = data_link_info->dlt;
+ }
+ link = (link_row *)g_malloc(sizeof(link_row));
+ link->dlt = data_link_info->dlt;
+ link->name = g_strdup(str);
+ row.links = g_list_append(row.links, link);
+ linktype_count++;
+ } /* for link_types */
+ } else {
+ cap_settings.monitor_mode = FALSE;
+#if defined(HAVE_PCAP_CREATE)
+ row.monitor_mode_enabled = FALSE;
+ row.monitor_mode_supported = FALSE;
+#endif
+ row.active_dlt = -1;
+ link_type_name = g_strdup("default");
+ }
+ row.addresses = g_strdup(ip_str->str);
+ row.no_addresses = ips;
+ if (ips == 0) {
+ temp = g_strdup_printf("<b>%s</b>", row.display_name);
+ } else {
+ temp = g_strdup_printf("<b>%s</b>\n<span size='small'>%s</span>", row.display_name, row.addresses);
+ }
+#ifdef HAVE_PCAP_REMOTE
+ row.remote_opts.src_type= global_remote_opts.src_type;
+ row.remote_opts.remote_host_opts.remote_host = g_strdup(global_remote_opts.remote_host_opts.remote_host);
+ row.remote_opts.remote_host_opts.remote_port = g_strdup(global_remote_opts.remote_host_opts.remote_port);
+ row.remote_opts.remote_host_opts.auth_type = global_remote_opts.remote_host_opts.auth_type;
+ row.remote_opts.remote_host_opts.auth_username = g_strdup(global_remote_opts.remote_host_opts.auth_username);
+ row.remote_opts.remote_host_opts.auth_password = g_strdup(global_remote_opts.remote_host_opts.auth_password);
+ row.remote_opts.remote_host_opts.datatx_udp = global_remote_opts.remote_host_opts.datatx_udp;
+ row.remote_opts.remote_host_opts.nocap_rpcap = global_remote_opts.remote_host_opts.nocap_rpcap;
+ row.remote_opts.remote_host_opts.nocap_local = global_remote_opts.remote_host_opts.nocap_local;
+#endif
+#ifdef HAVE_PCAP_SETSAMPLING
+ row.remote_opts.sampling_method = global_remote_opts.sampling_method;
+ row.remote_opts.sampling_param = global_remote_opts.sampling_param;
+#endif
+ g_array_append_val(rows, row);
+ if (row.has_snaplen) {
+ snaplen_string = g_strdup_printf("%d", row.snaplen);
+ } else {
+ snaplen_string = g_strdup("default");
+ }
+
+#if defined(HAVE_PCAP_CREATE)
+ gtk_list_store_set (GTK_LIST_STORE(model), &iter, CAPTURE, FALSE, INTERFACE, temp, LINK, link_type_name, PMODE, (row.pmode?"enabled":"disabled"), SNAPLEN, snaplen_string, BUFFER, (guint) global_capture_opts.default_options.buffer_size, MONITOR, "no",FILTER, "",-1);
+#elif defined(_WIN32) && !defined(HAVE_PCAP_CREATE)
+ gtk_list_store_set (GTK_LIST_STORE(model), &iter, CAPTURE, FALSE, INTERFACE, temp, LINK, link_type_name, PMODE, (row.pmode?"enabled":"disabled"), SNAPLEN, snaplen_string, BUFFER, (guint) global_capture_opts.default_options.buffer_size, FILTER, "",-1);
+ #else
+ gtk_list_store_set (GTK_LIST_STORE(model), &iter, CAPTURE, FALSE, INTERFACE, temp, LINK, link_type_name, PMODE, (row.pmode?"enabled":"disabled"), SNAPLEN, snaplen_string, -1);
+#endif
+ count++;
+ g_string_free(ip_str, TRUE);
+#ifdef HAVE_PCAP_REMOTE
+ add_interface_to_list(if_info->name, if_info->description, &row.remote_opts);
+#endif
+ } /*for*/
+ gtk_tree_view_set_model(GTK_TREE_VIEW(if_cb), model);
+}
+
+/* Retrieve the list of local or remote interfaces according to selected
+ * options and re-fill interface name combobox */
+static void
+update_interface_list(void)
+{
+ GtkWidget *iftype_cbx;
+ GtkTreeView *if_cb;
+ GList *if_list, *if_r_list;
+ int iftype, prev_iftype, err;
+ gchar *err_str;
+
+ if (cap_open_w == NULL)
+ return;
+ if_cb = (GtkTreeView *)g_object_get_data(G_OBJECT(cap_open_w), E_CAP_IFACE_KEY);
+ iftype_cbx = g_object_get_data(G_OBJECT(cap_open_w), E_CAP_IFTYPE_CBX_KEY);
+ iftype = CAPTURE_IFREMOTE;
+ if (iftype >= CAPTURE_IFREMOTE) {
+ if_r_list = get_remote_interface_list(global_remote_opts.remote_host_opts.remote_host,
+ global_remote_opts.remote_host_opts.remote_port,
+ global_remote_opts.remote_host_opts.auth_type,
+ global_remote_opts.remote_host_opts.auth_username,
+ global_remote_opts.remote_host_opts.auth_password,
+ &err, &err_str);
+
+ if_list = if_r_list;
+ } else {
+ if_list = capture_interface_list(&err, &err_str); /* Warning: see capture_prep_cb() */
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_IF_LIST_KEY, NULL);
+ }
+
+ if (if_list == NULL &&
+ (err == CANT_GET_INTERFACE_LIST || err == DONT_HAVE_PCAP)) {
+ gpointer dialog = simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_str);
+ g_free(err_str);
+
+ if (iftype >= CAPTURE_IFREMOTE) {
+ /* Fall back to previous interface list */
+ simple_dialog_set_cb(dialog, error_list_remote_interface_cb, iftype_cbx);
+ prev_iftype = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(iftype_cbx),
+ E_CAP_CBX_PREV_IFTYPE_VALUE_KEY));
+ g_object_set_data(G_OBJECT(iftype_cbx), E_CAP_CBX_IFTYPE_VALUE_KEY, GINT_TO_POINTER(prev_iftype));
+ return;
+ }
+ } else if (iftype == CAPTURE_IFREMOTE) {
+ /* New remote interface */
+ insert_new_rows(if_list);
+ if (interfaces_dialog_window_present()) {
+ refresh_if_window();
+ }
+ }
+}
+
+/* User changed an interface entry of "Remote interface" dialog */
+static void
+capture_remote_adjust_sensitivity(GtkWidget *tb _U_, gpointer parent_w)
+{
+ GtkWidget *auth_passwd_rb,
+ *username_lb, *username_te,
+ *passwd_lb, *passwd_te;
+ gboolean state;
+
+ auth_passwd_rb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w),
+ E_REMOTE_AUTH_PASSWD_KEY);
+ username_lb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w),
+ E_REMOTE_USERNAME_LB_KEY);
+ username_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w),
+ E_REMOTE_USERNAME_TE_KEY);
+ passwd_lb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_REMOTE_PASSWD_LB_KEY);
+ passwd_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_REMOTE_PASSWD_TE_KEY);
+
+ state = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(auth_passwd_rb));
+ gtk_widget_set_sensitive(GTK_WIDGET(username_lb), state);
+ gtk_widget_set_sensitive(GTK_WIDGET(username_te), state);
+ gtk_widget_set_sensitive(GTK_WIDGET(passwd_lb), state);
+ gtk_widget_set_sensitive(GTK_WIDGET(passwd_te), state);
+}
+
+/* user requested to destroy the dialog */
+static void
+capture_remote_destroy_cb(GtkWidget *win, gpointer user_data _U_)
+{
+ GtkWidget *caller;
+
+ caller = g_object_get_data(G_OBJECT(win), E_CAP_REMOTE_CALLER_PTR_KEY);
+ g_object_set_data(G_OBJECT(caller), E_CAP_REMOTE_DIALOG_PTR_KEY, NULL);
+}
+
+/* user requested to accept remote interface options */
+static void
+capture_remote_ok_cb(GtkWidget *win _U_, GtkWidget *remote_w)
+{
+ GtkWidget *host_te, *port_te, *auth_pwd_rb, *username_te, *passwd_te,
+ *auth_null_rb, *auth_passwd_rb, *iftype_cbx;
+ int prev_iftype;
+
+ if (remote_w == NULL) {
+ return;
+ }
+ host_te = (GtkWidget *)g_object_get_data(G_OBJECT(remote_w), E_REMOTE_HOST_TE_KEY);
+ port_te = (GtkWidget *)g_object_get_data(G_OBJECT(remote_w), E_REMOTE_PORT_TE_KEY);
+ auth_pwd_rb = (GtkWidget *)g_object_get_data(G_OBJECT(remote_w),
+ E_REMOTE_AUTH_PASSWD_KEY);
+ username_te = (GtkWidget *)g_object_get_data(G_OBJECT(remote_w),
+ E_REMOTE_USERNAME_TE_KEY);
+ passwd_te = (GtkWidget *)g_object_get_data(G_OBJECT(remote_w), E_REMOTE_PASSWD_TE_KEY);
+ auth_null_rb = (GtkWidget *) g_object_get_data(G_OBJECT(remote_w), E_REMOTE_AUTH_NULL_KEY);
+ auth_passwd_rb = (GtkWidget *) g_object_get_data(G_OBJECT(remote_w), E_REMOTE_AUTH_PASSWD_KEY);
+ iftype_cbx = g_object_get_data(G_OBJECT(cap_open_w), E_CAP_IFTYPE_CBX_KEY);
+ prev_iftype = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(iftype_cbx),
+ E_CAP_CBX_IFTYPE_VALUE_KEY));
+ g_free(global_remote_opts.remote_host_opts.remote_host);
+ global_remote_opts.remote_host_opts.remote_host = g_strdup(gtk_entry_get_text(GTK_ENTRY(host_te)));
+ g_free(global_remote_opts.remote_host_opts.remote_port);
+ global_remote_opts.remote_host_opts.remote_port = g_strdup(gtk_entry_get_text(GTK_ENTRY(port_te)));
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(auth_passwd_rb)))
+ global_remote_opts.remote_host_opts.auth_type = CAPTURE_AUTH_PWD;
+ else
+ global_remote_opts.remote_host_opts.auth_type = CAPTURE_AUTH_NULL;
+ g_free(global_remote_opts.remote_host_opts.auth_username);
+ global_remote_opts.remote_host_opts.auth_username =
+ g_strdup(gtk_entry_get_text(GTK_ENTRY(username_te)));
+
+ g_free(global_remote_opts.remote_host_opts.auth_password);
+ global_remote_opts.remote_host_opts.auth_password =
+ g_strdup(gtk_entry_get_text(GTK_ENTRY(passwd_te)));
+
+ g_object_set_data(G_OBJECT(iftype_cbx), E_CAP_CBX_PREV_IFTYPE_VALUE_KEY,
+ GINT_TO_POINTER(prev_iftype));
+ g_object_set_data(G_OBJECT(iftype_cbx), E_CAP_CBX_IFTYPE_VALUE_KEY,
+ GINT_TO_POINTER(CAPTURE_IFREMOTE));
+
+ window_destroy(GTK_WIDGET(remote_w));
+ update_interface_list();
+}
+
+static void
+capture_remote_cancel_cb(GtkWidget *win, gpointer data)
+{
+ GtkWidget *iftype_cbx;
+ int old_iftype;
+
+ iftype_cbx = g_object_get_data(G_OBJECT(cap_open_w), E_CAP_IFTYPE_CBX_KEY);
+ old_iftype = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(iftype_cbx),
+ E_CAP_CBX_PREV_IFTYPE_VALUE_KEY));
+ gtk_combo_box_set_active(GTK_COMBO_BOX(iftype_cbx), old_iftype);
+
+ window_cancel_button_cb (win, data);
+}
+
+/* Show remote capture interface parameters dialog */
+static void
+capture_remote_cb(GtkWidget *w, gboolean focus_username)
+{
+ GtkWidget *caller, *remote_w,
+ *main_vb, *host_tb,
+ *host_lb, *host_te, *port_lb, *port_te,
+ *auth_fr, *auth_vb,
+ *auth_null_rb, *auth_passwd_rb, *auth_passwd_tb,
+ *user_lb, *user_te, *passwd_lb, *passwd_te,
+ *bbox, *ok_bt, *cancel_bt;
+ gchar *title;
+ GSList *auth_group;
+
+ caller = gtk_widget_get_toplevel(w);
+ remote_w = g_object_get_data(G_OBJECT(caller), E_CAP_REMOTE_DIALOG_PTR_KEY);
+ if (remote_w != NULL) {
+ reactivate_window(remote_w);
+ return;
+ }
+
+ title = create_user_window_title("Wireshark: Remote Interface");
+ remote_w = dlg_window_new(title);
+ g_object_set_data(G_OBJECT(remote_w), E_CAP_REMOTE_CALLER_PTR_KEY, caller);
+ g_object_set_data(G_OBJECT(caller), E_CAP_REMOTE_DIALOG_PTR_KEY, remote_w);
+ g_free(title);
+
+ main_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(remote_w), main_vb);
+
+ /* Host/port table */
+ host_tb = gtk_table_new(2, 2, FALSE);
+ gtk_table_set_row_spacings(GTK_TABLE(host_tb), 3);
+ gtk_table_set_col_spacings(GTK_TABLE(host_tb), 3);
+ gtk_box_pack_start(GTK_BOX(main_vb), host_tb, FALSE, FALSE, 0);
+
+ /* Host row */
+ host_lb = gtk_label_new("Host:");
+ gtk_table_attach_defaults(GTK_TABLE(host_tb), host_lb, 0, 1, 0, 1);
+
+ host_te = gtk_entry_new();
+ gtk_widget_set_tooltip_text(host_te, "Enter the hostname or host IP address to be used as a source for remote capture.");
+ gtk_table_attach_defaults(GTK_TABLE(host_tb), host_te, 1, 2, 0, 1);
+ if (global_remote_opts.remote_host_opts.remote_host != NULL)
+ gtk_entry_set_text(GTK_ENTRY(host_te), global_remote_opts.remote_host_opts.remote_host);
+
+ /* Port row */
+ port_lb = gtk_label_new("Port:");
+ gtk_table_attach_defaults(GTK_TABLE(host_tb), port_lb, 0, 1, 1, 2);
+
+ port_te = gtk_entry_new();
+ gtk_widget_set_tooltip_text(port_te, "Enter the TCP port number used by RPCAP server at remote host "
+ "(leave it empty for default port number).");
+ gtk_table_attach_defaults(GTK_TABLE(host_tb), port_te, 1, 2, 1, 2);
+
+ if (global_remote_opts.remote_host_opts.remote_port != NULL)
+ gtk_entry_set_text(GTK_ENTRY(port_te),global_remote_opts.remote_host_opts.remote_port);
+
+ /* Authentication options frame */
+ auth_fr = gtk_frame_new("Authentication");
+ gtk_container_add(GTK_CONTAINER(main_vb), auth_fr);
+
+ auth_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(auth_vb), 5);
+ gtk_container_add(GTK_CONTAINER(auth_fr), auth_vb);
+
+ auth_null_rb = gtk_radio_button_new_with_label(NULL,
+ "Null authentication");
+ gtk_box_pack_start(GTK_BOX(auth_vb), auth_null_rb, TRUE, TRUE, 0);
+
+ auth_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(auth_null_rb));
+ auth_passwd_rb = gtk_radio_button_new_with_label(auth_group,
+ "Password authentication");
+ gtk_box_pack_start(GTK_BOX(auth_vb), auth_passwd_rb, TRUE, TRUE, 0);
+ g_signal_connect(auth_passwd_rb, "toggled",
+ G_CALLBACK(capture_remote_adjust_sensitivity), remote_w);
+
+ auth_passwd_tb = gtk_table_new(2, 2, FALSE);
+ gtk_table_set_row_spacings(GTK_TABLE(auth_passwd_tb), 3);
+ gtk_table_set_col_spacings(GTK_TABLE(auth_passwd_tb), 3);
+ gtk_box_pack_start(GTK_BOX(auth_vb), auth_passwd_tb, FALSE, FALSE, 0);
+
+ user_lb = gtk_label_new("Username:");
+ gtk_table_attach_defaults(GTK_TABLE(auth_passwd_tb), user_lb, 0, 1, 0, 1);
+
+ user_te = gtk_entry_new();
+ gtk_table_attach_defaults(GTK_TABLE(auth_passwd_tb), user_te, 1, 2, 0, 1);
+ if (global_remote_opts.remote_host_opts.auth_username != NULL)
+ gtk_entry_set_text(GTK_ENTRY(user_te), global_remote_opts.remote_host_opts.auth_username);
+
+ passwd_lb = gtk_label_new("Password:");
+ gtk_table_attach_defaults(GTK_TABLE(auth_passwd_tb), passwd_lb, 0, 1, 1, 2);
+
+ passwd_te = gtk_entry_new();
+ gtk_entry_set_visibility(GTK_ENTRY(passwd_te), FALSE);
+ gtk_table_attach_defaults(GTK_TABLE(auth_passwd_tb), passwd_te, 1, 2, 1, 2);
+ if (global_remote_opts.remote_host_opts.auth_password != NULL)
+ gtk_entry_set_text(GTK_ENTRY(passwd_te), global_remote_opts.remote_host_opts.auth_password);
+
+ /* Button row: "Start" and "Cancel" buttons */
+ bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 5);
+
+ ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(capture_remote_ok_cb), remote_w);
+ gtk_widget_set_tooltip_text(ok_bt,
+ "Accept remote host parameters and lookup "
+ "remote interfaces.");
+
+ cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ gtk_widget_set_tooltip_text(cancel_bt, "Cancel and exit dialog.");
+ window_set_cancel_button(remote_w, cancel_bt, capture_remote_cancel_cb);
+
+ if (focus_username) {
+ /* Give the initial focus to the "Username" entry box. */
+ gtk_widget_grab_focus(user_te);
+ }
+
+ gtk_widget_grab_default(ok_bt);
+
+ /* Catch the "activate" signal on the text
+ entries, so that if the user types Return there, we act as if the
+ "OK" button had been selected, as happens if Return is typed if some
+ widget that *doesn't* handle the Return key has the input focus. */
+ dlg_set_activate(host_te, ok_bt);
+ dlg_set_activate(port_te, ok_bt);
+ dlg_set_activate(user_te, ok_bt);
+ dlg_set_activate(passwd_te, ok_bt);
+
+ g_signal_connect(remote_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(remote_w, "destroy", G_CALLBACK(capture_remote_destroy_cb), NULL);
+
+ g_object_set_data(G_OBJECT(remote_w), E_REMOTE_HOST_TE_KEY, host_te);
+ g_object_set_data(G_OBJECT(remote_w), E_REMOTE_PORT_TE_KEY, port_te);
+ g_object_set_data(G_OBJECT(remote_w), E_REMOTE_AUTH_NULL_KEY, auth_null_rb);
+ g_object_set_data(G_OBJECT(remote_w), E_REMOTE_AUTH_PASSWD_KEY, auth_passwd_rb);
+ g_object_set_data(G_OBJECT(remote_w), E_REMOTE_USERNAME_LB_KEY, user_lb);
+ g_object_set_data(G_OBJECT(remote_w), E_REMOTE_USERNAME_TE_KEY, user_te);
+ g_object_set_data(G_OBJECT(remote_w), E_REMOTE_PASSWD_LB_KEY, passwd_lb);
+ g_object_set_data(G_OBJECT(remote_w), E_REMOTE_PASSWD_TE_KEY, passwd_te);
+
+ if (global_remote_opts.remote_host_opts.auth_type == CAPTURE_AUTH_PWD)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(auth_passwd_rb), TRUE);
+ else
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(auth_null_rb), TRUE);
+ capture_remote_adjust_sensitivity(NULL, remote_w);
+
+ gtk_widget_show_all(remote_w);
+ window_present(remote_w);
+}
+
+/* user requested to destroy the dialog */
+static void
+options_remote_destroy_cb(GtkWidget *win, gpointer user_data _U_)
+{
+ GtkWidget *caller;
+
+ caller = g_object_get_data(G_OBJECT(win), E_OPT_REMOTE_CALLER_PTR_KEY);
+ g_object_set_data(G_OBJECT(caller), E_OPT_REMOTE_DIALOG_PTR_KEY, NULL);
+}
+
+/* user requested to accept remote interface options */
+static void
+options_remote_ok_cb(GtkWidget *win _U_, GtkWidget *parent_w)
+{
+ GtkWidget *datatx_udp_cb, *nocap_rpcap_cb;
+#ifdef HAVE_PCAP_SETSAMPLING
+ GtkWidget *samp_none_rb, *samp_count_rb, *samp_timer_rb,
+ *samp_count_sb, *samp_timer_sb;
+#endif
+ interface_row row;
+
+ if (parent_w == NULL)
+ return;
+
+ row = g_array_index(rows, interface_row, marked_row);
+ g_array_remove_index(rows, marked_row);
+ datatx_udp_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_DATATX_UDP_CB_KEY);
+ nocap_rpcap_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_NOCAP_RPCAP_CB_KEY);
+
+ row.remote_opts.remote_host_opts.datatx_udp =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(datatx_udp_cb));
+ row.remote_opts.remote_host_opts.nocap_rpcap =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(nocap_rpcap_cb));
+
+#ifdef HAVE_PCAP_SETSAMPLING
+ samp_none_rb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_SAMP_NONE_RB_KEY);
+ samp_count_rb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_SAMP_COUNT_RB_KEY);
+ samp_timer_rb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_SAMP_TIMER_RB_KEY);
+ samp_count_sb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_SAMP_COUNT_SB_KEY);
+ samp_timer_sb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_SAMP_TIMER_SB_KEY);
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(samp_none_rb)))
+ row.remote_opts.sampling_method = CAPTURE_SAMP_NONE;
+ else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(samp_count_rb))) {
+ row.remote_opts.sampling_method = CAPTURE_SAMP_BY_COUNT;
+ row.remote_opts.sampling_param = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(samp_count_sb));
+ } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(samp_timer_rb))) {
+ row.remote_opts.sampling_method = CAPTURE_SAMP_BY_TIMER;
+ row.remote_opts.sampling_param = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(samp_timer_sb));
+ }
+#endif /* HAVE_PCAP_SETSAMPLING*/
+ g_array_insert_val(rows, marked_row, row);
+ window_destroy(GTK_WIDGET(parent_w));
+}
+#endif /*HAVE_PCAP_REMOTE*/
+
+#ifdef HAVE_PCAP_SETSAMPLING
+static void
+options_prep_adjust_sensitivity(GtkWidget *tb _U_, gpointer parent_w)
+{
+ GtkWidget *samp_count_rb, *samp_timer_rb,
+ *samp_count_sb, *samp_timer_sb;
+
+ samp_count_rb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_SAMP_COUNT_RB_KEY);
+ samp_timer_rb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_SAMP_TIMER_RB_KEY);
+ samp_count_sb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_SAMP_COUNT_SB_KEY);
+ samp_timer_sb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_SAMP_TIMER_SB_KEY);
+
+ if (samp_count_sb && samp_count_rb)
+ gtk_widget_set_sensitive(GTK_WIDGET(samp_count_sb),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(samp_count_rb)));
+
+ if (samp_timer_sb && samp_timer_rb)
+ gtk_widget_set_sensitive(GTK_WIDGET(samp_timer_sb),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(samp_timer_rb)));
+}
+
+#endif /*HAVE_PCAP_SETSAMPLING*/
+#ifdef HAVE_PCAP_REMOTE
+void
+options_remote_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ GtkWidget *opt_remote_w, *main_vb;
+ GtkWidget *caller, *bbox, *ok_bt, *cancel_bt;
+ GtkWidget *capture_fr, *capture_vb;
+ GtkWidget *nocap_rpcap_cb, *datatx_udp_cb;
+#ifdef HAVE_PCAP_SETSAMPLING
+ GtkWidget *sampling_fr, *sampling_vb, *sampling_tb, *sampling_lb,
+ *samp_none_rb, *samp_count_rb, *samp_timer_rb,
+ *samp_count_sb, *samp_timer_sb;
+ GtkAdjustment *samp_count_adj, *samp_timer_adj;
+ GSList *samp_group;
+#endif
+ interface_row row;
+
+ caller = gtk_widget_get_toplevel(w);
+ opt_remote_w = g_object_get_data(G_OBJECT(caller), E_OPT_REMOTE_DIALOG_PTR_KEY);
+ if (opt_remote_w != NULL) {
+ reactivate_window(opt_remote_w);
+ return;
+ }
+
+ row = g_array_index(rows, interface_row, marked_row);
+ opt_remote_w = dlg_window_new("Remote Capture Settings");
+ g_object_set_data(G_OBJECT(opt_remote_w), E_OPT_REMOTE_CALLER_PTR_KEY, caller);
+ g_object_set_data(G_OBJECT(caller), E_OPT_REMOTE_DIALOG_PTR_KEY, opt_remote_w);
+
+ main_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(opt_remote_w), main_vb);
+
+ /* Remote capture options */
+ capture_fr = gtk_frame_new("Capture Options");
+ gtk_container_add(GTK_CONTAINER(main_vb), capture_fr);
+
+ capture_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(capture_vb), 5);
+ gtk_container_add(GTK_CONTAINER(capture_fr), capture_vb);
+
+ nocap_rpcap_cb = gtk_check_button_new_with_mnemonic("Do not capture own RPCAP traffic");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(nocap_rpcap_cb),
+ row.remote_opts.remote_host_opts.nocap_rpcap);
+ gtk_container_add(GTK_CONTAINER(capture_vb), nocap_rpcap_cb);
+
+ datatx_udp_cb = gtk_check_button_new_with_mnemonic("Use UDP for data transfer");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(datatx_udp_cb),
+ row.remote_opts.remote_host_opts.datatx_udp);
+ gtk_container_add(GTK_CONTAINER(capture_vb), datatx_udp_cb);
+
+#ifdef HAVE_PCAP_SETSAMPLING
+ /* Sampling options */
+ sampling_fr = gtk_frame_new("Sampling Options");
+ gtk_container_add(GTK_CONTAINER(main_vb), sampling_fr);
+
+ sampling_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(sampling_vb), 5);
+ gtk_container_add(GTK_CONTAINER(sampling_fr), sampling_vb);
+
+ sampling_tb = gtk_table_new(3, 3, FALSE);
+ gtk_table_set_row_spacings(GTK_TABLE(sampling_tb), 1);
+ gtk_table_set_col_spacings(GTK_TABLE(sampling_tb), 3);
+ gtk_box_pack_start(GTK_BOX(sampling_vb), sampling_tb, FALSE, FALSE, 0);
+
+ /* "No sampling" row */
+ samp_none_rb = gtk_radio_button_new_with_label(NULL, "None");
+ if (row.remote_opts.sampling_method == CAPTURE_SAMP_NONE)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(samp_none_rb), TRUE);
+ g_signal_connect(samp_none_rb, "toggled",
+ G_CALLBACK(options_prep_adjust_sensitivity), opt_remote_w);
+ gtk_table_attach_defaults(GTK_TABLE(sampling_tb), samp_none_rb, 0, 1, 0, 1);
+
+ /* "Sampling by counter" row */
+ samp_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(samp_none_rb));
+ samp_count_rb = gtk_radio_button_new_with_label(samp_group, "1 of");
+ if (row.remote_opts.sampling_method == CAPTURE_SAMP_BY_COUNT)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(samp_count_rb), TRUE);
+ g_signal_connect(samp_count_rb, "toggled",
+ G_CALLBACK(options_prep_adjust_sensitivity), opt_remote_w);
+ gtk_table_attach_defaults(GTK_TABLE(sampling_tb), samp_count_rb, 0, 1, 1, 2);
+
+ samp_count_adj = (GtkAdjustment *) gtk_adjustment_new(
+ (gfloat)row.remote_opts.sampling_param,
+ 1, (gfloat)INT_MAX, 1.0, 10.0, 0.0);
+ samp_count_sb = gtk_spin_button_new(samp_count_adj, 0, 0);
+ gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(samp_count_sb), TRUE);
+ gtk_table_attach_defaults(GTK_TABLE(sampling_tb), samp_count_sb, 1, 2, 1, 2);
+
+ sampling_lb = gtk_label_new("packets");
+ gtk_misc_set_alignment(GTK_MISC(sampling_lb), 0, 0.5);
+ gtk_table_attach_defaults(GTK_TABLE(sampling_tb), sampling_lb, 2, 3, 1, 2);
+
+ /* "Sampling by timer" row */
+ samp_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(samp_count_rb));
+ samp_timer_rb = gtk_radio_button_new_with_label(samp_group, "1 every");
+ if (row.remote_opts.sampling_method == CAPTURE_SAMP_BY_TIMER)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(samp_timer_rb), TRUE);
+ g_signal_connect(samp_timer_rb, "toggled",
+ G_CALLBACK(options_prep_adjust_sensitivity), opt_remote_w);
+ gtk_table_attach_defaults(GTK_TABLE(sampling_tb), samp_timer_rb, 0, 1, 2, 3);
+
+ samp_timer_adj = (GtkAdjustment *) gtk_adjustment_new(
+ (gfloat)row.remote_opts.sampling_param,
+ 1, (gfloat)INT_MAX, 1.0, 10.0, 0.0);
+ samp_timer_sb = gtk_spin_button_new(samp_timer_adj, 0, 0);
+ gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(samp_timer_sb), TRUE);
+ gtk_table_attach_defaults(GTK_TABLE(sampling_tb), samp_timer_sb, 1, 2, 2, 3);
+
+ sampling_lb = gtk_label_new("milliseconds");
+ gtk_misc_set_alignment(GTK_MISC(sampling_lb), 0, 0.5);
+ gtk_table_attach_defaults(GTK_TABLE(sampling_tb), sampling_lb, 2, 3, 2, 3);
+#endif
+
+ /* Button row: "Start" and "Cancel" buttons */
+ bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 5);
+
+ ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(options_remote_ok_cb), opt_remote_w);
+ gtk_widget_set_tooltip_text(ok_bt, "Accept parameters and close dialog");
+ cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ gtk_widget_set_tooltip_text(cancel_bt, "Cancel and exit dialog.");
+ window_set_cancel_button(opt_remote_w, cancel_bt, window_cancel_button_cb);
+
+ gtk_widget_grab_default(ok_bt);
+
+ g_signal_connect(opt_remote_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(opt_remote_w, "destroy", G_CALLBACK(options_remote_destroy_cb), NULL);
+
+ g_object_set_data(G_OBJECT(opt_remote_w), E_CAP_NOCAP_RPCAP_CB_KEY, nocap_rpcap_cb);
+ g_object_set_data(G_OBJECT(opt_remote_w), E_CAP_DATATX_UDP_CB_KEY, datatx_udp_cb);
+
+#ifdef HAVE_PCAP_SETSAMPLING
+ g_object_set_data(G_OBJECT(opt_remote_w), E_CAP_SAMP_NONE_RB_KEY, samp_none_rb);
+ g_object_set_data(G_OBJECT(opt_remote_w), E_CAP_SAMP_COUNT_RB_KEY, samp_count_rb);
+ g_object_set_data(G_OBJECT(opt_remote_w), E_CAP_SAMP_COUNT_SB_KEY, samp_count_sb);
+ g_object_set_data(G_OBJECT(opt_remote_w), E_CAP_SAMP_TIMER_RB_KEY, samp_timer_rb);
+ g_object_set_data(G_OBJECT(opt_remote_w), E_CAP_SAMP_TIMER_SB_KEY, samp_timer_sb);
+#endif
+
+#ifdef HAVE_PCAP_SETSAMPLING
+ /* Set the sensitivity of various widgets as per the settings of other
+ widgets. */
+ options_prep_adjust_sensitivity(NULL, opt_remote_w);
+#endif
+
+ gtk_widget_show_all(opt_remote_w);
+ window_present(opt_remote_w);
+}
+
+static void
+recent_print_remote_host (gpointer key _U_, gpointer value, gpointer user)
+{
+ FILE *rf = user;
+ struct remote_host *ri = value;
+
+ fprintf (rf, RECENT_KEY_REMOTE_HOST ": %s,%s,%d\n", ri->remote_host, ri->remote_port, ri->auth_type);
+}
+
+void
+capture_remote_combo_recent_write_all(FILE *rf)
+{
+ if (remote_host_list && g_hash_table_size (remote_host_list) > 0) {
+ /* Write all remote interfaces to the recent file */
+ g_hash_table_foreach (remote_host_list, recent_print_remote_host, rf);
+ }
+}
+
+gboolean
+capture_remote_combo_add_recent(gchar *s)
+{
+ GList *vals = prefs_get_string_list (s);
+ GList *valp = vals;
+ struct remote_host *rh;
+ gint auth_type;
+ char *p;
+
+ if (valp == NULL)
+ return FALSE;
+
+ if (remote_host_list == NULL) {
+ remote_host_list = g_hash_table_new (g_str_hash, g_str_equal);
+ }
+
+ rh = g_malloc (sizeof (*rh));
+
+ /* First value is the host */
+ rh->remote_host = g_strdup (valp->data);
+ if (strlen(rh->remote_host) == 0)
+ /* Empty remote host */
+ return FALSE;
+ rh->auth_type = CAPTURE_AUTH_NULL;
+ valp = valp->next;
+
+ if (valp) {
+ /* Found value 2, this is the port number */
+ rh->remote_port = g_strdup (valp->data);
+ valp = valp->next;
+ } else {
+ /* Did not find a port number */
+ rh->remote_port = g_strdup ("");
+ }
+
+ if (valp) {
+ /* Found value 3, this is the authentication type */
+ auth_type = strtol(valp->data, &p, 0);
+ if (p != valp->data && *p == '\0') {
+ rh->auth_type = auth_type;
+ }
+ }
+
+ /* Do not store username and password */
+ rh->auth_username = g_strdup ("");
+ rh->auth_password = g_strdup ("");
+
+ prefs_clear_string_list(vals);
+
+ g_hash_table_insert (remote_host_list, g_strdup(rh->remote_host), rh);
+
+ return TRUE;
+}
+
+#endif /* HAVE_PCAP_REMOTE */
+
+#if defined(HAVE_PCAP_OPEN_DEAD) && defined(HAVE_BPF_IMAGE)
+static void
+capture_filter_compile_cb(GtkWidget *w _U_, gpointer user_data _U_)
+{
+ pcap_t *pd;
+ struct bpf_program fcode;
+
+ GtkWidget *filter_cm;
+ const gchar *filter_text;
+ gpointer ptr;
+ int dlt;
+ GtkWidget *linktype_combo_box = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CAP_LT_CBX_KEY);
+
+ if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(linktype_combo_box), &ptr)) {
+ g_assert_not_reached(); /* Programming error: somehow nothing is active */
+ }
+ if ((dlt = GPOINTER_TO_INT(ptr)) == -1) {
+ g_assert_not_reached(); /* Programming error: somehow managed to select an "unsupported" entry */
+ }
+ pd = pcap_open_dead(dlt, DUMMY_SNAPLENGTH);
+ filter_cm = (GtkWidget *)g_object_get_data(G_OBJECT(opt_edit_w), E_CFILTER_CM_KEY);
+ filter_text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(filter_cm));
+ g_mutex_lock(pcap_compile_mtx);
+ /* pcap_compile will not alter the filter string, so the (char *) cast is "safe" */
+#ifdef PCAP_NETMASK_UNKNOWN
+ if (pcap_compile(pd, &fcode, (char *)filter_text, 1 /* Do optimize */, PCAP_NETMASK_UNKNOWN) < 0) {
+#else
+ if (pcap_compile(pd, &fcode, (char *)filter_text, 1 /* Do optimize */, 0) < 0) {
+#endif
+ g_mutex_unlock(pcap_compile_mtx);
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", pcap_geterr(pd));
+ } else {
+ GString *bpf_code_dump = g_string_new("");
+ struct bpf_insn *insn = fcode.bf_insns;
+ int i, n = fcode.bf_len;
+
+ gchar *bpf_code_str;
+ gchar *bpf_code_markup;
+
+ for (i = 0; i < n; ++insn, ++i) {
+ g_string_append(bpf_code_dump, bpf_image(insn, i));
+ g_string_append(bpf_code_dump, "\n");
+ }
+
+ bpf_code_str = g_string_free(bpf_code_dump, FALSE);
+ bpf_code_markup = g_markup_escape_text(bpf_code_str, -1);
+
+ simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK, "<markup><tt>%s</tt></markup>", bpf_code_markup);
+
+ g_free(bpf_code_str);
+ g_free(bpf_code_markup);
+ }
+
+ pcap_close(pd);
+}
+#endif /* HAVE_PCAP_OPEN_DEAD && HAVE_BPF_IMAGE */
+
+static void
+options_edit_destroy_cb(GtkWidget *win, gpointer user_data _U_)
+{
+ GtkWidget *caller;
+
+ caller = (GtkWidget *)g_object_get_data(G_OBJECT(win), E_OPT_EDIT_CALLER_PTR_KEY);
+ g_object_set_data(G_OBJECT(caller), E_OPT_EDIT_DIALOG_PTR_KEY, NULL);
+}
+
+static void
+update_options_table(gint index)
+{
+ guint i;
+ gboolean found = FALSE;
+ interface_row row;
+ interface_options interface_opts;
+ GtkTreePath *path;
+ GtkTreeView *if_cb;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gchar *temp, *path_str, *snaplen_string;
+ GList *list;
+ link_row *link = NULL;
+ gboolean enabled;
+
+ row = g_array_index(rows, interface_row, index);
+
+ if (global_capture_opts.ifaces->len > 0) {
+ for (i = 0; i < global_capture_opts.ifaces->len; i++) {
+ interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, i);
+ if (strcmp(interface_opts.name, row.name) == 0) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (found) {
+ global_capture_opts.ifaces = g_array_remove_index(global_capture_opts.ifaces, i);
+ g_free(interface_opts.cfilter);
+ interface_opts.linktype = row.active_dlt;
+ interface_opts.promisc_mode = row.pmode;
+ interface_opts.has_snaplen = row.has_snaplen;
+ interface_opts.snaplen = row.snaplen;
+ interface_opts.cfilter = g_strdup(row.cfilter);
+#ifdef HAVE_PCAP_CREATE
+ interface_opts.monitor_mode = row.monitor_mode_enabled;
+#else
+ interface_opts.monitor_mode = FALSE;
+#endif
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ interface_opts.buffer_size = row.buffer;
+#endif
+ g_array_insert_val(global_capture_opts.ifaces, i, interface_opts);
+ }
+ }
+ if (!found || global_capture_opts.ifaces->len == 0) {
+ interface_opts.name = g_strdup(row.name);
+ interface_opts.descr = get_interface_descriptive_name(interface_opts.name);
+ interface_opts.linktype = row.active_dlt;
+ interface_opts.promisc_mode = row.pmode;
+ interface_opts.has_snaplen = row.has_snaplen;
+ interface_opts.snaplen = row.snaplen;
+ interface_opts.cfilter = g_strdup(row.cfilter);
+#ifdef HAVE_PCAP_CREATE
+ interface_opts.monitor_mode = row.monitor_mode_enabled;
+#else
+ interface_opts.monitor_mode = FALSE;
+#endif
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ interface_opts.buffer_size = row.buffer;
+#endif
+#ifdef HAVE_PCAP_REMOTE
+ interface_opts.src_type = global_capture_opts.default_options.src_type;
+ interface_opts.remote_host = g_strdup(global_capture_opts.default_options.remote_host);
+ interface_opts.remote_port = g_strdup(global_capture_opts.default_options.remote_port);
+ interface_opts.auth_type = global_capture_opts.default_options.auth_type;
+ interface_opts.auth_username = g_strdup(global_capture_opts.default_options.auth_username);
+ interface_opts.auth_password = g_strdup(global_capture_opts.default_options.auth_password);
+ interface_opts.datatx_udp = global_capture_opts.default_options.datatx_udp;
+ interface_opts.nocap_rpcap = global_capture_opts.default_options.nocap_rpcap;
+ interface_opts.nocap_local = global_capture_opts.default_options.nocap_local;
+#endif
+#ifdef HAVE_PCAP_SETSAMPLING
+ interface_opts.sampling_method = global_capture_opts.default_options.sampling_method;
+ interface_opts.sampling_param = global_capture_opts.default_options.sampling_param;
+#endif
+ g_array_append_val(global_capture_opts.ifaces, interface_opts);
+ }
+ if (row.no_addresses == 0) {
+ temp = g_strdup_printf("<b>%s</b>", row.display_name);
+ } else {
+ temp = g_strdup_printf("<b>%s</b>\n<span size='small'>%s</span>", row.display_name, row.addresses);
+ }
+ for (list=row.links; list!=NULL; list=g_list_next(list))
+ {
+ link = (link_row*)(list->data);
+ if (link->dlt == row.active_dlt) {
+ break;
+ }
+ }
+ if (row.has_snaplen) {
+ snaplen_string = g_strdup_printf("%d", row.snaplen);
+ } else {
+ snaplen_string = g_strdup("default");
+ }
+ if (cap_open_w) {
+ if_cb = (GtkTreeView *) g_object_get_data(G_OBJECT(cap_open_w), E_CAP_IFACE_KEY);
+ path_str = g_strdup_printf("%d", marked_row);
+ path = gtk_tree_path_new_from_string(path_str);
+ model = gtk_tree_view_get_model(if_cb);
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get(model, &iter, CAPTURE, &enabled, -1);
+ if (enabled == FALSE) {
+ num_selected++;
+ }
+
+#if defined(HAVE_PCAP_CREATE)
+ gtk_list_store_set (GTK_LIST_STORE(model), &iter, CAPTURE, TRUE, INTERFACE, temp, LINK, link->name, PMODE, row.pmode?"enabled":"disabled", SNAPLEN, snaplen_string, BUFFER, (guint) row.buffer, MONITOR, row.monitor_mode_supported?(row.monitor_mode_enabled?"enabled":"disabled"):"n/a", FILTER, row.cfilter, -1);
+#elif defined(_WIN32) && !defined(HAVE_PCAP_CREATE)
+ gtk_list_store_set (GTK_LIST_STORE(model), &iter, CAPTURE, TRUE, INTERFACE, temp,LINK, link->name, PMODE, row.pmode?"enabled":"disabled", SNAPLEN, snaplen_string, BUFFER, (guint) row.buffer, FILTER, row.cfilter, -1);
+#else
+ gtk_list_store_set (GTK_LIST_STORE(model), &iter, CAPTURE, TRUE, INTERFACE, temp,LINK, link->name, PMODE, row.pmode?"enabled":"disabled", SNAPLEN, snaplen_string, FILTER, row.cfilter, -1);
+#endif
+ if (num_selected > 0) {
+ gtk_widget_set_sensitive(ok_bt, TRUE);
+ } else {
+ gtk_widget_set_sensitive(ok_bt, FALSE);
+ }
+ gtk_tree_path_free (path);
+ }
+ if (interfaces_dialog_window_present()) {
+ update_selected_interface(g_strdup(row.name), TRUE);
+ }
+ if (get_welcome_window() != NULL) {
+ change_interface_selection(g_strdup(row.name), TRUE);
+ }
+}
+
+static void
+save_options_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+ GtkWidget *snap_cb, *snap_sb, *promisc_cb,
+#ifdef HAVE_PCAP_CREATE
+ *monitor_cb,
+#endif
+ *filter_cm, *linktype_combo_box;
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ GtkWidget *buffer_size_sb;
+#endif
+
+ interface_row row;
+ gpointer ptr;
+ int dlt;
+ const gchar *filter_text;
+
+ row = g_array_index(rows, interface_row, marked_row);
+ rows = g_array_remove_index(rows, marked_row);
+ snap_cb = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CAP_SNAP_CB_KEY);
+ snap_sb = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CAP_SNAP_SB_KEY);
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ buffer_size_sb = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CAP_BUFFER_SIZE_SB_KEY);
+#endif
+ promisc_cb = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CAP_PROMISC_KEY);
+#ifdef HAVE_PCAP_CREATE
+ monitor_cb = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CAP_MONITOR_KEY);
+#endif
+ filter_cm = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CFILTER_CM_KEY);
+
+ linktype_combo_box = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CAP_LT_CBX_KEY);
+
+ if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(linktype_combo_box), &ptr)) {
+ g_assert_not_reached(); /* Programming error: somehow nothing is active */
+ }
+ if ((dlt = GPOINTER_TO_INT(ptr)) == -1) {
+ g_assert_not_reached(); /* Programming error: somehow managed to select an "unsupported" entry */
+ }
+ row.active_dlt = dlt;
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ row.buffer = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(buffer_size_sb));
+#endif
+ row.pmode = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(promisc_cb));
+ row.has_snaplen = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(snap_cb));
+ if (row.has_snaplen) {
+ row.snaplen = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(snap_sb));
+ if (row.snaplen < 1)
+ row.snaplen = WTAP_MAX_PACKET_SIZE;
+ else if (row.snaplen < MIN_PACKET_SIZE)
+ row.snaplen = MIN_PACKET_SIZE;
+ } else {
+ row.snaplen = WTAP_MAX_PACKET_SIZE;
+ }
+
+ filter_text = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(filter_cm));
+ if (row.cfilter)
+ g_free(row.cfilter);
+ g_assert(filter_text != NULL);
+ row.cfilter = g_strdup(filter_text);
+#ifdef HAVE_PCAP_CREATE
+ row.monitor_mode_enabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(monitor_cb));
+#endif
+ g_array_insert_val(rows, marked_row, row);
+ window_destroy(opt_edit_w);
+ update_options_table(marked_row);
+}
+
+static void
+adjust_snap_sensitivity(GtkWidget *tb _U_, gpointer parent_w _U_)
+{
+ GtkWidget *snap_cb, *snap_sb;
+ interface_row row;
+
+ row = g_array_index(rows, interface_row, marked_row);
+ rows = g_array_remove_index(rows, marked_row);
+
+ snap_cb = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CAP_SNAP_CB_KEY);
+ snap_sb = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CAP_SNAP_SB_KEY);
+
+ /* The snapshot length spinbox is sensitive if the "Limit each packet
+ to" checkbox is on. */
+ gtk_widget_set_sensitive(GTK_WIDGET(snap_sb),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(snap_cb)));
+ row.has_snaplen = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(snap_cb));
+ g_array_insert_val(rows, marked_row, row);
+}
+
+void options_interface_cb(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *column _U_, gpointer userdata)
+{
+ GtkWidget *caller, *window, *swindow=NULL,
+ *main_vb, *if_hb, *if_lb, *if_lb_name,
+ *main_hb, *left_vb,
+#if defined (HAVE_AIRPCAP) || defined (HAVE_PCAP_REMOTE) || defined (HAVE_PCAP_CREATE)
+ *right_vb,
+#endif
+ *capture_fr, *capture_vb,
+ *if_ip_hb, *if_ip_lb = NULL, *if_ip_name,
+ *if_vb_left, *if_vb_right,
+ *linktype_hb, *linktype_lb, *linktype_combo_box,
+ *snap_hb, *snap_cb, *snap_sb, *snap_lb,
+ *promisc_cb,
+#ifdef HAVE_PCAP_CREATE
+ *monitor_cb,
+#endif
+ *filter_hb, *filter_bt, *filter_te, *filter_cm,
+#if defined(HAVE_PCAP_OPEN_DEAD) && defined(HAVE_BPF_IMAGE)
+ *compile_bt,
+#endif
+ *bbox, *ok_bt, *cancel_bt,
+ *help_bt;
+ GList *cf_entry, *list, *cfilter_list;
+ GtkAdjustment *snap_adj;
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ GtkAdjustment *buffer_size_adj;
+ GtkWidget *buffer_size_lb, *buffer_size_sb, *buffer_size_hb;
+#endif
+#ifdef HAVE_PCAP_REMOTE
+ GtkWidget *remote_bt;
+#endif
+ #ifdef HAVE_AIRPCAP
+ GtkWidget *advanced_bt;
+#endif
+ interface_row row;
+ displayed_interface d_interface;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ link_row *temp;
+ gboolean found = FALSE;
+ gint num_supported_link_types;
+ guint i;
+ gchar *tok;
+
+ window = (GtkWidget *)userdata;
+ caller = gtk_widget_get_toplevel(GTK_WIDGET(window));
+ opt_edit_w = (GtkWidget *)g_object_get_data(G_OBJECT(caller), E_OPT_EDIT_DIALOG_PTR_KEY);
+ if (opt_edit_w != NULL) {
+ reactivate_window(opt_edit_w);
+ return;
+ }
+
+ row.name = NULL;
+ row.display_name = NULL;
+ row.no_addresses = 0;
+ row.addresses = NULL;
+ row.links = NULL;
+ row.active_dlt = -1;
+ row.pmode = FALSE;
+#ifdef HAVE_PCAP_CREATE
+ row.monitor_mode_enabled = FALSE;
+ row.monitor_mode_supported = FALSE;
+#endif
+ row.has_snaplen = FALSE;
+ row.snaplen = 65535;
+ row.cfilter = NULL;
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ row.buffer = 1;
+#endif
+
+ model = gtk_tree_view_get_model(view);
+ gtk_tree_model_get_iter (model, &iter, path);
+ marked_row = atoi(gtk_tree_path_to_string(path));
+
+ if (cap_open_w) {
+ row = g_array_index(rows, interface_row, marked_row);
+ } else if (get_welcome_window() != NULL) {
+ d_interface = get_interface_data(marked_row);
+ if (!rows || rows->len == 0) {
+ make_and_fill_rows();
+ }
+ for (i = 0; i < rows->len; i++) {
+ row = g_array_index(rows, interface_row, i);
+ if (strcmp(row.name, (char*)d_interface.name)==0) {
+ marked_row = i;
+ break;
+ }
+ }
+ }
+ opt_edit_w = dlg_window_new("Edit Interface Settings");
+ g_object_set_data(G_OBJECT(opt_edit_w), E_OPT_EDIT_CALLER_PTR_KEY, caller);
+ g_object_set_data(G_OBJECT(caller), E_OPT_EDIT_DIALOG_PTR_KEY, opt_edit_w);
+
+ main_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(opt_edit_w), main_vb);
+
+ /* Capture-related options frame */
+ capture_fr = gtk_frame_new("Capture");
+ gtk_container_add(GTK_CONTAINER(main_vb), capture_fr);
+
+ capture_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(capture_vb), 5);
+ gtk_container_add(GTK_CONTAINER(capture_fr), capture_vb);
+
+ /* Interface row */
+ if_hb = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(capture_vb), if_hb, FALSE, FALSE, 3);
+
+ if_lb = gtk_label_new("Interface: ");
+ gtk_box_pack_start(GTK_BOX(if_hb), if_lb, FALSE, FALSE, 3);
+
+ if_lb_name = gtk_label_new(row.display_name);
+ gtk_box_pack_start(GTK_BOX(if_hb), if_lb_name, FALSE, FALSE, 3);
+
+ /* IP addresses row */
+ if_ip_hb = gtk_hbox_new(FALSE, 3);
+
+ gtk_widget_set_tooltip_text(if_ip_hb, "Lists the IP address(es) "
+ "assigned to the selected interface. ");
+ if_vb_left = gtk_vbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(if_ip_hb), if_vb_left, FALSE, FALSE, 3);
+ if_vb_right = gtk_vbox_new(FALSE, 3);
+
+ if_ip_lb = gtk_label_new("IP address:");
+ gtk_misc_set_alignment(GTK_MISC(if_ip_lb), 0, 0); /* Left justified */
+ gtk_box_pack_start(GTK_BOX(if_vb_left), if_ip_lb, FALSE, FALSE, 0);
+
+ if (row.no_addresses > 0) {
+ GtkWidget *if_ip_list = gtk_vbox_new(FALSE, 0);
+ gchar *temp_addresses = g_strdup(row.addresses);
+ gtk_box_pack_start(GTK_BOX(capture_vb), if_ip_hb, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(if_ip_hb), if_vb_right, TRUE, TRUE, 0);
+ swindow = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swindow), GTK_SHADOW_NONE);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_widget_set_size_request(GTK_WIDGET(swindow), -1, 50);
+ for (tok = strtok (temp_addresses, "\n"); tok; tok = strtok(NULL, "\n")) {
+ if_ip_name = gtk_label_new(tok);
+ gtk_misc_set_alignment(GTK_MISC(if_ip_name), 0, 0); /* Left justified */
+ gtk_box_pack_start(GTK_BOX(if_ip_list), if_ip_name, FALSE, FALSE, 3);
+ }
+ gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(swindow), if_ip_list);
+ gtk_box_pack_start(GTK_BOX(if_vb_right), swindow, TRUE, TRUE, 0);
+ g_free(temp_addresses);
+ } else {
+ gtk_box_pack_start(GTK_BOX(capture_vb), if_ip_hb, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(if_ip_hb), if_vb_right, FALSE, FALSE, 3);
+ if_ip_name = gtk_label_new("none");
+ gtk_misc_set_alignment(GTK_MISC(if_ip_name), 0, 0); /* Left justified */
+ gtk_box_pack_start(GTK_BOX(if_vb_right), if_ip_name, FALSE, FALSE, 0);
+ }
+ main_hb = gtk_hbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(main_hb), 0);
+ gtk_box_pack_start(GTK_BOX(capture_vb), main_hb, FALSE, FALSE, 3);
+
+ left_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(left_vb), 0);
+ gtk_box_pack_start(GTK_BOX(main_hb), left_vb, TRUE, TRUE, 0);
+
+#if defined (HAVE_AIRPCAP) || defined (HAVE_PCAP_REMOTE) || defined (HAVE_PCAP_CREATE)
+ /* Avoid adding the right vbox if not needed, because it steals 3 pixels */
+ right_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(right_vb), 0);
+ gtk_box_pack_start(GTK_BOX(main_hb), right_vb, FALSE, FALSE, 3);
+#endif
+
+ /* Linktype row */
+ linktype_hb = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(left_vb), linktype_hb, FALSE, FALSE, 0);
+
+ linktype_lb = gtk_label_new("Link-layer header type:");
+ gtk_box_pack_start(GTK_BOX(linktype_hb), linktype_lb, FALSE, FALSE, 3);
+
+ linktype_combo_box = ws_combo_box_new_text_and_pointer();
+ g_object_set_data(G_OBJECT(linktype_combo_box), E_CAP_LT_CBX_LABEL_KEY, linktype_lb);
+ /* Default to "use the default" */
+ /* Datalink menu index is not reset; it will be restored with last used value */
+
+ g_object_set_data(G_OBJECT(linktype_combo_box), E_CAP_IFACE_IP_KEY, if_ip_lb);
+ if (cap_settings_history == NULL) {
+ cap_settings_history = g_hash_table_new(g_str_hash, g_str_equal);
+ }
+ /*
+ * XXX - in some cases, this is "multiple link-layer header types", e.g.
+ * some 802.11 interfaces on FreeBSD 5.2 and later, where you can request
+ * fake Ethernet, 802.11, or 802.11-plus-radio-information headers.
+ *
+ * In other cases, it's "multiple link-layer types", e.g., with recent
+ * versions of libpcap, a DAG card on an "HDLC" WAN, where you can
+ * request Cisco HDLC or PPP depending on what type of traffic is going
+ * over the WAN, or an Ethernet interface, where you can request Ethernet
+ * or DOCSIS, the latter being for some Cisco cable modem equipment that
+ * can be configured to send raw DOCSIS frames over an Ethernet inside
+ * Ethernet low-level framing, for traffic capture purposes.
+ *
+ * We leave it as "multiple link-layer types" for now.
+ */
+ gtk_widget_set_tooltip_text(linktype_combo_box, "The selected interface supports multiple link-layer types; select the desired one.");
+ gtk_box_pack_start (GTK_BOX(linktype_hb), linktype_combo_box, FALSE, FALSE, 0);
+ g_object_set_data(G_OBJECT(opt_edit_w), E_CAP_LT_CBX_KEY, linktype_combo_box);
+ num_supported_link_types = 0;
+ for (list=row.links; list!=NULL; list=g_list_next(list))
+ {
+ temp = (link_row*)(list->data);
+ ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(linktype_combo_box),
+ temp->name,
+ GINT_TO_POINTER(temp->dlt) /* Flag as "not supported" */
+ );
+ num_supported_link_types++;
+ if (temp->dlt == row.active_dlt) {
+ ws_combo_box_set_active(GTK_COMBO_BOX(linktype_combo_box), num_supported_link_types - 1);
+ found = TRUE;
+ }
+ }
+ gtk_widget_set_sensitive(linktype_lb, num_supported_link_types >= 2);
+ gtk_widget_set_sensitive(linktype_combo_box, num_supported_link_types >= 2);
+ if (!found) {
+ ws_combo_box_set_active(GTK_COMBO_BOX(linktype_combo_box),0);
+ }
+ g_signal_connect(linktype_combo_box, "changed", G_CALLBACK(select_link_type_cb), NULL);
+
+ /* Promiscuous mode row */
+ promisc_cb = gtk_check_button_new_with_mnemonic(
+ "Capture packets in _promiscuous mode");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(promisc_cb),
+ row.pmode);
+ gtk_widget_set_tooltip_text(promisc_cb,
+ "Usually a network adapter will only capture the traffic sent to its own network address. "
+ "If you want to capture all traffic that the network adapter can \"see\", mark this option. "
+ "See the FAQ for some more details of capturing packets from a switched network.");
+ gtk_box_pack_start (GTK_BOX(left_vb), promisc_cb, FALSE, FALSE, 0);
+ g_object_set_data(G_OBJECT(opt_edit_w), E_CAP_PROMISC_KEY, promisc_cb);
+
+#ifdef HAVE_PCAP_CREATE
+ /* Monitor mode row */
+ monitor_cb = gtk_check_button_new_with_mnemonic( "Capture packets in monitor mode");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(monitor_cb), row.monitor_mode_enabled);
+ gtk_widget_set_sensitive(monitor_cb, row.monitor_mode_supported);
+ g_signal_connect(monitor_cb, "toggled", G_CALLBACK(capture_prep_monitor_changed_cb), NULL);
+
+ gtk_widget_set_tooltip_text(monitor_cb,
+ "Usually a Wi-Fi adapter will, even in promiscuous mode, only capture the traffic on the BSS to which it's associated. "
+ "If you want to capture all traffic that the Wi-Fi adapter can \"receive\", mark this option. "
+ "In order to see IEEE 802.11 headers or to see radio information for captured packets,"
+ "it might be necessary to turn this option on.\n\n"
+ "Note that, in monitor mode, the adapter might disassociate from the network to which it's associated.");
+ gtk_box_pack_start (GTK_BOX(left_vb), monitor_cb, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(opt_edit_w), E_CAP_MONITOR_KEY, monitor_cb);
+#endif
+
+ /*
+ * This controls the sensitivity of both the link-type list and, if
+ * you have it, the monitor mode checkbox. That's why we do this
+ * now.
+ */
+
+ /* Capture length row */
+ snap_hb = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start (GTK_BOX(left_vb), snap_hb, FALSE, FALSE, 0);
+
+ snap_cb = gtk_check_button_new_with_mnemonic("_Limit each packet to");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(snap_cb),
+ row.has_snaplen);
+ g_signal_connect(snap_cb, "toggled", G_CALLBACK(adjust_snap_sensitivity), NULL);
+ gtk_widget_set_tooltip_text(snap_cb,
+ "Limit the maximum number of bytes to be captured from each packet. This size includes the "
+ "link-layer header and all subsequent headers. ");
+ gtk_box_pack_start(GTK_BOX(snap_hb), snap_cb, FALSE, FALSE, 0);
+
+ snap_adj = (GtkAdjustment *) gtk_adjustment_new((gfloat) row.snaplen,
+ MIN_PACKET_SIZE, WTAP_MAX_PACKET_SIZE, 1.0, 10.0, 0.0);
+ snap_sb = gtk_spin_button_new (snap_adj, 0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (snap_sb), TRUE);
+ gtk_widget_set_size_request(snap_sb, 80, -1);
+ gtk_box_pack_start (GTK_BOX(snap_hb), snap_sb, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(opt_edit_w), E_CAP_SNAP_CB_KEY, snap_cb);
+ g_object_set_data(G_OBJECT(opt_edit_w), E_CAP_SNAP_SB_KEY, snap_sb);
+ snap_lb = gtk_label_new("bytes");
+ gtk_misc_set_alignment(GTK_MISC(snap_lb), 0, 0.5f);
+ gtk_box_pack_start(GTK_BOX(snap_hb), snap_lb, FALSE, FALSE, 0);
+ gtk_widget_set_sensitive(GTK_WIDGET(snap_sb), row.has_snaplen);
+
+ /* Filter row */
+ filter_hb = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(capture_vb), filter_hb, FALSE, FALSE, 0);
+
+ filter_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_CAPTURE_FILTER_ENTRY);
+ g_signal_connect(filter_bt, "clicked", G_CALLBACK(capture_filter_construct_cb), NULL);
+ g_signal_connect(filter_bt, "destroy", G_CALLBACK(filter_button_destroy_cb), NULL);
+ gtk_widget_set_tooltip_text(filter_bt,
+ "Select a capture filter to reduce the amount of packets to be captured. "
+ "See \"Capture Filters\" in the online help for further information how to use it."
+ );
+ gtk_box_pack_start(GTK_BOX(filter_hb), filter_bt, FALSE, FALSE, 3);
+
+ /* Create the capture filter combo box*/
+ filter_cm = gtk_combo_box_text_new_with_entry();
+ cfilter_list = (GList *)g_object_get_data(G_OBJECT(opt_edit_w), E_CFILTER_FL_KEY);
+ g_object_set_data(G_OBJECT(opt_edit_w), E_CFILTER_FL_KEY, cfilter_list);
+ g_object_set_data(G_OBJECT(opt_edit_w), E_CFILTER_CM_KEY, filter_cm);
+ filter_te = gtk_bin_get_child(GTK_BIN(filter_cm));
+ colorize_filter_te_as_empty(filter_te);
+ g_signal_connect(filter_te, "changed", G_CALLBACK(capture_filter_check_syntax_cb), NULL);
+ g_signal_connect(filter_te, "destroy", G_CALLBACK(capture_filter_destroy_cb), NULL);
+
+ for (cf_entry = cfilter_list; cf_entry != NULL; cf_entry = g_list_next(cf_entry)) {
+ if (cf_entry->data && (strlen((const char *)cf_entry->data) > 0)) {
+ gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(filter_cm), (const gchar *)cf_entry->data);
+ }
+ }
+ if (global_capture_opts.default_options.cfilter && (strlen(global_capture_opts.default_options.cfilter) > 0)) {
+ gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(filter_cm), global_capture_opts.default_options.cfilter);
+ }
+ if (row.cfilter && (strlen(row.cfilter) > 0)) {
+ gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(filter_cm), row.cfilter);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(filter_cm), 0);
+ }
+
+ gtk_widget_set_tooltip_text(filter_cm,
+ "Enter a capture filter to reduce the amount of packets to be captured. "
+ "See \"Capture Filters\" in the online help for further information how to use it. "
+ "Syntax checking can be disabled in Preferences -> Capture -> Syntax check capture filter."
+ );
+ gtk_box_pack_start(GTK_BOX(filter_hb), filter_cm, TRUE, TRUE, 3);
+
+ /* let an eventually capture filters dialog know the text entry to fill in */
+ g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
+
+#if defined(HAVE_PCAP_OPEN_DEAD) && defined(HAVE_BPF_IMAGE)
+ compile_bt = gtk_button_new_with_label("Compile BPF");
+ g_signal_connect(compile_bt, "clicked", G_CALLBACK(capture_filter_compile_cb), NULL);
+ gtk_widget_set_tooltip_text(compile_bt,
+ "Compile the capture filter expression and show the BPF (Berkeley Packet Filter) code.");
+ gtk_box_pack_start(GTK_BOX(filter_hb), compile_bt, FALSE, FALSE, 3);
+#endif
+
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ buffer_size_hb = gtk_hbox_new(FALSE, 3);
+ buffer_size_lb = gtk_label_new("Buffer size:");
+ gtk_box_pack_start (GTK_BOX(buffer_size_hb), buffer_size_lb, FALSE, FALSE, 0);
+
+ buffer_size_adj = (GtkAdjustment *) gtk_adjustment_new((gfloat) row.buffer,
+ 1, 65535, 1.0, 10.0, 0.0);
+ buffer_size_sb = gtk_spin_button_new (buffer_size_adj, 0, 0);
+ gtk_spin_button_set_value(GTK_SPIN_BUTTON (buffer_size_sb), (gfloat) row.buffer);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (buffer_size_sb), TRUE);
+ gtk_widget_set_size_request(buffer_size_sb, 80, -1);
+ gtk_widget_set_tooltip_text(buffer_size_sb,
+ "The memory buffer size used while capturing. If you notice packet drops, you can try to increase this size.");
+ gtk_box_pack_start (GTK_BOX(buffer_size_hb), buffer_size_sb, FALSE, FALSE, 0);
+ g_object_set_data(G_OBJECT(opt_edit_w), E_CAP_BUFFER_SIZE_SB_KEY, buffer_size_sb);
+ buffer_size_lb = gtk_label_new("megabyte(s)");
+ gtk_box_pack_start (GTK_BOX(buffer_size_hb), buffer_size_lb, FALSE, FALSE, 3);
+ gtk_misc_set_alignment(GTK_MISC(buffer_size_lb), 1, 0);
+#ifdef HAVE_PCAP_REMOTE
+ gtk_box_pack_start (GTK_BOX(left_vb), buffer_size_hb, FALSE, FALSE, 0);
+#else
+ gtk_box_pack_start (GTK_BOX(right_vb), buffer_size_hb, FALSE, FALSE, 0);
+#endif
+#endif
+
+#ifdef HAVE_PCAP_REMOTE
+ remote_bt = gtk_button_new_with_label("Remote Settings");
+ gtk_widget_set_tooltip_text(remote_bt, "Various settings for remote capture.");
+
+ /* Both the callback and the data are global */
+ g_signal_connect(remote_bt, "clicked", G_CALLBACK(options_remote_cb), NULL);
+ g_object_set_data(G_OBJECT(opt_edit_w), E_OPT_REMOTE_BT_KEY, remote_bt);
+ if (strncmp (row.name, "rpcap://", 8) == 0) {
+ gtk_widget_set_sensitive(remote_bt, TRUE);
+ } else {
+ gtk_widget_set_sensitive(remote_bt, FALSE);
+ }
+ gtk_box_pack_start(GTK_BOX(right_vb), remote_bt, FALSE, FALSE, 0);
+ gtk_widget_show(remote_bt);
+#endif
+ /* advanced row */
+#ifdef HAVE_AIRPCAP
+ advanced_bt = gtk_button_new_with_label("Wireless Settings");
+
+ /* Both the callback and the data are global */
+ g_signal_connect(advanced_bt,"clicked", G_CALLBACK(options_airpcap_advanced_cb), airpcap_tb);
+ g_object_set_data(G_OBJECT(top_level),AIRPCAP_OPTIONS_ADVANCED_KEY, advanced_bt);
+ airpcap_if_selected = get_airpcap_if_from_name(airpcap_if_list, row.name);
+ if (airpcap_if_selected != NULL) {
+ /* It is an airpcap interface */
+ gtk_widget_set_sensitive(advanced_bt, TRUE);
+ } else {
+ gtk_widget_set_sensitive(advanced_bt, FALSE);
+ }
+
+ gtk_box_pack_start(GTK_BOX(right_vb), advanced_bt, FALSE, FALSE, 0);
+ gtk_widget_show(advanced_bt);
+#endif
+
+/* Button row: "Start", "Cancel" and "Help" buttons */
+ bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 5);
+
+ ok_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(save_options_cb), NULL);
+ gtk_widget_set_tooltip_text(ok_bt,
+ "Accept interface settings.");
+ cancel_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ gtk_widget_set_tooltip_text(cancel_bt,
+ "Cancel and exit dialog.");
+ window_set_cancel_button(opt_edit_w, cancel_bt, window_cancel_button_cb);
+ help_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ gtk_widget_set_tooltip_text(help_bt,
+ "Show help about capturing.");
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_CAPTURE_OPTIONS_DIALOG);
+ gtk_widget_grab_default(ok_bt);
+ dlg_set_activate(filter_te, ok_bt);
+ gtk_widget_grab_focus(filter_te);
+ g_signal_connect(opt_edit_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(opt_edit_w, "destroy", G_CALLBACK(options_edit_destroy_cb), NULL);
+ gtk_widget_show_all(opt_edit_w);
+ window_present(opt_edit_w);
+}
+
+static void toggle_callback(GtkCellRendererToggle *cell _U_,
+ gchar *path_str,
+ gpointer data _U_)
+{
+ /* get the treemodel from somewhere */
+ GtkTreeIter iter;
+ GtkTreeView *if_cb;
+ GtkTreeModel *model;
+ GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
+ gboolean enabled, found = FALSE;
+ GtkWidget *pcap_ng_cb;
+ interface_options interface_opts;
+ interface_row row;
+ int index = atoi(path_str);
+ guint i;
+
+ if_cb = (GtkTreeView *) g_object_get_data(G_OBJECT(cap_open_w), E_CAP_IFACE_KEY);
+ model = gtk_tree_view_get_model(if_cb);
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter, CAPTURE, &enabled, -1);
+ row = g_array_index(rows, interface_row, index);
+ if (enabled == FALSE)
+ num_selected++;
+ else
+ num_selected--;
+ enabled ^= 1;
+ pcap_ng_cb = (GtkWidget *) g_object_get_data(G_OBJECT(cap_open_w), E_CAP_PCAP_NG_KEY);
+ if (num_selected >= 2) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pcap_ng_cb), TRUE);
+ gtk_widget_set_sensitive(pcap_ng_cb, FALSE);
+ } else {
+ gtk_widget_set_sensitive(pcap_ng_cb, TRUE);
+ }
+ if (num_selected > 0) {
+ gtk_widget_set_sensitive(ok_bt, TRUE);
+ } else {
+ gtk_widget_set_sensitive(ok_bt, FALSE);
+ }
+ /* do something with the new enabled value, and set the new
+ enabled value in your treemodel */
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, CAPTURE, enabled, -1);
+
+ if (global_capture_opts.ifaces->len > 0) {
+ for (i = 0; i < global_capture_opts.ifaces->len; i++) {
+ interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, i);
+ if (strcmp(interface_opts.name, row.name) == 0) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (found) {
+ global_capture_opts.ifaces = g_array_remove_index(global_capture_opts.ifaces, i);
+ g_free(interface_opts.cfilter);
+ if (enabled) {
+ interface_opts.linktype = row.active_dlt;
+ interface_opts.promisc_mode = row.pmode;
+ interface_opts.has_snaplen = row.has_snaplen;
+ interface_opts.snaplen = row.snaplen;
+ interface_opts.cfilter = g_strdup(row.cfilter);
+#ifdef HAVE_PCAP_CREATE
+ interface_opts.monitor_mode = row.monitor_mode_enabled;
+#else
+ interface_opts.monitor_mode = FALSE;
+#endif
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ interface_opts.buffer_size = row.buffer;
+#endif
+#ifdef HAVE_PCAP_REMOTE
+ interface_opts.src_type = row.remote_opts.src_type;
+ if (interface_opts.src_type == CAPTURE_IFREMOTE) {
+ interface_opts.remote_host = g_strdup(row.remote_opts.remote_host_opts.remote_host);
+ interface_opts.remote_port = g_strdup(row.remote_opts.remote_host_opts.remote_port);
+ interface_opts.auth_type = row.remote_opts.remote_host_opts.auth_type;
+ interface_opts.auth_username = g_strdup(row.remote_opts.remote_host_opts.auth_username);
+ interface_opts.auth_password = g_strdup(row.remote_opts.remote_host_opts.auth_password);
+ interface_opts.datatx_udp = row.remote_opts.remote_host_opts.datatx_udp;
+ interface_opts.nocap_rpcap = row.remote_opts.remote_host_opts.nocap_rpcap;
+ interface_opts.nocap_local = row.remote_opts.remote_host_opts.nocap_local;
+#ifdef HAVE_PCAP_SETSAMPLING
+ interface_opts.sampling_method = row.remote_opts.sampling_method;
+ interface_opts.sampling_param = row.remote_opts.sampling_param;
+#endif
+ } else {
+ interface_opts.remote_host = g_strdup(global_capture_opts.default_options.remote_host);
+ interface_opts.remote_port = g_strdup(global_capture_opts.default_options.remote_port);
+ interface_opts.auth_type = global_capture_opts.default_options.auth_type;
+ interface_opts.auth_username = g_strdup(global_capture_opts.default_options.auth_username);
+ interface_opts.auth_password = g_strdup(global_capture_opts.default_options.auth_password);
+ interface_opts.datatx_udp = global_capture_opts.default_options.datatx_udp;
+ interface_opts.nocap_rpcap = global_capture_opts.default_options.nocap_rpcap;
+ interface_opts.nocap_local = global_capture_opts.default_options.nocap_local;
+#ifdef HAVE_PCAP_SETSAMPLING
+ interface_opts.sampling_method = global_capture_opts.default_options.sampling_method;
+ interface_opts.sampling_param = global_capture_opts.default_options.sampling_param;
+#endif
+ }
+#endif
+ g_array_insert_val(global_capture_opts.ifaces, i, interface_opts);
+ } else { /* not enabled */
+ if (interfaces_dialog_window_present()) {
+ update_selected_interface(g_strdup(interface_opts.name), FALSE);
+ }
+ if (get_welcome_window() != NULL) {
+ change_interface_selection(g_strdup(interface_opts.name), FALSE);
+ }
+ }
+ }
+ }
+ if (!found && enabled) {
+ interface_opts.name = g_strdup(row.name);
+ interface_opts.descr = get_interface_descriptive_name(interface_opts.name);
+ interface_opts.linktype = row.active_dlt;
+ interface_opts.promisc_mode = row.pmode;
+ interface_opts.has_snaplen = row.has_snaplen;
+ interface_opts.snaplen = row.snaplen;
+ interface_opts.cfilter = g_strdup(row.cfilter);
+#ifdef HAVE_PCAP_CREATE
+ interface_opts.monitor_mode = row.monitor_mode_enabled;
+#else
+ interface_opts.monitor_mode = FALSE;
+#endif
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ interface_opts.buffer_size = row.buffer;
+#endif
+#ifdef HAVE_PCAP_REMOTE
+ interface_opts.src_type = row.remote_opts.src_type;
+ if (interface_opts.src_type == CAPTURE_IFREMOTE) {
+ interface_opts.remote_host = g_strdup(row.remote_opts.remote_host_opts.remote_host);
+ interface_opts.remote_port = g_strdup(row.remote_opts.remote_host_opts.remote_port);
+ interface_opts.auth_type = row.remote_opts.remote_host_opts.auth_type;
+ interface_opts.auth_username = g_strdup(row.remote_opts.remote_host_opts.auth_username);
+ interface_opts.auth_password = g_strdup(row.remote_opts.remote_host_opts.auth_password);
+ interface_opts.datatx_udp = row.remote_opts.remote_host_opts.datatx_udp;
+ interface_opts.nocap_rpcap = row.remote_opts.remote_host_opts.nocap_rpcap;
+ interface_opts.nocap_local = row.remote_opts.remote_host_opts.nocap_local;
+#ifdef HAVE_PCAP_SETSAMPLING
+ interface_opts.sampling_method = row.remote_opts.sampling_method;
+ interface_opts.sampling_param = row.remote_opts.sampling_param;
+#endif
+ } else {
+ interface_opts.remote_host = g_strdup(global_capture_opts.default_options.remote_host);
+ interface_opts.remote_port = g_strdup(global_capture_opts.default_options.remote_port);
+ interface_opts.auth_type = global_capture_opts.default_options.auth_type;
+ interface_opts.auth_username = g_strdup(global_capture_opts.default_options.auth_username);
+ interface_opts.auth_password = g_strdup(global_capture_opts.default_options.auth_password);
+ interface_opts.datatx_udp = global_capture_opts.default_options.datatx_udp;
+ interface_opts.nocap_rpcap = global_capture_opts.default_options.nocap_rpcap;
+ interface_opts.nocap_local = global_capture_opts.default_options.nocap_local;
+#ifdef HAVE_PCAP_SETSAMPLING
+ interface_opts.sampling_method = global_capture_opts.default_options.sampling_method;
+ interface_opts.sampling_param = global_capture_opts.default_options.sampling_param;
+#endif
+ }
+#endif
+ g_array_append_val(global_capture_opts.ifaces, interface_opts);
+ if (interfaces_dialog_window_present()) {
+ update_selected_interface(g_strdup(interface_opts.name), TRUE);
+ }
+ if (get_welcome_window() != NULL) {
+ change_interface_selection(g_strdup(interface_opts.name), TRUE);
+ }
+ }
+ gtk_tree_path_free (path);
+}
+
+void enable_selected_interface(gchar *name, gboolean enable)
+{
+ guint i;
+ interface_row row;
+ GtkTreeIter iter;
+ GtkTreeView *if_cb;
+ GtkTreeModel *model;
+ gchar *path_str;
+ gboolean enabled;
+
+ for (i = 0; i < rows->len; i++) {
+ row = g_array_index(rows, interface_row, i);
+ if (strcmp(name, row.name) == 0) {
+ if_cb = (GtkTreeView *) g_object_get_data(G_OBJECT(cap_open_w), E_CAP_IFACE_KEY);
+ model = gtk_tree_view_get_model(if_cb);
+ path_str = g_strdup_printf("%d", i);
+ gtk_tree_model_get_iter_from_string(model, &iter, path_str);
+ gtk_tree_model_get (model, &iter, CAPTURE, &enabled, -1);
+ if ((enabled == TRUE) && (enable == FALSE)) {
+ num_selected--;
+ }
+ if ((enabled == FALSE) && (enable == TRUE)) {
+ num_selected++;
+ }
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, CAPTURE, enable, -1);
+ break;
+ }
+ }
+ if (num_selected > 0) {
+ gtk_widget_set_sensitive(ok_bt, TRUE);
+ } else {
+ gtk_widget_set_sensitive(ok_bt, FALSE);
+ }
+}
+
+
+static void capture_all_cb(GtkToggleButton *button, gpointer d _U_)
+{
+ GtkTreeIter iter;
+ GtkTreeView *if_cb;
+ GtkTreeModel *model;
+ GtkWidget *pcap_ng_cb;
+ gboolean enabled = FALSE, capture_set = FALSE;
+
+ if (gtk_toggle_button_get_active(button))
+ enabled = TRUE;
+ if_cb = (GtkTreeView *) g_object_get_data(G_OBJECT(cap_open_w), E_CAP_IFACE_KEY);
+ model = gtk_tree_view_get_model(if_cb);
+ pcap_ng_cb = (GtkWidget *) g_object_get_data(G_OBJECT(cap_open_w), E_CAP_PCAP_NG_KEY);
+ if (gtk_tree_model_get_iter_first(model, &iter)) {
+ do {
+ gtk_tree_model_get (model, &iter, CAPTURE, &capture_set, -1);
+ if (!capture_set && enabled) {
+ num_selected++;
+ } else if (capture_set && !enabled) {
+ num_selected--;
+ }
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, CAPTURE, enabled, -1);
+ } while (gtk_tree_model_iter_next(model, &iter));
+ }
+ if (num_selected >= 2) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pcap_ng_cb), TRUE);
+ gtk_widget_set_sensitive(pcap_ng_cb, FALSE);
+ } else if (num_selected <= 1) {
+ gtk_widget_set_sensitive(pcap_ng_cb, TRUE);
+ }
+ if (interfaces_dialog_window_present()) {
+ select_all_interfaces(enabled);
+ }
+ if (get_welcome_window() != NULL) {
+ change_selection_for_all(enabled);
+ }
+ if (num_selected > 0) {
+ gtk_widget_set_sensitive(ok_bt, TRUE);
+ } else {
+ gtk_widget_set_sensitive(ok_bt, FALSE);
+ }
+}
+
+
+static void promisc_mode_callback(GtkToggleButton *button, gpointer d _U_)
+{
+ GtkTreeIter iter;
+ GtkTreeView *if_cb;
+ GtkTreeModel *model;
+ gboolean enabled = FALSE;
+ interface_row row;
+ interface_options interface_opts;
+ guint i;
+
+ if (gtk_toggle_button_get_active(button))
+ enabled = TRUE;
+
+ if_cb = (GtkTreeView *) g_object_get_data(G_OBJECT(cap_open_w), E_CAP_IFACE_KEY);
+ model = gtk_tree_view_get_model(if_cb);
+ if (gtk_tree_model_get_iter_first(model, &iter)) {
+ do {
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, PMODE, enabled?"enabled":"disabled", -1);
+ } while (gtk_tree_model_iter_next(model, &iter));
+ }
+
+ for (i = 0; i < rows->len; i++) {
+ row = g_array_index(rows, interface_row, i);
+ rows = g_array_remove_index(rows, i);
+ row.pmode = (enabled?TRUE:FALSE);
+ g_array_insert_val(rows, i, row);
+ }
+
+ for (i = 0; i < global_capture_opts.ifaces->len; i++) {
+ interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, i);
+ global_capture_opts.ifaces = g_array_remove_index(global_capture_opts.ifaces, i);
+ interface_opts.promisc_mode = (enabled?TRUE:FALSE);
+ g_array_insert_val(global_capture_opts.ifaces, i, interface_opts);
+ }
+}
+
+#if defined (HAVE_PCAP_REMOTE)
+void show_remote_dialog(GtkWidget *w)
+{
+
+ g_free(global_remote_opts.remote_host_opts.remote_host);
+ g_free(global_remote_opts.remote_host_opts.remote_port);
+ g_free(global_remote_opts.remote_host_opts.auth_username);
+ g_free(global_remote_opts.remote_host_opts.auth_password);
+ global_remote_opts.src_type = CAPTURE_IFREMOTE;
+ global_remote_opts.remote_host_opts.remote_host = g_strdup(global_capture_opts.default_options.remote_host);
+ global_remote_opts.remote_host_opts.remote_port = g_strdup(global_capture_opts.default_options.remote_port);
+ global_remote_opts.remote_host_opts.auth_type = global_capture_opts.default_options.auth_type;
+ global_remote_opts.remote_host_opts.auth_username = g_strdup(global_capture_opts.default_options.auth_username);
+ global_remote_opts.remote_host_opts.auth_password = g_strdup(global_capture_opts.default_options.auth_password);
+ global_remote_opts.remote_host_opts.datatx_udp = global_capture_opts.default_options.datatx_udp;
+ global_remote_opts.remote_host_opts.nocap_rpcap = global_capture_opts.default_options.nocap_rpcap;
+ global_remote_opts.remote_host_opts.nocap_local = global_capture_opts.default_options.nocap_local;
+#ifdef HAVE_PCAP_SETSAMPLING
+ global_remote_opts.sampling_method = global_capture_opts.default_options.sampling_method;
+ global_remote_opts.sampling_param = global_capture_opts.default_options.sampling_param;
+#endif
+ capture_remote_cb(GTK_WIDGET(w), FALSE);
+}
+#endif
+
+/* show capture prepare (options) dialog */
+
+/* XXX: Warning:
+ Note that capture_interface_list() is called directly (or indirectly) during the
+ creation of (and changes to) the capture options dialog window.
+
+ Also note that capture_interface_list() indirectly runs the gtk main loop temporarily
+ to process queued events (which may include button-presses, key-presses, etc).
+ (This is done while awaiting a response from dumpcap which is invoked to obtain
+ the capture interface list).
+ This means other Wireshark callbacks can be invoked while the capture options window
+ is being created or updated (in effect an "interrupt" can occur).
+
+ Needless to say, "race conditions" may occur in "interrupt" code which depends upon the exact
+ state of the capture options dialog window and which may be invoked during the
+ creation of (or changes to) the capture options dialog window.
+
+ For example: if a user hits "Capture:Options" and then immediately hits "Capture:Start",
+ capture_start_cb() may be invoked before capture_prep_cb() has been completed (i.e., during
+ a call to capture_interface_list() in the code which creates the capture options window).
+ capture_start_cb() depends upon certain properties of the capture options window having been
+ initialized and thus fails if the properties have not (yet) been initialized.
+
+ An interlock has been added to handle this particular situation;
+ Ideally a more general solution should be implemented since it's probably difficult
+ (if not nearly impossible) to identify all the possible "race conditions".
+
+ ? Prevent the temporary running of the gtk main loop in cases wherein dumpcap is invoked for a
+ simple request/reply ? (e.g., capture_interface_list()) ??
+
+ ? Other ??
+*/
+
+void
+capture_prep_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ GtkWidget *main_vb,
+ *main_hb, *left_vb, *right_vb,
+ *capture_fr, *capture_vb,
+ *all_hb, *all_cb,
+ *promisc_cb, *pcap_ng_cb,
+ *file_fr, *file_vb,
+ *file_hb, *file_bt, *file_lb, *file_te,
+ *multi_tb, *multi_files_on_cb,
+ *ring_filesize_cb, *ring_filesize_sb, *ring_filesize_cbx,
+ *file_duration_cb, *file_duration_sb, *file_duration_cbx,
+ *ringbuffer_nbf_cb, *ringbuffer_nbf_sb, *ringbuffer_nbf_lb,
+ *stop_files_cb, *stop_files_sb, *stop_files_lb,
+ *limit_fr, *limit_vb, *limit_tb,
+ *stop_packets_cb, *stop_packets_sb, *stop_packets_lb,
+ *stop_filesize_cb, *stop_filesize_sb, *stop_filesize_cbx,
+ *stop_duration_cb, *stop_duration_sb, *stop_duration_cbx,
+ *display_fr, *display_vb,
+ *sync_cb, *auto_scroll_cb, *hide_info_cb,
+ *resolv_fr, *resolv_vb,
+ *m_resolv_cb, *n_resolv_cb, *t_resolv_cb,
+ *bbox, *close_bt,
+ *help_bt;
+#ifdef HAVE_AIRPCAP
+ GtkWidget *decryption_cb;
+#endif
+#ifdef HAVE_PCAP_REMOTE
+ GtkWidget *iftype_cbx;
+#endif
+
+ GtkAdjustment *ringbuffer_nbf_adj,
+ *stop_packets_adj, *stop_filesize_adj, *stop_duration_adj, *stop_files_adj,
+ *ring_filesize_adj, *file_duration_adj;
+ GList *if_list;
+ int row;
+ int err;
+ gchar *err_str;
+ guint32 value;
+ gchar *cap_title;
+ GtkWidget *view;
+ GtkWidget *swindow;
+ GtkCellRenderer *renderer;
+ GtkCellRenderer *toggle_renderer;
+ GtkTreeSelection *selection;
+ GtkTreeViewColumn *column;
+ gboolean if_present = TRUE;
+
+ if (interfaces_dialog_window_present()) {
+ destroy_if_window();
+ }
+ if (cap_open_w != NULL) {
+ /* There's already a "Capture Options" dialog box; reactivate it. */
+ reactivate_window(cap_open_w);
+ return;
+ }
+
+ num_selected = 0;
+ /* use user-defined title if preference is set */
+
+ cap_title = create_user_window_title("Wireshark: Capture Options");
+
+ cap_open_complete = FALSE;
+ cap_open_w = dlg_window_new(cap_title);
+ g_free(cap_title);
+
+ if_list = capture_interface_list(&err, &err_str);
+
+ if (if_list == NULL &&
+ (err == CANT_GET_INTERFACE_LIST || err == DONT_HAVE_PCAP)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_str);
+ g_free(err_str);
+ }
+ if_list = g_list_sort (if_list, if_list_comparator_alph);
+#ifdef HAVE_AIRPCAP
+ /* update airpcap interface list */
+
+ /* load the airpcap interfaces */
+ airpcap_if_list = get_airpcap_interface_list(&err, &err_str);
+
+ decryption_cb = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_DECRYPTION_KEY);
+ update_decryption_mode_list(decryption_cb);
+
+ if (airpcap_if_list == NULL && err == CANT_GET_AIRPCAP_INTERFACE_LIST) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_str);
+ g_free(err_str);
+ }
+
+ /* select the first as default (THIS SHOULD BE CHANGED) */
+ airpcap_if_active = airpcap_get_default_if(airpcap_if_list);
+#endif
+
+ main_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(cap_open_w), main_vb);
+
+ /* Capture-related options frame */
+ capture_fr = gtk_frame_new("Capture");
+ gtk_container_add(GTK_CONTAINER(main_vb), capture_fr);
+
+ capture_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(capture_vb), 5);
+ gtk_container_add(GTK_CONTAINER(capture_fr), capture_vb);
+
+#if defined (HAVE_PCAP_REMOTE)
+ if (remote_host_list == NULL) {
+ remote_host_list = g_hash_table_new (g_str_hash, g_str_equal);
+ }
+#endif
+
+ swindow = gtk_scrolled_window_new (NULL, NULL);
+ gtk_widget_set_size_request(swindow, FALSE, 180);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swindow), GTK_SHADOW_IN);
+
+ view = gtk_tree_view_new ();
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW (view), TRUE);
+ g_signal_connect(view, "row-activated", G_CALLBACK(options_interface_cb), (gpointer)cap_open_w);
+ toggle_renderer = gtk_cell_renderer_toggle_new();
+ column = gtk_tree_view_column_new_with_attributes("Capture", GTK_CELL_RENDERER(toggle_renderer), "active", CAPTURE, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
+ g_signal_connect (G_OBJECT(toggle_renderer), "toggled", G_CALLBACK (toggle_callback), NULL);
+ g_object_set (GTK_TREE_VIEW(view), "has-tooltip", TRUE, NULL);
+ g_signal_connect (GTK_TREE_VIEW(view), "query-tooltip", G_CALLBACK (query_tooltip_tree_view_cb), NULL);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW (view), -1, "Interface", renderer, "markup", INTERFACE, NULL);
+ column = gtk_tree_view_get_column(GTK_TREE_VIEW (view), INTERFACE);
+ gtk_tree_view_column_set_min_width(column, 200);
+ gtk_tree_view_column_set_resizable(column, TRUE );
+ gtk_tree_view_column_set_alignment(column, 0.5);
+ g_object_set(G_OBJECT(renderer), "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes ("Link-layer header", renderer, "text", LINK, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
+ gtk_tree_view_column_set_resizable(gtk_tree_view_get_column(GTK_TREE_VIEW (view),LINK), TRUE );
+ gtk_tree_view_column_set_alignment(column, 0.5);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Prom. Mode", renderer, "text", PMODE, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
+ g_object_set(renderer, "xalign", 0.5, NULL);
+ gtk_tree_view_column_set_alignment(column, 0.5);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Snaplen [B]", renderer, "text", SNAPLEN, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
+ g_object_set(renderer, "xalign", 0.5, NULL);
+
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Buffer [MB]", renderer, "text", BUFFER, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
+ g_object_set(renderer, "xalign", 0.5, NULL);
+#endif
+
+#if defined (HAVE_PCAP_CREATE)
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes ("Mon. Mode", renderer, "text", MONITOR, NULL);
+ gtk_tree_view_column_set_cell_data_func(column, renderer, activate_monitor, NULL, FALSE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
+ g_object_set(renderer, "xalign", 0.5, NULL);
+#endif
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Capture Filter", renderer, "text", FILTER, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
+ gtk_tree_view_column_set_alignment(column, 0.5);
+ create_and_fill_model(GTK_TREE_VIEW(view));
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+ gtk_container_add (GTK_CONTAINER (swindow), view);
+ gtk_box_pack_start(GTK_BOX(capture_vb), swindow, TRUE, TRUE, 0);
+
+ free_interface_list(if_list);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_IFACE_KEY, view);
+
+ main_hb = gtk_hbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(main_hb), 3);
+ gtk_box_pack_start(GTK_BOX(capture_vb), main_hb, FALSE, FALSE, 0);
+ all_hb = gtk_hbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(all_hb), 0);
+ gtk_box_pack_start(GTK_BOX(main_hb), all_hb, TRUE, TRUE, 0);
+
+ left_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(left_vb), 0);
+ gtk_box_pack_start(GTK_BOX(all_hb), left_vb, TRUE, TRUE, 0);
+
+ right_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(right_vb), 0);
+ gtk_box_pack_start(GTK_BOX(all_hb), right_vb, FALSE, FALSE, 3);
+
+ all_cb = gtk_check_button_new_with_mnemonic( "Capture on all interfaces");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(all_cb), FALSE);
+ g_signal_connect(all_cb, "toggled", G_CALLBACK(capture_all_cb), NULL);
+ gtk_widget_set_tooltip_text(all_cb, "Activate the box to capture on all interfaces. "
+ "Deactivate it to capture on none and set the interfaces individually.");
+ gtk_box_pack_start(GTK_BOX(left_vb), all_cb, TRUE, TRUE, 0);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(all_cb), if_present);
+ /* Promiscuous mode row */
+ promisc_cb = gtk_check_button_new_with_mnemonic("Capture all in _promiscuous mode");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(promisc_cb),
+ global_capture_opts.default_options.promisc_mode);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(promisc_cb)))
+ promisc_mode_callback(GTK_TOGGLE_BUTTON(promisc_cb), NULL);
+ g_signal_connect(promisc_cb, "toggled", G_CALLBACK(promisc_mode_callback), NULL);
+
+ gtk_widget_set_tooltip_text(promisc_cb,
+ "Usually a network adapter will only capture the traffic sent to its own network address. "
+ "If you want to capture all traffic that all network adapters can \"see\", mark this option. "
+ "If you want to set this option on a per interface basis, unmark this button and set the "
+ "option individually."
+ "See the FAQ for some more details of capturing packets from a switched network.");
+#if defined (HAVE_PCAP_REMOTE)
+ gtk_box_pack_start(GTK_BOX(left_vb), promisc_cb, TRUE, TRUE, 0);
+#else
+ gtk_box_pack_start(GTK_BOX(right_vb), promisc_cb, TRUE, TRUE, 0);
+#endif
+ gtk_widget_set_sensitive(GTK_WIDGET(promisc_cb), if_present);
+
+#ifdef HAVE_PCAP_REMOTE
+ iftype_cbx = gtk_button_new_with_label("Add Remote Interfaces");
+ gtk_widget_set_tooltip_text(iftype_cbx, "Connect to remote host to get interfaces to capture from");
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_IFTYPE_CBX_KEY, iftype_cbx);
+
+ gtk_box_pack_start(GTK_BOX(right_vb), iftype_cbx, FALSE, FALSE, 0);
+ g_signal_connect(iftype_cbx, "clicked", G_CALLBACK(show_remote_dialog), iftype_cbx);
+ gtk_widget_show(iftype_cbx);
+#endif
+ main_hb = gtk_hbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(main_hb), 0);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_hb, FALSE, FALSE, 0);
+
+ left_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(left_vb), 0);
+ gtk_box_pack_start(GTK_BOX(main_hb), left_vb, TRUE, TRUE, 0);
+
+ right_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(right_vb), 0);
+ gtk_box_pack_start(GTK_BOX(main_hb), right_vb, FALSE, FALSE, 0);
+
+ /* Capture file-related options frame */
+ file_fr = gtk_frame_new("Capture File(s)");
+ gtk_container_add(GTK_CONTAINER(left_vb), file_fr);
+
+ file_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(file_vb), 5);
+ gtk_container_add(GTK_CONTAINER(file_fr), file_vb);
+
+ /* File row */
+ file_hb = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(file_vb), file_hb, FALSE, FALSE, 0);
+
+ file_lb = gtk_label_new("File:");
+ gtk_box_pack_start(GTK_BOX(file_hb), file_lb, FALSE, FALSE, 3);
+
+ file_te = gtk_entry_new();
+ gtk_widget_set_tooltip_text(file_te,
+ "Enter the file name to which captured data will be written. "
+ "If you don't enter something here, a temporary file will be used."
+ );
+ gtk_box_pack_start(GTK_BOX(file_hb), file_te, TRUE, TRUE, 3);
+
+ file_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_BROWSE);
+ gtk_widget_set_tooltip_text(file_bt,
+ "Select a file to which captured data will be written, "
+ "instead of entering the file name directly. "
+ );
+ gtk_box_pack_start(GTK_BOX(file_hb), file_bt, FALSE, FALSE, 0);
+
+ g_signal_connect(file_bt, "clicked", G_CALLBACK(capture_prep_file_cb), file_te);
+
+ /* multiple files table */
+ multi_tb = gtk_table_new(5, 3, FALSE);
+ gtk_table_set_row_spacings(GTK_TABLE(multi_tb), 1);
+ gtk_table_set_col_spacings(GTK_TABLE(multi_tb), 3);
+ gtk_box_pack_start(GTK_BOX(file_vb), multi_tb, FALSE, FALSE, 0);
+ row = 0;
+
+ /* multiple files row */
+ multi_files_on_cb = gtk_check_button_new_with_mnemonic("Use _multiple files");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(multi_files_on_cb),
+ global_capture_opts.multi_files_on);
+ g_signal_connect(multi_files_on_cb, "toggled", G_CALLBACK(capture_prep_adjust_sensitivity),
+ cap_open_w);
+ gtk_widget_set_tooltip_text(multi_files_on_cb,
+ "Instead of using a single capture file, multiple files will be created. "
+ "The generated file names will contain an incrementing number and the start time of the capture.");
+ gtk_table_attach_defaults(GTK_TABLE(multi_tb), multi_files_on_cb, 0, 1, row, row+1);
+
+ /* Pcap-NG row */
+ pcap_ng_cb = gtk_check_button_new_with_mnemonic("Use pcap-ng format");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pcap_ng_cb), global_capture_opts.use_pcapng);
+ gtk_widget_set_tooltip_text(pcap_ng_cb, "Capture packets in the next-generation capture file format. "
+ "This is still experimental.");
+ gtk_table_attach_defaults(GTK_TABLE(multi_tb), pcap_ng_cb, 2, 3, row, row+1);
+ row++;
+
+ /* Ring buffer filesize row */
+ ring_filesize_cb = gtk_check_button_new_with_label("Next file every");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ring_filesize_cb),
+ global_capture_opts.has_autostop_filesize || !global_capture_opts.has_file_duration);
+ g_signal_connect(ring_filesize_cb, "toggled", G_CALLBACK(capture_prep_adjust_sensitivity), cap_open_w);
+ gtk_widget_set_tooltip_text(ring_filesize_cb,
+ "If the selected file size is exceeded, capturing switches to the next file.\n"
+ "PLEASE NOTE: at least one of the \"Next file every\" options MUST be selected.");
+ gtk_table_attach_defaults(GTK_TABLE(multi_tb), ring_filesize_cb, 0, 1, row, row+1);
+
+ ring_filesize_adj = (GtkAdjustment *) gtk_adjustment_new(0.0,
+ 1, (gfloat)INT_MAX, 1.0, 10.0, 0.0);
+ ring_filesize_sb = gtk_spin_button_new (ring_filesize_adj, 0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (ring_filesize_sb), TRUE);
+ gtk_widget_set_size_request(ring_filesize_sb, 80, -1);
+ gtk_table_attach_defaults(GTK_TABLE(multi_tb), ring_filesize_sb, 1, 2, row, row+1);
+
+ ring_filesize_cbx = size_unit_combo_box_new(global_capture_opts.autostop_filesize);
+ gtk_table_attach_defaults(GTK_TABLE(multi_tb), ring_filesize_cbx, 2, 3, row, row+1);
+
+ value = size_unit_combo_box_set_value(global_capture_opts.autostop_filesize);
+ gtk_adjustment_set_value(ring_filesize_adj, (gfloat) value);
+
+ row++;
+
+ /* Ring buffer duration row */
+ file_duration_cb = gtk_check_button_new_with_label("Next file every");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(file_duration_cb),
+ global_capture_opts.has_file_duration);
+ g_signal_connect(file_duration_cb, "toggled",
+ G_CALLBACK(capture_prep_adjust_sensitivity), cap_open_w);
+ gtk_widget_set_tooltip_text(file_duration_cb,
+ "If the selected duration is exceeded, capturing switches to the next file.\n"
+ "PLEASE NOTE: at least one of the \"Next file every\" options MUST be selected.");
+ gtk_table_attach_defaults(GTK_TABLE(multi_tb), file_duration_cb, 0, 1, row, row+1);
+
+ file_duration_adj = (GtkAdjustment *)gtk_adjustment_new(0.0,
+ 1, (gfloat)INT_MAX, 1.0, 10.0, 0.0);
+ file_duration_sb = gtk_spin_button_new (file_duration_adj, 0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (file_duration_sb), TRUE);
+ gtk_widget_set_size_request(file_duration_sb, 80, -1);
+ gtk_table_attach_defaults(GTK_TABLE(multi_tb), file_duration_sb, 1, 2, row, row+1);
+
+ file_duration_cbx = time_unit_combo_box_new(global_capture_opts.file_duration);
+ gtk_table_attach_defaults(GTK_TABLE(multi_tb), file_duration_cbx, 2, 3, row, row+1);
+
+ value = time_unit_combo_box_convert_value(global_capture_opts.file_duration);
+ gtk_adjustment_set_value(file_duration_adj, (gfloat) value);
+ row++;
+
+ /* Ring buffer files row */
+ ringbuffer_nbf_cb = gtk_check_button_new_with_label("Ring buffer with");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ringbuffer_nbf_cb),
+ global_capture_opts.has_ring_num_files);
+ g_signal_connect(ringbuffer_nbf_cb, "toggled", G_CALLBACK(capture_prep_adjust_sensitivity), cap_open_w);
+ gtk_widget_set_tooltip_text(ringbuffer_nbf_cb,
+ "After capturing has switched to the next file and the given number of files has exceeded, "
+ "the oldest file will be removed."
+ );
+ gtk_table_attach_defaults(GTK_TABLE(multi_tb), ringbuffer_nbf_cb, 0, 1, row, row+1);
+
+ ringbuffer_nbf_adj = (GtkAdjustment *) gtk_adjustment_new((gfloat) global_capture_opts.ring_num_files,
+ 2/*RINGBUFFER_MIN_NUM_FILES*/, RINGBUFFER_MAX_NUM_FILES, 1.0, 10.0, 0.0);
+ ringbuffer_nbf_sb = gtk_spin_button_new (ringbuffer_nbf_adj, 0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (ringbuffer_nbf_sb), TRUE);
+ gtk_widget_set_size_request(ringbuffer_nbf_sb, 80, -1);
+ g_signal_connect(ringbuffer_nbf_sb, "changed", G_CALLBACK(capture_prep_adjust_sensitivity), cap_open_w);
+ gtk_table_attach_defaults(GTK_TABLE(multi_tb), ringbuffer_nbf_sb, 1, 2, row, row+1);
+
+ ringbuffer_nbf_lb = gtk_label_new("files");
+ gtk_misc_set_alignment(GTK_MISC(ringbuffer_nbf_lb), 0, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(multi_tb), ringbuffer_nbf_lb, 2, 3, row, row+1);
+ row++;
+
+ /* Files row */
+ stop_files_cb = gtk_check_button_new_with_label("Stop capture after");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(stop_files_cb),
+ global_capture_opts.has_autostop_files);
+ g_signal_connect(stop_files_cb, "toggled", G_CALLBACK(capture_prep_adjust_sensitivity), cap_open_w);
+ gtk_widget_set_tooltip_text(stop_files_cb, "Stop capturing after the given number of \"file switches\" have been done.");
+ gtk_table_attach_defaults(GTK_TABLE(multi_tb), stop_files_cb, 0, 1, row, row+1);
+
+ stop_files_adj = (GtkAdjustment *) gtk_adjustment_new((gfloat)global_capture_opts.autostop_files,
+ 1, (gfloat)INT_MAX, 1.0, 10.0, 0.0);
+ stop_files_sb = gtk_spin_button_new (stop_files_adj, 0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (stop_files_sb), TRUE);
+ gtk_widget_set_size_request(stop_files_sb, 80, -1);
+ gtk_table_attach_defaults(GTK_TABLE(multi_tb), stop_files_sb, 1, 2, row, row+1);
+
+ stop_files_lb = gtk_label_new("file(s)");
+ gtk_misc_set_alignment(GTK_MISC(stop_files_lb), 0, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(multi_tb), stop_files_lb, 2, 3, row, row+1);
+ row++;
+
+ /* Capture limits frame */
+ limit_fr = gtk_frame_new("Stop Capture ...");
+ gtk_container_add(GTK_CONTAINER(left_vb), limit_fr);
+
+ limit_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(limit_vb), 5);
+ gtk_container_add(GTK_CONTAINER(limit_fr), limit_vb);
+
+ /* limits table */
+ limit_tb = gtk_table_new(3, 3, FALSE);
+ gtk_table_set_row_spacings(GTK_TABLE(limit_tb), 1);
+ gtk_table_set_col_spacings(GTK_TABLE(limit_tb), 3);
+ gtk_box_pack_start(GTK_BOX(limit_vb), limit_tb, FALSE, FALSE, 0);
+ row = 0;
+
+ /* Packet count row */
+ stop_packets_cb = gtk_check_button_new_with_label("... after");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(stop_packets_cb),
+ global_capture_opts.has_autostop_packets);
+ g_signal_connect(stop_packets_cb, "toggled", G_CALLBACK(capture_prep_adjust_sensitivity), cap_open_w);
+ gtk_widget_set_tooltip_text(stop_packets_cb, "Stop capturing after the given number of packets have been captured.");
+ gtk_table_attach_defaults(GTK_TABLE(limit_tb), stop_packets_cb, 0, 1, row, row+1);
+
+ stop_packets_adj = (GtkAdjustment *) gtk_adjustment_new((gfloat)global_capture_opts.autostop_packets,
+ 1, (gfloat)INT_MAX, 1.0, 10.0, 0.0);
+ stop_packets_sb = gtk_spin_button_new (stop_packets_adj, 0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (stop_packets_sb), TRUE);
+ gtk_widget_set_size_request(stop_packets_sb, 80, -1);
+ gtk_table_attach_defaults(GTK_TABLE(limit_tb), stop_packets_sb, 1, 2, row, row+1);
+
+ stop_packets_lb = gtk_label_new("packet(s)");
+ gtk_misc_set_alignment(GTK_MISC(stop_packets_lb), 0, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(limit_tb), stop_packets_lb, 2, 3, row, row+1);
+ row++;
+
+ /* Filesize row */
+ stop_filesize_cb = gtk_check_button_new_with_label("... after");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(stop_filesize_cb),
+ global_capture_opts.has_autostop_filesize);
+ g_signal_connect(stop_filesize_cb, "toggled", G_CALLBACK(capture_prep_adjust_sensitivity), cap_open_w);
+ gtk_widget_set_tooltip_text(stop_filesize_cb, "Stop capturing after the given amount of capture data has been captured.");
+ gtk_table_attach_defaults(GTK_TABLE(limit_tb), stop_filesize_cb, 0, 1, row, row+1);
+
+ stop_filesize_adj = (GtkAdjustment *) gtk_adjustment_new(0.0,
+ 1, (gfloat)INT_MAX, 1.0, 10.0, 0.0);
+ stop_filesize_sb = gtk_spin_button_new (stop_filesize_adj, 0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (stop_filesize_sb), TRUE);
+ gtk_widget_set_size_request(stop_filesize_sb, 80, -1);
+ gtk_table_attach_defaults(GTK_TABLE(limit_tb), stop_filesize_sb, 1, 2, row, row+1);
+
+ stop_filesize_cbx = size_unit_combo_box_new(global_capture_opts.autostop_filesize);
+ gtk_table_attach_defaults(GTK_TABLE(limit_tb), stop_filesize_cbx, 2, 3, row, row+1);
+
+ value = size_unit_combo_box_set_value(global_capture_opts.autostop_filesize);
+ gtk_adjustment_set_value(stop_filesize_adj, (gfloat) value);
+
+ row++;
+
+ /* Duration row */
+ stop_duration_cb = gtk_check_button_new_with_label("... after");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(stop_duration_cb),
+ global_capture_opts.has_autostop_duration);
+ g_signal_connect(stop_duration_cb, "toggled", G_CALLBACK(capture_prep_adjust_sensitivity), cap_open_w);
+ gtk_widget_set_tooltip_text(stop_duration_cb, "Stop capturing after the given time is exceeded.");
+ gtk_table_attach_defaults(GTK_TABLE(limit_tb), stop_duration_cb, 0, 1, row, row+1);
+
+ stop_duration_adj = (GtkAdjustment *) gtk_adjustment_new(0.0,
+ 1, (gfloat)INT_MAX, 1.0, 10.0, 0.0);
+ stop_duration_sb = gtk_spin_button_new (stop_duration_adj, 0, 0);
+ gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (stop_duration_sb), TRUE);
+ gtk_widget_set_size_request(stop_duration_sb, 80, -1);
+ gtk_table_attach_defaults(GTK_TABLE(limit_tb), stop_duration_sb, 1, 2, row, row+1);
+
+ stop_duration_cbx = time_unit_combo_box_new(global_capture_opts.autostop_duration);
+ gtk_table_attach_defaults(GTK_TABLE(limit_tb), stop_duration_cbx, 2, 3, row, row+1);
+
+ value = time_unit_combo_box_convert_value(global_capture_opts.autostop_duration);
+ gtk_adjustment_set_value(stop_duration_adj, (gfloat) value);
+ row++;
+
+ /* Display-related options frame */
+ display_fr = gtk_frame_new("Display Options");
+ gtk_container_add(GTK_CONTAINER(right_vb), display_fr);
+
+ display_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(display_vb), 5);
+ gtk_container_add(GTK_CONTAINER(display_fr), display_vb);
+
+ /* "Update display in real time" row */
+ sync_cb = gtk_check_button_new_with_mnemonic(
+ "_Update list of packets in real time");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sync_cb),
+ global_capture_opts.real_time_mode);
+ g_signal_connect(sync_cb, "toggled", G_CALLBACK(capture_prep_adjust_sensitivity), cap_open_w);
+ gtk_widget_set_tooltip_text(sync_cb,
+ "Using this option will show the captured packets immediately on the main screen. "
+ "Please note: this will slow down capturing, so increased packet drops might appear.");
+ gtk_container_add(GTK_CONTAINER(display_vb), sync_cb);
+
+ /* "Auto-scroll live update" row */
+ auto_scroll_cb = gtk_check_button_new_with_mnemonic("_Automatic scrolling in live capture");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(auto_scroll_cb), auto_scroll_live);
+ gtk_widget_set_tooltip_text(auto_scroll_cb,
+ "This will scroll the \"Packet List\" automatically to the latest captured packet, "
+ "when the \"Update List of packets in real time\" option is used.");
+ gtk_container_add(GTK_CONTAINER(display_vb), auto_scroll_cb);
+
+ /* "Hide capture info" row */
+ hide_info_cb = gtk_check_button_new_with_mnemonic("_Hide capture info dialog");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hide_info_cb), !global_capture_opts.show_info);
+ gtk_widget_set_tooltip_text(hide_info_cb, "Hide the capture info dialog while capturing.");
+ gtk_container_add(GTK_CONTAINER(display_vb), hide_info_cb);
+
+ /* Name Resolution frame */
+ resolv_fr = gtk_frame_new("Name Resolution");
+ gtk_container_add(GTK_CONTAINER(right_vb), resolv_fr);
+
+ resolv_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(resolv_vb), 5);
+ gtk_container_add(GTK_CONTAINER(resolv_fr), resolv_vb);
+
+ m_resolv_cb = gtk_check_button_new_with_mnemonic(
+ "Enable _MAC name resolution");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_resolv_cb),
+ gbl_resolv_flags & RESOLV_MAC);
+ gtk_widget_set_tooltip_text(m_resolv_cb, "Perform MAC layer name resolution while capturing.");
+ gtk_container_add(GTK_CONTAINER(resolv_vb), m_resolv_cb);
+
+ n_resolv_cb = gtk_check_button_new_with_mnemonic(
+ "Enable _network name resolution");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(n_resolv_cb),
+ gbl_resolv_flags & RESOLV_NETWORK);
+ gtk_widget_set_tooltip_text(n_resolv_cb, "Perform network layer name resolution while capturing.");
+ gtk_container_add(GTK_CONTAINER(resolv_vb), n_resolv_cb);
+
+ t_resolv_cb = gtk_check_button_new_with_mnemonic(
+ "Enable _transport name resolution");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(t_resolv_cb),
+ gbl_resolv_flags & RESOLV_TRANSPORT);
+ gtk_widget_set_tooltip_text(t_resolv_cb,
+ "Perform transport layer name resolution while capturing.");
+ gtk_container_add(GTK_CONTAINER(resolv_vb), t_resolv_cb);
+
+ /* Button row: "Start", "Cancel" and "Help" buttons */
+ bbox = dlg_button_row_new(WIRESHARK_STOCK_CAPTURE_START, GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 5);
+
+ ok_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_CAPTURE_START);
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(capture_start_cb), NULL);
+ gtk_widget_set_tooltip_text(ok_bt, "Start the capture process.");
+ if (num_selected > 0) {
+ gtk_widget_set_sensitive(ok_bt, TRUE);
+ } else {
+ gtk_widget_set_sensitive(ok_bt, FALSE);
+ }
+
+ close_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ gtk_widget_set_tooltip_text(close_bt,
+ "Exit dialog.");
+ window_set_cancel_button(cap_open_w, close_bt, window_cancel_button_cb);
+
+ help_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ gtk_widget_set_tooltip_text(help_bt,
+ "Show help about capturing.");
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_CAPTURE_OPTIONS_DIALOG);
+ gtk_widget_grab_default(ok_bt);
+
+ /* Attach pointers to needed widgets to the capture prefs window/object */
+#ifdef HAVE_PCAP_REMOTE
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_REMOTE_DIALOG_PTR_KEY, NULL);
+#endif
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_PROMISC_KEY_ALL, promisc_cb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_PCAP_NG_KEY, pcap_ng_cb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_FILE_TE_KEY, file_te);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_MULTI_FILES_ON_CB_KEY, multi_files_on_cb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_RING_NBF_CB_KEY, ringbuffer_nbf_cb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_RING_NBF_SB_KEY, ringbuffer_nbf_sb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_RING_NBF_LB_KEY, ringbuffer_nbf_lb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_RING_FILESIZE_CB_KEY, ring_filesize_cb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_RING_FILESIZE_SB_KEY, ring_filesize_sb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_RING_FILESIZE_CBX_KEY, ring_filesize_cbx);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_FILE_DURATION_CB_KEY, file_duration_cb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_FILE_DURATION_SB_KEY, file_duration_sb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_FILE_DURATION_CBX_KEY, file_duration_cbx);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_SYNC_KEY, sync_cb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_AUTO_SCROLL_KEY, auto_scroll_cb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_HIDE_INFO_KEY, hide_info_cb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_STOP_PACKETS_CB_KEY, stop_packets_cb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_STOP_PACKETS_SB_KEY, stop_packets_sb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_STOP_PACKETS_LB_KEY, stop_packets_lb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_STOP_FILESIZE_CB_KEY, stop_filesize_cb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_STOP_FILESIZE_SB_KEY, stop_filesize_sb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_STOP_FILESIZE_CBX_KEY, stop_filesize_cbx);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_STOP_DURATION_CB_KEY, stop_duration_cb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_STOP_DURATION_SB_KEY, stop_duration_sb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_STOP_DURATION_CBX_KEY, stop_duration_cbx);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_STOP_FILES_CB_KEY, stop_files_cb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_STOP_FILES_SB_KEY, stop_files_sb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_STOP_FILES_LB_KEY, stop_files_lb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_M_RESOLVE_KEY, m_resolv_cb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_N_RESOLVE_KEY, n_resolv_cb);
+ g_object_set_data(G_OBJECT(cap_open_w), E_CAP_T_RESOLVE_KEY, t_resolv_cb);
+
+ /* Set the sensitivity of various widgets as per the settings of other
+ widgets. */
+ capture_prep_adjust_sensitivity(NULL, cap_open_w);
+
+ /* Catch the "activate" signal on the text
+ entries, so that if the user types Return there, we act as if the
+ "OK" button had been selected, as happens if Return is typed if some
+ widget that *doesn't* handle the Return key has the input focus. */
+ /*dlg_set_activate(gtk_bin_get_child(GTK_BIN(if_cb)), ok_bt);*/
+ dlg_set_activate(file_te, ok_bt);
+
+ g_signal_connect(cap_open_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(cap_open_w, "destroy", G_CALLBACK(capture_prep_destroy_cb), NULL);
+
+ gtk_widget_show_all(cap_open_w);
+ window_present(cap_open_w);
+
+ cap_open_complete = TRUE; /* "Capture:Start" is now OK */
+}
+
+/* everythings prepared, now it's really time to start the capture */
+void
+capture_start_confirmed(void)
+{
+ interface_options interface_opts;
+ guint i;
+
+ /* did the user ever select a capture interface before? */
+ if(global_capture_opts.ifaces->len == 0 && prefs.capture_device == NULL) {
+ simple_dialog(ESD_TYPE_CONFIRMATION,
+ ESD_BTN_OK,
+ "%sNo capture interface selected!%s\n\n"
+ "To select an interface use:\n\n"
+ "Capture->Options (until Wireshark is stopped)\n"
+ "Edit->Preferences/Capture (permanent, if saved)",
+ simple_dialog_primary_start(), simple_dialog_primary_end());
+ return;
+ }
+
+ /* XXX - we might need to init other pref data as well... */
+ menu_auto_scroll_live_changed(auto_scroll_live);
+
+ if (capture_start(&global_capture_opts)) {
+ /* The capture succeeded, which means the capture filter syntax is
+ valid; add this capture filter to the recent capture filter list. */
+ for (i = 0; i < global_capture_opts.ifaces->len; i++) {
+ interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, i);
+ if (interface_opts.cfilter) {
+ cfilter_combo_add_recent(interface_opts.cfilter);
+ }
+ }
+ }
+}
+
+/* user confirmed the "Save capture file..." dialog */
+static void
+capture_start_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
+{
+ switch(btn) {
+ case(ESD_BTN_SAVE):
+ /* save file first */
+ file_save_as_cmd(after_save_capture_dialog, data, FALSE);
+ break;
+ case(ESD_BTN_DONT_SAVE):
+ /* XXX - unlink old file? */
+ /* start the capture */
+ capture_start_confirmed();
+ break;
+ case(ESD_BTN_CANCEL):
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+/* user pressed the "Start" button (in dialog or toolbar) */
+void
+capture_start_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ gpointer dialog;
+ gchar *if_name;
+ cap_settings_t *cap_settings_p = NULL;
+ interface_options interface_opts;
+ guint i;
+
+#ifdef HAVE_AIRPCAP
+ airpcap_if_active = airpcap_if_selected;
+ airpcap_set_toolbar_start_capture(airpcap_if_active);
+#endif
+
+ if (cap_open_w) {
+ /*
+ * There's an options dialog; get the values from it and close it.
+ */
+ gboolean success;
+
+ /* Determine if "capture start" while building of the "capture options" window */
+ /* is in progress. If so, ignore the "capture start. */
+ /* XXX: Would it be better/cleaner for the "capture options" window code to */
+ /* disable the capture start button temporarily ? */
+ if (cap_open_complete == FALSE) {
+ return; /* Building options window: ignore "capture start" */
+ }
+ success = capture_dlg_prep(cap_open_w);
+ window_destroy(GTK_WIDGET(cap_open_w));
+ if (!success)
+ return; /* error in options dialog */
+ }
+
+ if (global_capture_opts.ifaces->len == 0) {
+ if (prefs.capture_device == NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "You didn't specify an interface on which to capture packets.");
+ return;
+ }
+ interface_opts.name = g_strdup(get_if_name(prefs.capture_device));
+ interface_opts.descr = get_interface_descriptive_name(interface_opts.name);
+#ifdef HAVE_PCAP_CREATE
+ interface_opts.monitor_mode = prefs_capture_device_monitor_mode(interface_opts.name);
+#else
+ interface_opts.monitor_mode = FALSE;
+#endif
+ interface_opts.linktype = capture_dev_user_linktype_find(interface_opts.name);
+ interface_opts.cfilter = g_strdup(global_capture_opts.default_options.cfilter);
+ interface_opts.snaplen = global_capture_opts.default_options.snaplen;
+ interface_opts.has_snaplen = global_capture_opts.default_options.has_snaplen;
+ interface_opts.promisc_mode = global_capture_opts.default_options.promisc_mode;
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ interface_opts.buffer_size = global_capture_opts.default_options.buffer_size;
+#endif
+#ifdef HAVE_PCAP_REMOTE
+ interface_opts.src_type = global_capture_opts.default_options.src_type;
+ interface_opts.remote_host = g_strdup(global_capture_opts.default_options.remote_host);
+ interface_opts.remote_port = g_strdup(global_capture_opts.default_options.remote_port);
+ interface_opts.auth_type = global_capture_opts.default_options.auth_type;
+ interface_opts.auth_username = g_strdup(global_capture_opts.default_options.auth_username);
+ interface_opts.auth_password = g_strdup(global_capture_opts.default_options.auth_password);
+ interface_opts.datatx_udp = global_capture_opts.default_options.datatx_udp;
+ interface_opts.nocap_rpcap = global_capture_opts.default_options.nocap_rpcap;
+ interface_opts.nocap_local = global_capture_opts.default_options.nocap_local;
+ #endif
+ #ifdef HAVE_PCAP_SETSAMPLING
+ interface_opts.sampling_method = global_capture_opts.default_options.sampling_method;
+ interface_opts.sampling_param = global_capture_opts.default_options.sampling_param;
+ #endif
+ g_array_insert_val(global_capture_opts.ifaces, 0, interface_opts);
+ }
+
+ if (cap_settings_history != NULL) {
+ for (i = 0; i < global_capture_opts.ifaces->len; i++) {
+ interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, i);
+ if_name = g_strdup(interface_opts.name);
+ cap_settings_p = (cap_settings_t *)g_hash_table_lookup(cap_settings_history, if_name);
+ if (cap_settings_p == NULL) {
+ cap_settings_p = g_new(cap_settings_t,1);
+ g_hash_table_insert(cap_settings_history, if_name, cap_settings_p);
+ } else {
+ g_free(if_name);
+ }
+ cap_settings_p->monitor_mode = interface_opts.monitor_mode;
+ cap_settings_p->linktype = interface_opts.linktype;
+ }
+ }
+
+ if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
+ /* user didn't saved his current file, ask him */
+ dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE_CANCEL,
+ "%sSave capture file before starting a new capture?%s\n\n"
+ "If you start a new capture without saving, your current capture data will\nbe discarded.",
+ simple_dialog_primary_start(), simple_dialog_primary_end());
+ simple_dialog_set_cb(dialog, capture_start_answered_cb, NULL);
+ } else {
+ /* unchanged file, just capture a new one */
+ capture_start_confirmed();
+ }
+}
+
+
+/* user change linktype selection;, convert to internal DLT value */
+static void
+select_link_type_cb(GtkWidget *linktype_combo_box, gpointer data _U_)
+{
+ gpointer ptr;
+ int dlt;
+ interface_row row;
+
+ row = g_array_index(rows, interface_row, marked_row);
+ rows = g_array_remove_index(rows, marked_row);
+ if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(linktype_combo_box), &ptr)) {
+ g_assert_not_reached(); /* Programming error: somehow nothing is active */
+ }
+ if ((dlt = GPOINTER_TO_INT(ptr)) == -1) {
+ g_assert_not_reached(); /* Programming error: somehow managed to select an "unsupported" entry */
+ }
+ row.active_dlt = dlt;
+ g_array_insert_val(rows, marked_row, row);
+ capture_filter_check_syntax_cb(linktype_combo_box, data);
+}
+
+/* user pressed "File" button */
+static void
+capture_prep_file_cb(GtkWidget *file_bt, GtkWidget *file_te)
+{
+ file_selection_browse(file_bt, file_te, "Wireshark: Specify a Capture File", FILE_SELECTION_WRITE_BROWSE);
+}
+
+
+/* convert dialog settings into capture_opts values */
+static gboolean
+capture_dlg_prep(gpointer parent_w) {
+ GtkWidget *pcap_ng_cb,
+ *file_te, *multi_files_on_cb, *ringbuffer_nbf_sb, *ringbuffer_nbf_cb,
+ *sync_cb, *auto_scroll_cb, *hide_info_cb,
+ *stop_packets_cb, *stop_packets_sb,
+ *stop_filesize_cb, *stop_filesize_sb, *stop_filesize_cbx,
+ *stop_duration_cb, *stop_duration_sb, *stop_duration_cbx,
+ *ring_filesize_cb, *ring_filesize_sb, *ring_filesize_cbx,
+ *file_duration_cb, *file_duration_sb, *file_duration_cbx,
+ *stop_files_cb, *stop_files_sb,
+ *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
+ const gchar *g_save_file;
+ gchar *cf_name;
+ gchar *dirname;
+ gint32 tmp;
+
+ pcap_ng_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_PCAP_NG_KEY);
+ file_te = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_FILE_TE_KEY);
+ multi_files_on_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_MULTI_FILES_ON_CB_KEY);
+ ringbuffer_nbf_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_RING_NBF_CB_KEY);
+ ringbuffer_nbf_sb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_RING_NBF_SB_KEY);
+ ring_filesize_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_RING_FILESIZE_CB_KEY);
+ ring_filesize_sb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_RING_FILESIZE_SB_KEY);
+ ring_filesize_cbx = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_RING_FILESIZE_CBX_KEY);
+ file_duration_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_FILE_DURATION_CB_KEY);
+ file_duration_sb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_FILE_DURATION_SB_KEY);
+ file_duration_cbx = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_FILE_DURATION_CBX_KEY);
+ sync_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_SYNC_KEY);
+ auto_scroll_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_AUTO_SCROLL_KEY);
+ hide_info_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_HIDE_INFO_KEY);
+ stop_packets_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_PACKETS_CB_KEY);
+ stop_packets_sb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_PACKETS_SB_KEY);
+ stop_filesize_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_FILESIZE_CB_KEY);
+ stop_filesize_sb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_FILESIZE_SB_KEY);
+ stop_filesize_cbx = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_FILESIZE_CBX_KEY);
+ stop_duration_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_DURATION_CB_KEY);
+ stop_duration_sb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_DURATION_SB_KEY);
+ stop_duration_cbx = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_DURATION_CBX_KEY);
+ stop_files_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_FILES_CB_KEY);
+ stop_files_sb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_FILES_SB_KEY);
+ m_resolv_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_M_RESOLVE_KEY);
+ n_resolv_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_N_RESOLVE_KEY);
+ t_resolv_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_T_RESOLVE_KEY);
+
+ if (global_capture_opts.ifaces->len == 0) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "You didn't specify an interface on which to capture packets.");
+ return FALSE;
+ }
+ global_capture_opts.use_pcapng =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pcap_ng_cb));
+ /* Wireshark always saves to a capture file. */
+ global_capture_opts.saving_to_file = TRUE;
+ g_save_file = gtk_entry_get_text(GTK_ENTRY(file_te));
+ if (g_save_file && g_save_file[0]) {
+ /* User specified a file to which the capture should be written. */
+ global_capture_opts.save_file = g_strdup(g_save_file);
+ /* Save the directory name for future file dialogs. */
+ cf_name = g_strdup(g_save_file);
+ dirname = get_dirname(cf_name); /* Overwrites cf_name */
+ set_last_open_dir(dirname);
+ g_free(cf_name);
+ } else {
+ /* User didn't specify a file; save to a temporary file. */
+ global_capture_opts.save_file = NULL;
+ }
+
+ global_capture_opts.has_autostop_packets =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(stop_packets_cb));
+ if (global_capture_opts.has_autostop_packets)
+ global_capture_opts.autostop_packets =
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(stop_packets_sb));
+
+ global_capture_opts.has_autostop_duration =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(stop_duration_cb));
+ if (global_capture_opts.has_autostop_duration) {
+ global_capture_opts.autostop_duration =
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(stop_duration_sb));
+ global_capture_opts.autostop_duration =
+ time_unit_combo_box_get_value(stop_duration_cbx, global_capture_opts.autostop_duration);
+ }
+
+ global_capture_opts.real_time_mode =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sync_cb));
+
+ auto_scroll_live =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(auto_scroll_cb));
+
+ global_capture_opts.show_info =
+ !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hide_info_cb));
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m_resolv_cb)))
+ gbl_resolv_flags |= RESOLV_MAC;
+ else
+ gbl_resolv_flags &= ~RESOLV_MAC;
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(n_resolv_cb)))
+ gbl_resolv_flags |= RESOLV_NETWORK;
+ else
+ gbl_resolv_flags &= ~RESOLV_NETWORK;
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(t_resolv_cb)))
+ gbl_resolv_flags |= RESOLV_TRANSPORT;
+ else
+ gbl_resolv_flags &= ~RESOLV_TRANSPORT;
+
+ global_capture_opts.has_ring_num_files =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ringbuffer_nbf_cb));
+
+ global_capture_opts.ring_num_files =
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(ringbuffer_nbf_sb));
+ if (global_capture_opts.ring_num_files > RINGBUFFER_MAX_NUM_FILES)
+ global_capture_opts.ring_num_files = RINGBUFFER_MAX_NUM_FILES;
+#if RINGBUFFER_MIN_NUM_FILES > 0
+ else if (global_capture_opts.ring_num_files < RINGBUFFER_MIN_NUM_FILES)
+ global_capture_opts.ring_num_files = RINGBUFFER_MIN_NUM_FILES;
+#endif
+
+ global_capture_opts.multi_files_on =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(multi_files_on_cb));
+
+ global_capture_opts.has_file_duration =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(file_duration_cb));
+ if (global_capture_opts.has_file_duration) {
+ global_capture_opts.file_duration =
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(file_duration_sb));
+ global_capture_opts.file_duration =
+ time_unit_combo_box_get_value(file_duration_cbx, global_capture_opts.file_duration);
+ }
+
+ global_capture_opts.has_autostop_files =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(stop_files_cb));
+ if (global_capture_opts.has_autostop_files)
+ global_capture_opts.autostop_files =
+ gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(stop_files_sb));
+
+ if (global_capture_opts.multi_files_on) {
+ global_capture_opts.has_autostop_filesize =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ring_filesize_cb));
+ if (global_capture_opts.has_autostop_filesize) {
+ tmp = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(ring_filesize_sb));
+ tmp = size_unit_combo_box_convert_value(ring_filesize_cbx, tmp);
+ if(tmp != 0) {
+ global_capture_opts.autostop_filesize = tmp;
+ } else {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%sMultiple files: Requested filesize too large!%s\n\n"
+ "The setting \"Next file every x byte(s)\" can't be greater than %u bytes (2GB).",
+ simple_dialog_primary_start(), simple_dialog_primary_end(), G_MAXINT);
+ return FALSE;
+ }
+ }
+
+ /* test if the settings are ok for a ringbuffer */
+ if (global_capture_opts.save_file == NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%sMultiple files: No capture file name given!%s\n\n"
+ "You must specify a filename if you want to use multiple files.",
+ simple_dialog_primary_start(), simple_dialog_primary_end());
+ return FALSE;
+ } else if (!global_capture_opts.has_autostop_filesize && !global_capture_opts.has_file_duration) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%sMultiple files: No file limit given!%s\n\n"
+ "You must specify a file size or duration at which is switched to the next capture file\n"
+ "if you want to use multiple files.",
+ simple_dialog_primary_start(), simple_dialog_primary_end());
+ g_free(global_capture_opts.save_file);
+ global_capture_opts.save_file = NULL;
+ return FALSE;
+ }
+ } else {
+ global_capture_opts.has_autostop_filesize =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(stop_filesize_cb));
+ if (global_capture_opts.has_autostop_filesize) {
+ tmp = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(stop_filesize_sb));
+ tmp = size_unit_combo_box_convert_value(stop_filesize_cbx, tmp);
+ if(tmp != 0) {
+ global_capture_opts.autostop_filesize = tmp;
+ } else {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%sStop Capture: Requested filesize too large!%s\n\n"
+ "The setting \"... after x byte(s)\" can't be greater than %u bytes (2GB).",
+ simple_dialog_primary_start(), simple_dialog_primary_end(), G_MAXINT);
+ return FALSE;
+ }
+ }
+ } /* multi_files_on */
+ return TRUE;
+}
+
+static void
+make_and_fill_rows(void)
+{
+ GList *if_entry, *if_list;
+ if_info_t *if_info;
+ char *if_string=NULL;
+ gchar *descr;
+ if_capabilities_t *caps=NULL;
+ gint linktype_count;
+ cap_settings_t cap_settings;
+ GSList *curr_addr;
+ int ips = 0, err;
+ guint i;
+ if_addr_t *addr;
+ GList *lt_entry;
+ link_row *link = NULL;
+ data_link_info_t *data_link_info;
+ gchar *str, *err_str = NULL, *err_str_norfmon;
+ interface_row row;
+ interface_options interface_opts;
+ gboolean found = FALSE;
+ GString *ip_str;
+
+ rows = g_array_new(TRUE, TRUE, sizeof(interface_row));
+ /* Scan through the list and build a list of strings to display. */
+ if_list = capture_interface_list(&err, &err_str);
+ if_list = g_list_sort (if_list, if_list_comparator_alph);
+ if (if_list == NULL &&
+ (err == CANT_GET_INTERFACE_LIST || err == DONT_HAVE_PCAP)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_str);
+ g_free(err_str);
+ return;
+ } else if (err_str) {
+ g_free(err_str);
+ }
+ for (if_entry = if_list; if_entry != NULL; if_entry = g_list_next(if_entry)) {
+ if_info = (if_info_t *)if_entry->data;
+ ip_str = g_string_new("");
+ str = NULL;
+ ips = 0;
+ row.name = g_strdup(if_info->name);
+ /* Is this interface hidden and, if so, should we include it anyway? */
+ if (!prefs_is_capture_device_hidden(if_info->name)) {
+ /* It's not hidden, or it is but we should include it in the list. */
+ /* Do we have a user-supplied description? */
+ descr = capture_dev_user_descr_find(if_info->name);
+ if (descr != NULL) {
+ /* Yes, we have a user-supplied description; use it. */
+ if_string = g_strdup_printf("%s: %s", descr, if_info->name);
+ g_free(descr);
+ } else {
+ /* No, we don't have a user-supplied description; did we get
+ one from the OS or libpcap? */
+ if (if_info->description != NULL) {
+ /* Yes - use it. */
+ if_string = g_strdup_printf("%s: %s", if_info->description, if_info->name);
+ } else {
+ /* No. */
+ if_string = g_strdup(if_info->name);
+ }
+ }
+ if (if_info->loopback) {
+ row.display_name = g_strdup_printf("%s (loopback)", if_string);
+ } else {
+ row.display_name = g_strdup(if_string);
+ }
+ found = FALSE;
+ for (i = 0; i < global_capture_opts.ifaces->len; i++) {
+ interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, i);
+ if (!interface_opts.name || strcmp(interface_opts.name, (char*)row.name)!=0) {
+ continue;
+ } else {
+ found = TRUE;
+ break;
+ }
+ }
+ if (found) {
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ row.buffer = interface_opts.buffer_size;
+#endif
+ row.pmode = interface_opts.promisc_mode;
+ row.has_snaplen = interface_opts.has_snaplen;
+ row.snaplen = interface_opts.snaplen;
+ row.cfilter = g_strdup(interface_opts.cfilter);
+ } else {
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ row.buffer = global_capture_opts.default_options.buffer_size;
+#endif
+ row.pmode = global_capture_opts.default_options.promisc_mode;
+ row.has_snaplen = global_capture_opts.default_options.has_snaplen;
+ row.snaplen = global_capture_opts.default_options.snaplen;
+ row.cfilter = g_strdup(global_capture_opts.default_options.cfilter);
+ }
+ cap_settings = capture_get_cap_settings(if_info->name);
+ caps = capture_get_if_capabilities(if_info->name,
+ cap_settings.monitor_mode,
+ &err_str);
+ if (caps == NULL) {
+ /* Error attempting to get interface capabilities. */
+ if (cap_settings.monitor_mode) {
+ /*
+ * Perhaps this is the libpcap bug on Linux where
+ * attempting to set monitor mode with the Wireless
+ * Extensions ioctls doesn't work correctly.
+ *
+ * Try fetching the capabilities without monitor mode;
+ * if that succeeds, report the monitor-mode problem,
+ * and use the no-monitor-mode capabilities. If that
+ * fails, report that failure. In either case, force
+ * monitor mode off.
+ */
+ cap_settings.monitor_mode = FALSE;
+ caps = capture_get_if_capabilities(if_string, cap_settings.monitor_mode,
+ &err_str_norfmon);
+ if (caps == NULL) {
+ /* Epic fail. */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_str_norfmon);
+ g_free(err_str_norfmon);
+ g_free(err_str);
+ } else {
+ /*
+ * OK, it's probably that bug. Suggest using airmon-ng,
+ * just in case the adapter has a mac80211 driver and
+ * libpcap was built without libnl so that it can't
+ * use the mac80211 features to create a monitor-mode
+ * device.
+ */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s\n\n"
+ "Try using airmon-ng, as suggested by CaptureSetup/WLAN in the Wireshark Wiki.",
+ err_str);
+ g_free(err_str);
+ }
+ } else {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_str);
+ g_free(err_str);
+ }
+ }
+ for (; (curr_addr = g_slist_nth(if_info->addrs, ips)) != NULL; ips++) {
+ if (ips != 0) {
+ g_string_append(ip_str, "\n");
+ }
+ addr = (if_addr_t *)curr_addr->data;
+ switch (addr->ifat_type) {
+ case IF_AT_IPv4:
+ g_string_append(ip_str, ip_to_str((guint8 *)&addr->addr.ip4_addr));
+ break;
+ case IF_AT_IPv6:
+ g_string_append(ip_str, ip6_to_str((struct e_in6_addr *)&addr->addr.ip6_addr));
+ break;
+ default:
+ /* In case we add non-IP addresses */
+ break;
+ }
+ }
+ linktype_count = 0;
+ row.links = NULL;
+ if (caps != NULL) {
+#ifdef HAVE_PCAP_CREATE
+ row.monitor_mode_enabled = cap_settings.monitor_mode;
+ row.monitor_mode_supported = caps->can_set_rfmon;
+#endif
+ for (lt_entry = caps->data_link_types; lt_entry != NULL; lt_entry = g_list_next(lt_entry)) {
+ data_link_info = (data_link_info_t *)lt_entry->data;
+ if (data_link_info->description != NULL) {
+ str = g_strdup_printf("%s", data_link_info->description);
+ } else {
+ str = g_strdup_printf("%s (not supported)", data_link_info->name);
+ }
+ if (linktype_count == 0) {
+ row.active_dlt = data_link_info->dlt;
+ }
+ link = (link_row *)g_malloc(sizeof(link_row));
+ link->dlt = data_link_info->dlt;
+ link->name = g_strdup(str);
+ row.links = g_list_append(row.links, link);
+ linktype_count++;
+ }
+ } else {
+ cap_settings.monitor_mode = FALSE;
+#ifdef HAVE_PCAP_CREATE
+ row.monitor_mode_enabled = FALSE;
+ row.monitor_mode_supported = FALSE;
+#endif
+ row.active_dlt = -1;
+ }
+ row.addresses = g_strdup(ip_str->str);
+ row.no_addresses = ips;
+ g_array_append_val(rows, row);
+ if (caps != NULL) {
+ free_if_capabilities(caps);
+ }
+ }
+ g_string_free(ip_str, TRUE);
+ }
+}
+
+GtkTreeModel *create_and_fill_model(GtkTreeView *view)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GList *list;
+ char *temp=NULL, *snaplen_string;
+ guint i, j;
+ link_row *link = NULL;
+ interface_row row;
+ interface_options interface_opts;
+ gboolean found = FALSE;
+
+#if defined(HAVE_PCAP_CREATE)
+ store = gtk_list_store_new (8, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING);
+#elif defined(_WIN32) && !defined (HAVE_PCAP_CREATE)
+ store = gtk_list_store_new (7, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING);
+#else
+ store = gtk_list_store_new (6, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+#endif
+
+ if (!rows || rows->len == 0) {
+ make_and_fill_rows();
+ }
+ if (rows && rows->len > 0) {
+ for (i = 0; i < rows->len; i++) {
+ row = g_array_index(rows, interface_row, i);
+ found = FALSE;
+ for (j = 0; j < global_capture_opts.ifaces->len; j++) {
+ interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, j);
+ if (!interface_opts.name || strcmp(interface_opts.name, (char*)row.name)!=0) {
+ continue;
+ } else {
+ found = TRUE;
+ num_selected++;
+ break;
+ }
+ }
+ if (row.no_addresses == 0) {
+ temp = g_strdup_printf("<b>%s</b>", row.display_name);
+ } else {
+ temp = g_strdup_printf("<b>%s</b>\n<span size='small'>%s</span>", row.display_name, row.addresses);
+ }
+ for (list = row.links; list != NULL; list = g_list_next(list)) {
+ link = (link_row*)(list->data);
+ if (link->dlt == row.active_dlt) {
+ break;
+ }
+ }
+ if (row.has_snaplen) {
+ snaplen_string = g_strdup_printf("%d", row.snaplen);
+ } else {
+ snaplen_string = g_strdup("default");
+ }
+ gtk_list_store_append (store, &iter);
+#if defined(HAVE_PCAP_CREATE)
+ gtk_list_store_set (store, &iter, CAPTURE, found, INTERFACE, temp, LINK, link->name, PMODE, row.pmode?"enabled":"disabled", SNAPLEN, snaplen_string, BUFFER, (guint) row.buffer, MONITOR, row.monitor_mode_supported?(row.monitor_mode_enabled?"enabled":"disabled"):"n/a", FILTER, row.cfilter, -1);
+#elif defined(_WIN32) && !defined(HAVE_PCAP_CREATE)
+ gtk_list_store_set (store, &iter, CAPTURE, found, INTERFACE, temp, LINK, link->name, PMODE, row.pmode?"enabled":"disabled", SNAPLEN, snaplen_string, BUFFER, (guint) row.buffer, FILTER, row.cfilter, -1);
+#else
+ gtk_list_store_set (store, &iter, CAPTURE, found, INTERFACE, temp, LINK, link->name, PMODE, row.pmode?"enabled":"disabled", SNAPLEN, snaplen_string, FILTER, row.cfilter, -1);
+#endif
+ }
+ }
+ gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
+ return GTK_TREE_MODEL(store);
+}
+
+gboolean query_tooltip_tree_view_cb (GtkWidget *widget,
+ gint x,
+ gint y,
+ gboolean keyboard_tip,
+ GtkTooltip *tooltip,
+ gpointer data _U_)
+{
+ GtkTreeIter iter;
+ GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
+ GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
+ GtkTreePath *path = NULL;
+ gchar *tmp;
+ gchar *pathstring;
+ GtkTreeViewColumn *column;
+ int col;
+ GtkCellRenderer* renderer=NULL;
+ GList *renderer_list;
+
+ char buffer[512];
+
+ if (!gtk_tree_view_get_tooltip_context (tree_view, &x, &y, keyboard_tip, &model, &path, &iter))
+ return FALSE;
+
+ gtk_tree_model_get (model, &iter, 0, &tmp, -1);
+ pathstring = gtk_tree_path_to_string (path);
+
+ if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(tree_view), (gint) x, (gint) y, NULL, &column, NULL, NULL)) {
+ for (col = 0; col < NUM_COLUMNS; col++) {
+ if (gtk_tree_view_get_column(tree_view, col) == column)
+ break;
+ }
+ switch (col)
+ {
+ case 0: g_snprintf (buffer, 511, "Choose which interface (network adapter) will be used to capture packets from. "
+ "Be sure to select the correct one, as it's a common mistake to select the wrong interface.");
+ break;
+ case 1: g_snprintf (buffer, 511, "Lists the interface name and the IP address(es) assigned to it. ");
+ break;
+ case 2: g_snprintf (buffer, 511, "Link-layer type the interface supports.");
+ break;
+ case 3: g_snprintf (buffer, 511, "Usually a network adapter will only capture the traffic sent to its own network address. "
+ "If you want to capture all traffic that the network adapter can \"see\", promiscuous mode should be configured.");
+ break;
+ case 4: g_snprintf(buffer, 511, "Limit the maximum number of bytes to be captured from each packet. This size includes the "
+ "link-layer header and all subsequent headers. ");
+ break;
+ case 5: g_snprintf (buffer, 511, "The memory buffer size used while capturing."
+ "If you notice packet drops, you can try to increase this size.");
+ break;
+ case 6: g_snprintf (buffer, 511, "Usually a Wi-Fi adapter will, even in promiscuous mode, only capture "
+ "the traffic on the BSS to which it's associated. "
+ "If you want to capture all traffic that the Wi-Fi adapter can \"receive\", mark this option."
+ "In order to see IEEE 802.11 headers or to see radio information for captured packets,"
+ "it might be necessary to turn this option on.\n\n"
+ "Note that, in monitor mode, the adapter might disassociate from the network to which it's associated. mode");
+ break;
+ case 7: g_snprintf(buffer, 511, "Selected capture filter to reduce the amount of packets to be captured.");
+ break;
+ default: g_snprintf(buffer, 511, "another option");
+ }
+
+ gtk_tooltip_set_markup (tooltip, buffer);
+ renderer_list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(column));
+ /* get the first renderer */
+ if (g_list_first(renderer_list)) {
+ renderer = (GtkCellRenderer*)g_list_nth_data(renderer_list, 0);
+ gtk_tree_view_set_tooltip_cell (tree_view, tooltip, path, column, renderer);
+ }
+ }
+ gtk_tree_path_free (path);
+ g_free (pathstring);
+
+ return TRUE;
+}
+
+#if defined (HAVE_PCAP_CREATE)
+void activate_monitor (GtkTreeViewColumn *tree_column _U_, GtkCellRenderer *renderer,
+ GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data _U_)
+{
+ interface_row row;
+ GtkTreePath *path = gtk_tree_model_get_path(tree_model, iter);
+ int index = atoi(gtk_tree_path_to_string(path));
+
+ row = g_array_index(rows, interface_row, index);
+
+ if (row.monitor_mode_supported==TRUE) {
+ g_object_set(G_OBJECT(renderer), "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
+ } else {
+ g_object_set(G_OBJECT(renderer), "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
+ }
+}
+#endif
+
+/* user requested to destroy the dialog */
+static void
+capture_prep_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+ GtkWidget *fs;
+#ifdef HAVE_PCAP_REMOTE
+ GList *if_list;
+ GtkWidget *remote_w;
+#endif
+
+ /* Is there a file selection dialog associated with this
+ Capture Options dialog? */
+ fs = g_object_get_data(G_OBJECT(cap_open_w), E_FILE_SEL_DIALOG_PTR_KEY);
+
+#ifdef HAVE_PCAP_REMOTE
+ if_list = (GList *) g_object_get_data(G_OBJECT(cap_open_w), E_CAP_IF_LIST_KEY);
+ if (if_list && g_list_length(if_list)>0) {
+ free_interface_list(if_list);
+ }
+#endif
+
+ if (fs != NULL) {
+ /* Yes. Destroy it. */
+ window_destroy(fs);
+ }
+
+ /* Note that we no longer have a "Capture Options" dialog box. */
+ cap_open_w = NULL;
+
+#ifdef HAVE_AIRPCAP
+ /* update airpcap toolbar */
+ airpcap_set_toolbar_stop_capture(airpcap_if_active);
+#endif
+
+#ifdef HAVE_PCAP_REMOTE
+ remote_w = g_object_get_data(G_OBJECT(win), E_CAP_REMOTE_DIALOG_PTR_KEY);
+ if (remote_w != NULL)
+ window_destroy(remote_w);
+#endif
+}
+
+
+#ifdef HAVE_PCAP_CREATE
+/* user changed the setting of the monitor-mode checkbox */
+static void
+capture_prep_monitor_changed_cb(GtkWidget *monitor, gpointer argp _U_)
+{
+ GList *lt_entry;
+ gchar *if_string=NULL;
+ cap_settings_t cap_settings;
+ gchar *err_str, *err_str_norfmon;
+ if_capabilities_t *caps=NULL;
+ gint linktype_count = 0, i;
+ data_link_info_t *data_link_info;
+ interface_row row;
+ link_row *link;
+ GtkWidget *linktype_combo_box = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CAP_LT_CBX_KEY);
+ GtkWidget *linktype_lb = (GtkWidget *)g_object_get_data(G_OBJECT(linktype_combo_box), E_CAP_LT_CBX_LABEL_KEY);
+
+ row = g_array_index(rows, interface_row, marked_row);
+ rows = g_array_remove_index(rows, marked_row);
+
+
+ if_string = g_strdup(row.name);
+ cap_settings = capture_get_cap_settings(if_string);
+ cap_settings.monitor_mode = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(monitor));
+ caps = capture_get_if_capabilities(if_string, cap_settings.monitor_mode,
+ &err_str);
+ if (caps == NULL) {
+ /* Error attempting to get interface capabilities. */
+ if (cap_settings.monitor_mode) {
+ /*
+ * Perhaps this is the libpcap bug on Linux where
+ * attempting to set monitor mode with the Wireless
+ * Extensions ioctls doesn't work correctly.
+ *
+ * Try fetching the capabilities without monitor mode;
+ * if that succeeds, report the monitor-mode problem,
+ * and use the no-monitor-mode capabilities. If that
+ * fails, report that failure. In either case, force
+ * monitor mode off.
+ */
+ cap_settings.monitor_mode = FALSE;
+ /* Set the monitor-mode checkbox to the new forced value */
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(monitor),
+ cap_settings.monitor_mode);
+ caps = capture_get_if_capabilities(if_string, cap_settings.monitor_mode,
+ &err_str_norfmon);
+ if (caps == NULL) {
+ /* Epic fail. */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_str_norfmon);
+ g_free(err_str_norfmon);
+ g_free(err_str);
+ } else {
+ /*
+ * OK, it's probably that bug. Suggest using airmon-ng,
+ * just in case the adapter has a mac80211 driver and
+ * libpcap was built without libnl so that it can't
+ * use the mac80211 features to create a monitor-mode
+ * device.
+ */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s\n\n"
+ "Try using airmon-ng, as suggested by CaptureSetup/WLAN in the Wireshark Wiki.",
+ err_str);
+ g_free(err_str);
+ }
+ } else {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_str);
+ g_free(err_str);
+ }
+ }
+
+ if (caps != NULL) {
+ g_signal_handlers_disconnect_by_func(linktype_combo_box, G_CALLBACK(select_link_type_cb), NULL );
+ ws_combo_box_clear_text_and_pointer(GTK_COMBO_BOX(linktype_combo_box));
+ for (i = (gint)g_list_length(row.links)-1; i >= 0; i--) {
+ GList* rem = g_list_nth(row.links, i);
+ row.links = g_list_remove_link(row.links, rem);
+ g_list_free_1(rem);
+ }
+ row.active_dlt = -1;
+ linktype_count = 0;
+ row.monitor_mode_supported = caps->can_set_rfmon;
+ row.monitor_mode_enabled = cap_settings.monitor_mode;
+ for (lt_entry = caps->data_link_types; lt_entry != NULL; lt_entry = g_list_next(lt_entry)) {
+ link = (link_row *)g_malloc(sizeof(link_row));
+ data_link_info = (data_link_info_t *)lt_entry->data;
+ if (data_link_info->description != NULL) {
+ ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(linktype_combo_box),
+ data_link_info->description,
+ GINT_TO_POINTER(data_link_info->dlt));
+ link->dlt = data_link_info->dlt;
+ if (linktype_count == 0) {
+ row.active_dlt = data_link_info->dlt;
+ }
+ link->name = g_strdup(data_link_info->description);
+ } else {
+ gchar *str;
+ /* Not supported - tell them about it but don't let them select it. */
+ str = g_strdup_printf("%s (not supported)", data_link_info->name);
+ ws_combo_box_append_text_and_pointer_full(GTK_COMBO_BOX(linktype_combo_box),
+ NULL,
+ str,
+ GINT_TO_POINTER(-1), /* Flag as "not supported" */
+ FALSE);
+ link->dlt = -1;
+ link->name = g_strdup(str);
+ g_free(str);
+ }
+ row.links = g_list_append(row.links, link);
+ linktype_count++;
+ }
+ free_if_capabilities(caps);
+ } else {
+ /* We don't know whether this supports monitor mode or not;
+ don't ask for monitor mode. */
+ cap_settings.monitor_mode = FALSE;
+ row.monitor_mode_enabled = FALSE;
+ row.monitor_mode_supported = FALSE;
+ }
+ gtk_widget_set_sensitive(linktype_lb, linktype_count >= 2);
+ gtk_widget_set_sensitive(linktype_combo_box, linktype_count >= 2);
+ ws_combo_box_set_active(GTK_COMBO_BOX(linktype_combo_box),0);
+ g_array_insert_val(rows, marked_row, row);
+}
+#endif
+
+/*
+ * Adjust the sensitivity of various widgets as per the current setting
+ * of other widgets.
+ */
+static void
+capture_prep_adjust_sensitivity(GtkWidget *tb _U_, gpointer parent_w)
+{
+ GtkWidget *multi_files_on_cb, *ringbuffer_nbf_cb, *ringbuffer_nbf_sb, *ringbuffer_nbf_lb,
+ *ring_filesize_cb, *ring_filesize_sb, *ring_filesize_cbx,
+ *file_duration_cb, *file_duration_sb, *file_duration_cbx,
+ *sync_cb, *auto_scroll_cb,
+ *stop_packets_cb, *stop_packets_sb, *stop_packets_lb,
+ *stop_filesize_cb, *stop_filesize_sb, *stop_filesize_cbx,
+ *stop_duration_cb, *stop_duration_sb, *stop_duration_cbx,
+ *stop_files_cb, *stop_files_sb, *stop_files_lb;
+
+ multi_files_on_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_MULTI_FILES_ON_CB_KEY);
+ ringbuffer_nbf_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_RING_NBF_CB_KEY);
+ ringbuffer_nbf_sb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_RING_NBF_SB_KEY);
+ ringbuffer_nbf_lb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_RING_NBF_LB_KEY);
+ ring_filesize_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_RING_FILESIZE_CB_KEY);
+ ring_filesize_sb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_RING_FILESIZE_SB_KEY);
+ ring_filesize_cbx = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_RING_FILESIZE_CBX_KEY);
+ file_duration_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_FILE_DURATION_CB_KEY);
+ file_duration_sb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_FILE_DURATION_SB_KEY);
+ file_duration_cbx = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_FILE_DURATION_CBX_KEY);
+ sync_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_SYNC_KEY);
+ auto_scroll_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_AUTO_SCROLL_KEY);
+ stop_packets_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_PACKETS_CB_KEY);
+ stop_packets_sb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_PACKETS_SB_KEY);
+ stop_packets_lb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_PACKETS_LB_KEY);
+ stop_filesize_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_FILESIZE_CB_KEY);
+ stop_filesize_sb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_FILESIZE_SB_KEY);
+ stop_filesize_cbx = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_FILESIZE_CBX_KEY);
+ stop_duration_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_DURATION_CB_KEY);
+ stop_duration_sb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_DURATION_SB_KEY);
+ stop_duration_cbx = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_DURATION_CBX_KEY);
+ stop_files_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_FILES_CB_KEY);
+ stop_files_sb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_FILES_SB_KEY);
+ stop_files_lb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CAP_STOP_FILES_LB_KEY);
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sync_cb))) {
+ /* "Update list of packets in real time" captures enabled; we don't
+ support ring buffer mode for those captures, so turn ring buffer
+ mode off if it's on, and make its toggle button, and the spin
+ button for the number of ring buffer files (and the spin button's
+ label), insensitive. */
+#if 0
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(multi_files_on_cb), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(multi_files_on_cb), FALSE);
+#endif
+
+ /* Auto-scroll mode is meaningful only in "Update list of packets
+ in real time" captures, so make its toggle button sensitive. */
+ gtk_widget_set_sensitive(GTK_WIDGET(auto_scroll_cb), TRUE);
+
+ /*gtk_widget_set_sensitive(GTK_WIDGET(hide_info_cb), TRUE);*/
+ } else {
+ /* "Update list of packets in real time" captures disabled; that
+ means ring buffer mode is OK, so make its toggle button
+ sensitive. */
+/* gtk_widget_set_sensitive(GTK_WIDGET(multi_files_on_cb), TRUE);*/
+
+ /* Auto-scroll mode is meaningful only in "Update list of packets
+ in real time" captures, so make its toggle button insensitive. */
+ gtk_widget_set_sensitive(GTK_WIDGET(auto_scroll_cb), FALSE);
+
+ /*gtk_widget_set_sensitive(GTK_WIDGET(hide_info_cb), FALSE);*/
+ }
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(multi_files_on_cb))) {
+ /* Ring buffer mode enabled. */
+
+ /* Force at least one of the "file switch" conditions (we need at least one) */
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ring_filesize_cb)) == FALSE &&
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(file_duration_cb)) == FALSE) {
+ if (tb == ring_filesize_cb)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(file_duration_cb), TRUE);
+ else
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ring_filesize_cb), TRUE);
+ }
+
+ gtk_widget_set_sensitive(GTK_WIDGET(ringbuffer_nbf_cb), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ringbuffer_nbf_sb),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ringbuffer_nbf_cb)));
+ gtk_widget_set_sensitive(GTK_WIDGET(ringbuffer_nbf_lb),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ringbuffer_nbf_cb)));
+
+ /* The ring filesize spinbox is sensitive if the "Next capture file
+ after N kilobytes" checkbox is on. */
+ gtk_widget_set_sensitive(GTK_WIDGET(ring_filesize_cb), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ring_filesize_sb),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ring_filesize_cb)));
+ gtk_widget_set_sensitive(GTK_WIDGET(ring_filesize_cbx),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ring_filesize_cb)));
+
+ /* The ring duration spinbox is sensitive if the "Next capture file
+ after N seconds" checkbox is on. */
+ gtk_widget_set_sensitive(GTK_WIDGET(file_duration_cb), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(file_duration_sb),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(file_duration_cb)));
+ gtk_widget_set_sensitive(GTK_WIDGET(file_duration_cbx),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(file_duration_cb)));
+
+ gtk_widget_set_sensitive(GTK_WIDGET(stop_filesize_cb), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(stop_filesize_sb), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(stop_filesize_cbx), FALSE);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(stop_files_cb), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(stop_files_sb),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(stop_files_cb)));
+ gtk_widget_set_sensitive(GTK_WIDGET(stop_files_lb),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(stop_files_cb)));
+ } else {
+ /* Ring buffer mode disabled. */
+ gtk_widget_set_sensitive(GTK_WIDGET(ringbuffer_nbf_cb), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ringbuffer_nbf_sb), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ringbuffer_nbf_lb), FALSE);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(ring_filesize_cb), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ring_filesize_sb),FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(ring_filesize_cbx),FALSE);
+
+ gtk_widget_set_sensitive(GTK_WIDGET(file_duration_cb), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(file_duration_sb),FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(file_duration_cbx),FALSE);
+
+ /* The maximum file size spinbox is sensitive if the "Stop capture
+ after N kilobytes" checkbox is on. */
+ gtk_widget_set_sensitive(GTK_WIDGET(stop_filesize_cb), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(stop_filesize_sb),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(stop_filesize_cb)));
+ gtk_widget_set_sensitive(GTK_WIDGET(stop_filesize_cbx),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(stop_filesize_cb)));
+
+ gtk_widget_set_sensitive(GTK_WIDGET(stop_files_cb), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(stop_files_sb), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(stop_files_lb), FALSE);
+ }
+
+ /* The maximum packet count spinbox is sensitive if the "Stop capture
+ after N packets" checkbox is on. */
+ gtk_widget_set_sensitive(GTK_WIDGET(stop_packets_sb),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(stop_packets_cb)));
+ gtk_widget_set_sensitive(GTK_WIDGET(stop_packets_lb),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(stop_packets_cb)));
+
+ /* The capture duration spinbox is sensitive if the "Stop capture
+ after N seconds" checkbox is on. */
+ gtk_widget_set_sensitive(GTK_WIDGET(stop_duration_sb),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(stop_duration_cb)));
+ gtk_widget_set_sensitive(GTK_WIDGET(stop_duration_cbx),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(stop_duration_cb)));
+}
+
+gboolean dlg_window_present(void)
+{
+ return (cap_open_w?TRUE:FALSE);
+}
+
+#endif /* HAVE_LIBPCAP */
diff --git a/ui/gtk/capture_dlg.h b/ui/gtk/capture_dlg.h
new file mode 100644
index 0000000000..c782596504
--- /dev/null
+++ b/ui/gtk/capture_dlg.h
@@ -0,0 +1,210 @@
+/* capture_dlg.h
+ * Definitions for packet capture windows
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __CAPTURE_DLG_H__
+#define __CAPTURE_DLG_H__
+
+/* extern GtkWidget* airpcap_tb; */
+
+/** @file
+ * "Capture Options" dialog box.
+ * @ingroup dialog_group
+ */
+#include "capture_opts.h"
+
+#ifdef HAVE_PCAP_REMOTE
+struct remote_host {
+ gchar *remote_host; /**< Host name or network address for remote capturing */
+ gchar *remote_port; /**< TCP port of remote RPCAP server */
+ gint auth_type; /**< Authentication type */
+ gchar *auth_username; /**< Remote authentication parameters */
+ gchar *auth_password; /**< Remote authentication parameters */
+ gboolean datatx_udp;
+ gboolean nocap_rpcap;
+ gboolean nocap_local;
+};
+
+typedef struct remote_options_tag {
+ capture_source src_type;
+ struct remote_host remote_host_opts;
+#ifdef HAVE_PCAP_SETSAMPLING
+ capture_sampling sampling_method;
+ int sampling_param;
+#endif
+} remote_options;
+#endif /* HAVE_PCAP_REMOTE */
+
+typedef struct row_options_tag {
+ gchar *name;
+ gchar *display_name;
+ gchar *addresses;
+ gint no_addresses;
+ gchar *cfilter;
+ GList *links;
+ gint active_dlt;
+ gboolean pmode;
+#ifdef HAVE_PCAP_CREATE
+ gboolean monitor_mode_enabled;
+ gboolean monitor_mode_supported;
+#endif
+ gboolean has_snaplen;
+ guint snaplen;
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ gint buffer;
+#endif
+#ifdef HAVE_PCAP_REMOTE
+ remote_options remote_opts;
+#endif
+} interface_row;
+
+typedef struct link_row_tag {
+ gchar *name;
+ gint dlt;
+} link_row;
+
+enum
+{
+ CAPTURE = 0,
+ INTERFACE,
+ LINK,
+ PMODE,
+ SNAPLEN,
+#if defined(HAVE_PCAP_CREATE)
+ BUFFER,
+ MONITOR,
+#elif defined(_WIN32) && !defined(HAVE_PCAP_CREATE)
+ BUFFER,
+#endif
+ FILTER,
+ NUM_COLUMNS
+};
+
+/** Initialize background capture filter syntax checking
+ */
+void capture_filter_init(void);
+
+/** User requested the "Capture Options" dialog box by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+void capture_prep_cb(GtkWidget *widget, gpointer data);
+
+/** User requested capture start by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+void capture_start_cb(GtkWidget *widget, gpointer data);
+
+/** User requested capture stop by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+void capture_stop_cb(GtkWidget *widget, gpointer data);
+
+/** User requested capture restart by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+void capture_restart_cb(GtkWidget *widget, gpointer data);
+
+/* capture start confirmed by "Save unsaved capture", so do it now */
+void capture_start_confirmed(void);
+
+/** User requested the "Capture Airpcap" dialog box by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+void
+capture_air_cb(GtkWidget *widget, gpointer data);
+
+/*
+ * We remember the capture settings for each interface when a capture
+ * is started on it; the next time we select that interface we start
+ * out with those settings.
+ *
+ * XXX - we currently only do that for monitor mode and the link-layer
+ * type; arguably we should do it for the snapshot length, and perhaps
+ * promiscuous mode.
+ */
+typedef struct {
+ gboolean monitor_mode;
+ int linktype;
+} cap_settings_t;
+
+/** Get capture settings for interface
+ *
+ * @param if_name interface name
+ */
+cap_settings_t
+capture_get_cap_settings (gchar *if_name);
+
+GtkTreeModel*
+create_and_fill_model (GtkTreeView *view);
+
+gboolean
+query_tooltip_tree_view_cb (GtkWidget *widget,
+ gint x,
+ gint y,
+ gboolean keyboard_tip,
+ GtkTooltip *tooltip,
+ gpointer data);
+
+void
+activate_monitor (GtkTreeViewColumn *tree_column, GtkCellRenderer *renderer,
+ GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data);
+
+#ifdef HAVE_PCAP_REMOTE
+#define RECENT_KEY_REMOTE_HOST "recent.remote_host"
+
+/** Write all remote hosts to the recent file
+ *
+ * @param rf recent file
+ */
+void
+capture_remote_combo_recent_write_all(FILE *rf);
+
+/** Add a new remote host from the recent file
+ *
+ * @param s string with hostname,port,auth_type
+ * @return TRUE if correctly added
+ */
+gboolean
+capture_remote_combo_add_recent(gchar *s);
+#endif
+
+gboolean
+dlg_window_present(void);
+
+void
+enable_selected_interface(gchar *name, gboolean enable);
+
+void
+options_interface_cb(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *column _U_, gpointer userdata);
+
+#endif /* capture_dlg.h */
diff --git a/ui/gtk/capture_file_dlg.c b/ui/gtk/capture_file_dlg.c
new file mode 100644
index 0000000000..4a2a6c1ed7
--- /dev/null
+++ b/ui/gtk/capture_file_dlg.c
@@ -0,0 +1,1778 @@
+/* capture_file_dlg.c
+ * Dialog boxes for handling capture files
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "packet-range.h"
+#include <epan/filesystem.h>
+#include <epan/addr_resolv.h>
+#include <epan/prefs.h>
+
+#include "../globals.h"
+#include "../alert_box.h"
+#include "../simple_dialog.h"
+#include "../color.h"
+#include "../ui_util.h"
+#include "../color_filters.h"
+#include "../merge.h"
+#include "../util.h"
+#include <wsutil/file_util.h>
+
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/file_dlg.h"
+#include "ui/gtk/capture_file_dlg.h"
+#include "ui/gtk/drag_and_drop.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/menus.h"
+#include "ui/gtk/recent.h"
+#include "ui/gtk/color_dlg.h"
+#include "ui/gtk/new_packet_list.h"
+#ifdef HAVE_LIBPCAP
+#include "ui/gtk/capture_dlg.h"
+#endif
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/range_utils.h"
+#include "ui/gtk/filter_autocomplete.h"
+
+#if _WIN32
+#include <gdk/gdkwin32.h>
+#include <windows.h>
+#include "win32/file_dlg_win32.h"
+#endif
+
+
+static void file_open_ok_cb(GtkWidget *w, gpointer fs);
+static void file_open_destroy_cb(GtkWidget *win, gpointer user_data);
+static void file_merge_ok_cb(GtkWidget *w, gpointer fs);
+static void file_merge_destroy_cb(GtkWidget *win, gpointer user_data);
+static void file_save_as_select_file_type_cb(GtkWidget *w, gpointer data);
+static void file_save_as_ok_cb(GtkWidget *w, gpointer fs);
+static void file_save_as_destroy_cb(GtkWidget *win, gpointer user_data);
+static void file_color_import_ok_cb(GtkWidget *w, gpointer filter_list);
+static void file_color_import_destroy_cb(GtkWidget *win, gpointer user_data);
+static void file_color_export_ok_cb(GtkWidget *w, gpointer filter_list);
+static void file_color_export_destroy_cb(GtkWidget *win, gpointer user_data);
+static void set_file_type_list(GtkWidget *combo_box, int default_file_type);
+
+#define E_FILE_TYPE_COMBO_BOX_KEY "file_type_combo_box"
+#define E_COMPRESSED_CB_KEY "compressed_cb"
+
+#define E_FILE_M_RESOLVE_KEY "file_dlg_mac_resolve_key"
+#define E_FILE_N_RESOLVE_KEY "file_dlg_network_resolve_key"
+#define E_FILE_T_RESOLVE_KEY "file_dlg_transport_resolve_key"
+
+#define E_MERGE_PREPEND_KEY "merge_dlg_prepend_key"
+#define E_MERGE_CHRONO_KEY "merge_dlg_chrono_key"
+#define E_MERGE_APPEND_KEY "merge_dlg_append_key"
+
+
+#define PREVIEW_TABLE_KEY "preview_table_key"
+#define PREVIEW_FILENAME_KEY "preview_filename_key"
+#define PREVIEW_FORMAT_KEY "preview_format_key"
+#define PREVIEW_SIZE_KEY "preview_size_key"
+#define PREVIEW_ELAPSED_KEY "preview_elapsed_key"
+#define PREVIEW_PACKETS_KEY "preview_packets_key"
+#define PREVIEW_FIRST_KEY "preview_first_key"
+
+
+/*
+ * Keep a static pointer to the current "Save Capture File As" window, if
+ * any, so that if somebody tries to do "File:Save" or "File:Save As"
+ * while there's already a "Save Capture File As" window up, we just pop
+ * up the existing one, rather than creating a new one.
+ */
+static GtkWidget *file_save_as_w;
+
+/* XXX - can we make these not be static? */
+static packet_range_t range;
+static gboolean color_selected;
+static GtkWidget *range_tb;
+
+#define PREVIEW_STR_MAX 200
+
+
+/* set a new filename for the preview widget */
+static wtap *
+preview_set_filename(GtkWidget *prev, const gchar *cf_name)
+{
+ GtkWidget *label;
+ wtap *wth;
+ int err = 0;
+ gchar *err_info;
+ gchar string_buff[PREVIEW_STR_MAX];
+ gint64 filesize;
+
+
+ /* init preview labels */
+ label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_FILENAME_KEY);
+ gtk_label_set_text(GTK_LABEL(label), "-");
+ label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
+ gtk_label_set_text(GTK_LABEL(label), "-");
+ label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_SIZE_KEY);
+ gtk_label_set_text(GTK_LABEL(label), "-");
+ label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_ELAPSED_KEY);
+ gtk_label_set_text(GTK_LABEL(label), "-");
+ label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_PACKETS_KEY);
+ gtk_label_set_text(GTK_LABEL(label), "-");
+ label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_FIRST_KEY);
+ gtk_label_set_text(GTK_LABEL(label), "-");
+
+ if(!cf_name) {
+ return NULL;
+ }
+
+ label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_FILENAME_KEY);
+ gtk_label_set_text(GTK_LABEL(label), get_basename(cf_name));
+
+ if (test_for_directory(cf_name) == EISDIR) {
+ label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
+ gtk_label_set_text(GTK_LABEL(label), "directory");
+ return NULL;
+ }
+
+ wth = wtap_open_offline(cf_name, &err, &err_info, TRUE);
+ if (wth == NULL) {
+ label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
+ if(err == WTAP_ERR_FILE_UNKNOWN_FORMAT) {
+ gtk_label_set_text(GTK_LABEL(label), "unknown file format");
+ } else {
+ gtk_label_set_text(GTK_LABEL(label), "error opening file");
+ }
+ return NULL;
+ }
+
+ /* Find the size of the file. */
+ filesize = wtap_file_size(wth, &err);
+ if (filesize == -1) {
+ gtk_label_set_text(GTK_LABEL(label), "error getting file size");
+ wtap_close(wth);
+ return NULL;
+ }
+ g_snprintf(string_buff, PREVIEW_STR_MAX, "%" G_GINT64_MODIFIER "d bytes", filesize);
+ label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_SIZE_KEY);
+ gtk_label_set_text(GTK_LABEL(label), string_buff);
+
+ /* type */
+ g_strlcpy(string_buff, wtap_file_type_string(wtap_file_type(wth)), PREVIEW_STR_MAX);
+ label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_FORMAT_KEY);
+ gtk_label_set_text(GTK_LABEL(label), string_buff);
+
+ return wth;
+}
+
+
+/* do a preview run on the currently selected capture file */
+static void
+preview_do(GtkWidget *prev, wtap *wth)
+{
+ GtkWidget *label;
+ unsigned int elapsed_time;
+ time_t time_preview;
+ time_t time_current;
+ int err = 0;
+ gchar *err_info;
+ gint64 data_offset;
+ const struct wtap_pkthdr *phdr;
+ double start_time = 0; /* seconds, with nsec resolution */
+ double stop_time = 0; /* seconds, with nsec resolution */
+ double cur_time;
+ unsigned int packets = 0;
+ gboolean is_breaked = FALSE;
+ gchar string_buff[PREVIEW_STR_MAX];
+ time_t ti_time;
+ struct tm *ti_tm;
+
+
+ time(&time_preview);
+ while ( (wtap_read(wth, &err, &err_info, &data_offset)) ) {
+ phdr = wtap_phdr(wth);
+ cur_time = wtap_nstime_to_sec(&phdr->ts);
+ if(packets == 0) {
+ start_time = cur_time;
+ stop_time = cur_time;
+ }
+ if (cur_time < start_time) {
+ start_time = cur_time;
+ }
+ if (cur_time > stop_time){
+ stop_time = cur_time;
+ }
+
+ packets++;
+ if(packets%1000 == 0) {
+ /* do we have a timeout? */
+ time(&time_current);
+ if(time_current-time_preview >= (time_t) prefs.gui_fileopen_preview) {
+ is_breaked = TRUE;
+ break;
+ }
+ }
+ }
+
+ if(err != 0) {
+ g_snprintf(string_buff, PREVIEW_STR_MAX, "error after reading %u packets", packets);
+ label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_PACKETS_KEY);
+ gtk_label_set_text(GTK_LABEL(label), string_buff);
+ wtap_close(wth);
+ return;
+ }
+
+ /* packet count */
+ if(is_breaked) {
+ g_snprintf(string_buff, PREVIEW_STR_MAX, "more than %u packets (preview timeout)", packets);
+ } else {
+ g_snprintf(string_buff, PREVIEW_STR_MAX, "%u", packets);
+ }
+ label = g_object_get_data(G_OBJECT(prev), PREVIEW_PACKETS_KEY);
+ gtk_label_set_text(GTK_LABEL(label), string_buff);
+
+ /* first packet */
+ ti_time = (long)start_time;
+ ti_tm = localtime( &ti_time );
+ if(ti_tm) {
+ g_snprintf(string_buff, PREVIEW_STR_MAX,
+ "%04d-%02d-%02d %02d:%02d:%02d",
+ ti_tm->tm_year + 1900,
+ ti_tm->tm_mon + 1,
+ ti_tm->tm_mday,
+ ti_tm->tm_hour,
+ ti_tm->tm_min,
+ ti_tm->tm_sec);
+ } else {
+ g_snprintf(string_buff, PREVIEW_STR_MAX, "?");
+ }
+ label = g_object_get_data(G_OBJECT(prev), PREVIEW_FIRST_KEY);
+ gtk_label_set_text(GTK_LABEL(label), string_buff);
+
+ /* elapsed time */
+ elapsed_time = (unsigned int)(stop_time-start_time);
+ if(elapsed_time/86400) {
+ g_snprintf(string_buff, PREVIEW_STR_MAX, "%02u days %02u:%02u:%02u",
+ elapsed_time/86400, elapsed_time%86400/3600, elapsed_time%3600/60, elapsed_time%60);
+ } else {
+ g_snprintf(string_buff, PREVIEW_STR_MAX, "%02u:%02u:%02u",
+ elapsed_time%86400/3600, elapsed_time%3600/60, elapsed_time%60);
+ }
+ if(is_breaked) {
+ g_snprintf(string_buff, PREVIEW_STR_MAX, "unknown");
+ }
+ label = (GtkWidget *)g_object_get_data(G_OBJECT(prev), PREVIEW_ELAPSED_KEY);
+ gtk_label_set_text(GTK_LABEL(label), string_buff);
+
+ wtap_close(wth);
+}
+
+#if 0
+/* as the dialog layout will look very ugly when using the file chooser preview mechanism,
+ simply use the same layout as in GTK2.0 */
+static void
+update_preview_cb (GtkFileChooser *file_chooser, gpointer data)
+{
+ GtkWidget *prev = GTK_WIDGET (data);
+ char *cf_name;
+ gboolean have_preview;
+
+ cf_name = gtk_file_chooser_get_preview_filename (file_chooser);
+
+ have_preview = preview_set_filename(prev, cf_name);
+
+ g_free (cf_name);
+
+ have_preview = TRUE;
+ gtk_file_chooser_set_preview_widget_active (file_chooser, have_preview);
+}
+#endif
+
+
+/* the filename text entry changed */
+static void
+file_open_entry_changed(GtkWidget *w _U_, gpointer file_sel)
+{
+ GtkWidget *prev = (GtkWidget *)g_object_get_data(G_OBJECT(file_sel), PREVIEW_TABLE_KEY);
+ gchar *cf_name;
+ gboolean have_preview;
+ wtap *wth;
+
+ /* get the filename */
+ cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_sel));
+
+ /* set the filename to the preview */
+ wth = preview_set_filename(prev, cf_name);
+ have_preview = (wth != NULL);
+
+ g_free(cf_name);
+
+ /* make the preview widget sensitive */
+ gtk_widget_set_sensitive(prev, have_preview);
+
+ /*
+ * XXX - if the Open button isn't sensitive, you can't type into
+ * the location bar and select the file or directory you've typed.
+ * See
+ *
+ * https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=1791
+ *
+ * It's not as if allowing users to click Open when they've
+ * selected a file that's not a valid capture file will cause
+ * anything worse than an error dialog, so we'll leave the Open
+ * button sensitive for now. Perhaps making it sensitive if
+ * cf_name is NULL would also work, although I don't know whether
+ * there are any cases where it would be non-null when you've
+ * typed in the location bar.
+ *
+ * XXX - Bug 1791 also notes that, with the line removed, Bill
+ * Meier "somehow managed to get the file chooser window somewhat
+ * wedged in that neither the cancel or open buttons were responsive".
+ * That seems a bit odd, given that, without this line, we're not
+ * monkeying with the Open button's sensitivity, but...
+ */
+#if 0
+ /* make the open/save/... dialog button sensitive */
+
+ gtk_dialog_set_response_sensitive(file_sel, GTK_RESPONSE_ACCEPT, have_preview);
+#endif
+
+ /* do the actual preview */
+ if(have_preview)
+ preview_do(prev, wth);
+}
+
+
+/* copied from summary_dlg.c */
+static GtkWidget *
+add_string_to_table_sensitive(GtkWidget *list, guint *row, const gchar *title, const gchar *value, gboolean sensitive)
+{
+ GtkWidget *label;
+ gchar *indent;
+
+ if(strlen(value) != 0) {
+ indent = g_strdup_printf(" %s", title);
+ } else {
+ indent = g_strdup(title);
+ }
+ label = gtk_label_new(indent);
+ g_free(indent);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
+ gtk_widget_set_sensitive(label, sensitive);
+ gtk_table_attach_defaults(GTK_TABLE(list), label, 0, 1, *row, *row+1);
+
+ label = gtk_label_new(value);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
+ gtk_widget_set_sensitive(label, sensitive);
+ gtk_table_attach_defaults(GTK_TABLE(list), label, 1, 2, *row, *row+1);
+
+ *row = *row + 1;
+
+ return label;
+}
+
+static GtkWidget *
+add_string_to_table(GtkWidget *list, guint *row, const gchar *title, const gchar *value)
+{
+ return add_string_to_table_sensitive(list, row, title, value, TRUE);
+}
+
+
+
+static GtkWidget *
+preview_new(void)
+{
+ GtkWidget *table, *label;
+ guint row;
+
+ table = gtk_table_new(1, 2, FALSE);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 6);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 3);
+ row = 0;
+
+ label = add_string_to_table(table, &row, "Filename:", "-");
+ gtk_widget_set_size_request(label, DEF_WIDTH/3, -1);
+ g_object_set_data(G_OBJECT(table), PREVIEW_FILENAME_KEY, label);
+ label = add_string_to_table(table, &row, "Format:", "-");
+ g_object_set_data(G_OBJECT(table), PREVIEW_FORMAT_KEY, label);
+ label = add_string_to_table(table, &row, "Size:", "-");
+ g_object_set_data(G_OBJECT(table), PREVIEW_SIZE_KEY, label);
+ label = add_string_to_table(table, &row, "Packets:", "-");
+ g_object_set_data(G_OBJECT(table), PREVIEW_PACKETS_KEY, label);
+ label = add_string_to_table(table, &row, "First Packet:", "-");
+ g_object_set_data(G_OBJECT(table), PREVIEW_FIRST_KEY, label);
+ label = add_string_to_table(table, &row, "Elapsed time:", "-");
+ g_object_set_data(G_OBJECT(table), PREVIEW_ELAPSED_KEY, label);
+
+ return table;
+}
+
+/*
+ * Keep a static pointer to the current "Open Capture File" window, if
+ * any, so that if somebody tries to do "File:Open" while there's already
+ * an "Open Capture File" window up, we just pop up the existing one,
+ * rather than creating a new one.
+ */
+static GtkWidget *file_open_w;
+
+/* Open a file */
+static void
+file_open_cmd(GtkWidget *w)
+{
+#if _WIN32
+ win32_open_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)));
+#else /* _WIN32 */
+ GtkWidget *main_hb, *main_vb, *filter_hbox, *filter_bt, *filter_te,
+ *m_resolv_cb, *n_resolv_cb, *t_resolv_cb, *prev;
+ /* No Apply button, and "OK" just sets our text widget, it doesn't
+ activate it (i.e., it doesn't cause us to try to open the file). */
+ static construct_args_t args = {
+ "Wireshark: Read Filter",
+ FALSE,
+ FALSE,
+ TRUE
+ };
+
+ if (file_open_w != NULL) {
+ /* There's already an "Open Capture File" dialog box; reactivate it. */
+ reactivate_window(file_open_w);
+ return;
+ }
+
+ file_open_w = file_selection_new("Wireshark: Open Capture File",
+ FILE_SELECTION_OPEN);
+ /* it's annoying, that the file chooser dialog is already shown here,
+ so we cannot use the correct gtk_window_set_default_size() to resize it */
+ gtk_widget_set_size_request(file_open_w, DEF_WIDTH, DEF_HEIGHT);
+
+ switch (prefs.gui_fileopen_style) {
+
+ case FO_STYLE_LAST_OPENED:
+ /* The user has specified that we should start out in the last directory
+ we looked in. If we've already opened a file, use its containing
+ directory, if we could determine it, as the directory, otherwise
+ use the "last opened" directory saved in the preferences file if
+ there was one. */
+ /* This is now the default behaviour in file_selection_new() */
+ break;
+
+ case FO_STYLE_SPECIFIED:
+ /* The user has specified that we should always start out in a
+ specified directory; if they've specified that directory,
+ start out by showing the files in that dir. */
+ if (prefs.gui_fileopen_dir[0] != '\0')
+ file_selection_set_current_folder(file_open_w, prefs.gui_fileopen_dir);
+ break;
+ }
+
+
+ main_hb = gtk_hbox_new(FALSE, 3);
+ file_selection_set_extra_widget(file_open_w, main_hb);
+ gtk_widget_show(main_hb);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_box_pack_start(GTK_BOX(main_hb), main_vb, FALSE, FALSE, 0);
+ gtk_widget_show(main_vb);
+
+ /* filter row */
+ filter_hbox = gtk_hbox_new(FALSE, 1);
+ gtk_container_set_border_width(GTK_CONTAINER(filter_hbox), 0);
+ gtk_box_pack_start(GTK_BOX(main_vb), filter_hbox, FALSE, FALSE, 0);
+ gtk_widget_show(filter_hbox);
+
+ filter_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
+ g_signal_connect(filter_bt, "clicked",
+ G_CALLBACK(display_filter_construct_cb), &args);
+ g_signal_connect(filter_bt, "destroy",
+ G_CALLBACK(filter_button_destroy_cb), NULL);
+ gtk_box_pack_start(GTK_BOX(filter_hbox), filter_bt, FALSE, TRUE, 0);
+ gtk_widget_show(filter_bt);
+ gtk_widget_set_tooltip_text(filter_bt, "Open the \"Display Filter\" dialog, to edit/apply filters");
+
+ filter_te = gtk_entry_new();
+ g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
+ gtk_box_pack_start(GTK_BOX(filter_hbox), filter_te, TRUE, TRUE, 3);
+ g_signal_connect(filter_te, "changed",
+ G_CALLBACK(filter_te_syntax_check_cb), NULL);
+ g_object_set_data(G_OBJECT(filter_hbox), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+ g_signal_connect(filter_te, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
+ g_signal_connect(file_open_w, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
+ colorize_filter_te_as_empty(filter_te);
+ gtk_widget_show(filter_te);
+ gtk_widget_set_tooltip_text(filter_te, "Enter a display filter.");
+
+ g_object_set_data(G_OBJECT(file_open_w), E_RFILTER_TE_KEY, filter_te);
+
+ /* resolve buttons */
+ m_resolv_cb = gtk_check_button_new_with_mnemonic("Enable _MAC name resolution");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m_resolv_cb),
+ gbl_resolv_flags & RESOLV_MAC);
+ gtk_box_pack_start(GTK_BOX(main_vb), m_resolv_cb, FALSE, FALSE, 0);
+ g_object_set_data(G_OBJECT(file_open_w),
+ E_FILE_M_RESOLVE_KEY, m_resolv_cb);
+ gtk_widget_show(m_resolv_cb);
+
+ n_resolv_cb = gtk_check_button_new_with_mnemonic("Enable _network name resolution");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(n_resolv_cb),
+ gbl_resolv_flags & RESOLV_NETWORK);
+ gtk_box_pack_start(GTK_BOX(main_vb), n_resolv_cb, FALSE, FALSE, 0);
+ gtk_widget_show(n_resolv_cb);
+ g_object_set_data(G_OBJECT(file_open_w), E_FILE_N_RESOLVE_KEY, n_resolv_cb);
+ t_resolv_cb = gtk_check_button_new_with_mnemonic("Enable _transport name resolution");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(t_resolv_cb),
+ gbl_resolv_flags & RESOLV_TRANSPORT);
+ gtk_box_pack_start(GTK_BOX(main_vb), t_resolv_cb, FALSE, FALSE, 0);
+ gtk_widget_show(t_resolv_cb);
+ g_object_set_data(G_OBJECT(file_open_w), E_FILE_T_RESOLVE_KEY, t_resolv_cb);
+
+ g_signal_connect(file_open_w, "destroy",
+ G_CALLBACK(file_open_destroy_cb), NULL);
+
+ /* preview widget */
+ prev = preview_new();
+ g_object_set_data(G_OBJECT(file_open_w), PREVIEW_TABLE_KEY, prev);
+ gtk_widget_show_all(prev);
+ gtk_box_pack_start(GTK_BOX(main_hb), prev, TRUE, TRUE, 0);
+
+ g_signal_connect(GTK_FILE_CHOOSER(file_open_w), "selection-changed",
+ G_CALLBACK(file_open_entry_changed), file_open_w);
+ file_open_entry_changed(file_open_w, file_open_w);
+
+ g_object_set_data(G_OBJECT(file_open_w), E_DFILTER_TE_KEY,
+ g_object_get_data(G_OBJECT(w), E_DFILTER_TE_KEY));
+ if (gtk_dialog_run(GTK_DIALOG(file_open_w)) == GTK_RESPONSE_ACCEPT)
+ {
+ file_open_ok_cb(file_open_w, file_open_w);
+ }
+ else window_destroy(file_open_w);
+#endif /* _WIN32 */
+}
+
+static void file_open_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
+{
+ switch(btn) {
+ case(ESD_BTN_SAVE):
+ /* save file first */
+ file_save_as_cmd(after_save_open_dialog, data, FALSE);
+ break;
+ case(ESD_BTN_DONT_SAVE):
+ cf_close(&cfile);
+ file_open_cmd(data);
+ break;
+ case(ESD_BTN_CANCEL):
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+void
+file_open_cmd_cb(GtkWidget *widget, gpointer data _U_) {
+ gpointer dialog;
+
+ if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
+ /* user didn't save his current file, ask him */
+ dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE_CANCEL,
+ "%sSave capture file before opening a new one?%s\n\n"
+ "If you open a new capture file without saving, your capture data will be discarded.",
+ simple_dialog_primary_start(), simple_dialog_primary_end());
+ simple_dialog_set_cb(dialog, file_open_answered_cb, widget);
+ } else {
+ /* unchanged file, just open a new one */
+ file_open_cmd(widget);
+ }
+}
+
+/* user pressed "open" button */
+static void
+file_open_ok_cb(GtkWidget *w, gpointer fs) {
+ gchar *cf_name, *s;
+ const gchar *rfilter;
+ GtkWidget *filter_te, *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
+ dfilter_t *rfcode = NULL;
+ int err;
+
+ cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
+ filter_te = (GtkWidget *)g_object_get_data(G_OBJECT(w), E_RFILTER_TE_KEY);
+ rfilter = gtk_entry_get_text(GTK_ENTRY(filter_te));
+ if (!dfilter_compile(rfilter, &rfcode)) {
+ bad_dfilter_alert_box(rfilter);
+ g_free(cf_name);
+ return;
+ }
+
+ /* Perhaps the user specified a directory instead of a file.
+ Check whether they did. */
+ if (test_for_directory(cf_name) == EISDIR) {
+ /* It's a directory - set the file selection box to display that
+ directory, don't try to open the directory as a capture file. */
+ set_last_open_dir(cf_name);
+ g_free(cf_name);
+ file_selection_set_current_folder(fs, get_last_open_dir());
+ return;
+ }
+
+ /* Try to open the capture file. */
+ if (cf_open(&cfile, cf_name, FALSE, &err) != CF_OK) {
+ /* We couldn't open it; don't dismiss the open dialog box,
+ just leave it around so that the user can, after they
+ dismiss the alert box popped up for the open error,
+ try again. */
+ if (rfcode != NULL)
+ dfilter_free(rfcode);
+ g_free(cf_name);
+
+ /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
+ * as this will prevent the user from closing the now existing error
+ * message, simply close the dialog (this is the best we can do here). */
+ if (file_open_w)
+ window_destroy(file_open_w);
+
+ return;
+ }
+
+ /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
+ it closed the previous capture file, and thus destroyed any
+ previous read filter attached to "cf"). */
+ cfile.rfcode = rfcode;
+
+ /* Set the global resolving variable */
+ gbl_resolv_flags = prefs.name_resolve;
+ m_resolv_cb = (GtkWidget *)g_object_get_data(G_OBJECT(w), E_FILE_M_RESOLVE_KEY);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (m_resolv_cb)))
+ gbl_resolv_flags |= RESOLV_MAC;
+ else
+ gbl_resolv_flags &= ~RESOLV_MAC;
+ n_resolv_cb = (GtkWidget *)g_object_get_data(G_OBJECT(w), E_FILE_N_RESOLVE_KEY);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (n_resolv_cb)))
+ gbl_resolv_flags |= RESOLV_NETWORK;
+ else
+ gbl_resolv_flags &= ~RESOLV_NETWORK;
+ t_resolv_cb = (GtkWidget *)g_object_get_data(G_OBJECT(w), E_FILE_T_RESOLVE_KEY);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (t_resolv_cb)))
+ gbl_resolv_flags |= RESOLV_TRANSPORT;
+ else
+ gbl_resolv_flags &= ~RESOLV_TRANSPORT;
+
+ /* We've crossed the Rubicon; get rid of the file selection box. */
+ window_destroy(GTK_WIDGET (fs));
+
+ switch (cf_read(&cfile, FALSE)) {
+
+ case CF_READ_OK:
+ case CF_READ_ERROR:
+ /* Just because we got an error, that doesn't mean we were unable
+ to read any of the file; we handle what we could get from the
+ file. */
+ break;
+
+ case CF_READ_ABORTED:
+ /* The user bailed out of re-reading the capture file; the
+ capture file has been closed - just free the capture file name
+ string and return (without changing the last containing
+ directory). */
+ g_free(cf_name);
+ return;
+ }
+
+ /* Save the name of the containing directory specified in the path name,
+ if any; we can write over cf_name, which is a good thing, given that
+ "get_dirname()" does write over its argument. */
+ s = get_dirname(cf_name);
+ set_last_open_dir(s);
+
+ g_free(cf_name);
+}
+
+static void
+file_open_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+ /* Note that we no longer have a "Open Capture File" dialog box. */
+ file_open_w = NULL;
+}
+
+/*
+ * Keep a static pointer to the current "Merge Capture File" window, if
+ * any, so that if somebody tries to do "File:Merge" while there's already
+ * an "Merge Capture File" window up, we just pop up the existing one,
+ * rather than creating a new one.
+ */
+static GtkWidget *file_merge_w;
+
+/* Merge existing with another file */
+static void
+file_merge_cmd(GtkWidget *w)
+{
+#if _WIN32
+ win32_merge_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)));
+ new_packet_list_freeze();
+ new_packet_list_thaw();
+#else /* _WIN32 */
+ GtkWidget *main_hb, *main_vb, *ft_hb, *ft_lb, *ft_combo_box, *filter_hbox,
+ *filter_bt, *filter_te, *prepend_rb, *chrono_rb,
+ *append_rb, *prev;
+
+ /* No Apply button, and "OK" just sets our text widget, it doesn't
+ activate it (i.e., it doesn't cause us to try to open the file). */
+ static construct_args_t args = {
+ "Wireshark: Read Filter",
+ FALSE,
+ FALSE,
+ TRUE
+ };
+
+ if (file_merge_w != NULL) {
+ /* There's already an "Merge Capture File" dialog box; reactivate it. */
+ reactivate_window(file_merge_w);
+ return;
+ }
+
+ /* Default to saving all packets, in the file's current format. */
+
+ file_merge_w = file_selection_new("Wireshark: Merge with Capture File",
+ FILE_SELECTION_OPEN);
+ /* it's annoying, that the file chooser dialog is already shown here,
+ so we cannot use the correct gtk_window_set_default_size() to resize it */
+ gtk_widget_set_size_request(file_merge_w, DEF_WIDTH, DEF_HEIGHT);
+
+ switch (prefs.gui_fileopen_style) {
+
+ case FO_STYLE_LAST_OPENED:
+ /* The user has specified that we should start out in the last directory
+ we looked in. If we've already opened a file, use its containing
+ directory, if we could determine it, as the directory, otherwise
+ use the "last opened" directory saved in the preferences file if
+ there was one. */
+ /* This is now the default behaviour in file_selection_new() */
+ break;
+
+ case FO_STYLE_SPECIFIED:
+ /* The user has specified that we should always start out in a
+ specified directory; if they've specified that directory,
+ start out by showing the files in that dir. */
+ if (prefs.gui_fileopen_dir[0] != '\0')
+ file_selection_set_current_folder(file_merge_w, prefs.gui_fileopen_dir);
+ break;
+ }
+
+ main_hb = gtk_hbox_new(FALSE, 3);
+ file_selection_set_extra_widget(file_merge_w, main_hb);
+ gtk_widget_show(main_hb);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_box_pack_start(GTK_BOX(main_hb), main_vb, FALSE, FALSE, 0);
+ gtk_widget_show(main_vb);
+
+ /* File type row */
+ range_tb = NULL;
+ ft_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
+ gtk_widget_show(ft_hb);
+
+ ft_lb = gtk_label_new("Merged output file type:");
+ gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0);
+ gtk_widget_show(ft_lb);
+
+ ft_combo_box = ws_combo_box_new_text_and_pointer();
+
+ /* Generate the list of file types we can save. */
+ set_file_type_list(ft_combo_box, cfile.cd_t);
+ gtk_box_pack_start(GTK_BOX(ft_hb), ft_combo_box, FALSE, FALSE, 0);
+ gtk_widget_show(ft_combo_box);
+ g_object_set_data(G_OBJECT(file_merge_w), E_FILE_TYPE_COMBO_BOX_KEY, ft_combo_box);
+ ws_combo_box_set_active(GTK_COMBO_BOX(ft_combo_box), 0); /* No callback */
+
+ filter_hbox = gtk_hbox_new(FALSE, 1);
+ gtk_container_set_border_width(GTK_CONTAINER(filter_hbox), 0);
+ gtk_box_pack_start(GTK_BOX(main_vb), filter_hbox, FALSE, FALSE, 0);
+ gtk_widget_show(filter_hbox);
+
+ filter_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
+ g_signal_connect(filter_bt, "clicked",
+ G_CALLBACK(display_filter_construct_cb), &args);
+ g_signal_connect(filter_bt, "destroy",
+ G_CALLBACK(filter_button_destroy_cb), NULL);
+ gtk_box_pack_start(GTK_BOX(filter_hbox), filter_bt, FALSE, TRUE, 0);
+ gtk_widget_show(filter_bt);
+ gtk_widget_set_tooltip_text(filter_bt, "Open the \"Display Filter\" dialog, to edit/apply filters");
+
+ filter_te = gtk_entry_new();
+ g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
+ gtk_box_pack_start(GTK_BOX(filter_hbox), filter_te, TRUE, TRUE, 3);
+ g_signal_connect(filter_te, "changed",
+ G_CALLBACK(filter_te_syntax_check_cb), NULL);
+ g_object_set_data(G_OBJECT(filter_hbox), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+ g_signal_connect(filter_te, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
+ g_signal_connect(file_merge_w, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
+ colorize_filter_te_as_empty(filter_te);
+ gtk_widget_show(filter_te);
+ gtk_widget_set_tooltip_text(filter_te, "Enter a display filter.");
+
+ g_object_set_data(G_OBJECT(file_merge_w), E_RFILTER_TE_KEY, filter_te);
+
+ prepend_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL,
+ "Prepend packets to existing file");
+ gtk_widget_set_tooltip_text(prepend_rb, "The resulting file contains the packets from the selected, followed by the packets from the currently loaded file, the packet timestamps will be ignored.");
+ gtk_box_pack_start(GTK_BOX(main_vb), prepend_rb, FALSE, FALSE, 0);
+ g_object_set_data(G_OBJECT(file_merge_w),
+ E_MERGE_PREPEND_KEY, prepend_rb);
+ gtk_widget_show(prepend_rb);
+
+ chrono_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(prepend_rb), "Merge packets chronologically");
+ gtk_widget_set_tooltip_text(chrono_rb, "The resulting file contains all the packets from the currently loaded and the selected file, sorted by the packet timestamps.");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chrono_rb), TRUE);
+ gtk_box_pack_start(GTK_BOX(main_vb), chrono_rb, FALSE, FALSE, 0);
+ gtk_widget_show(chrono_rb);
+ g_object_set_data(G_OBJECT(file_merge_w), E_MERGE_CHRONO_KEY, chrono_rb);
+
+ append_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(prepend_rb), "Append packets to existing file");
+ gtk_widget_set_tooltip_text(append_rb, "The resulting file contains the packets from the currently loaded, followed by the packets from the selected file, the packet timestamps will be ignored.");
+ gtk_box_pack_start(GTK_BOX(main_vb), append_rb, FALSE, FALSE, 0);
+ gtk_widget_show(append_rb);
+ g_object_set_data(G_OBJECT(file_merge_w), E_MERGE_APPEND_KEY, append_rb);
+
+ g_signal_connect(file_merge_w, "destroy",
+ G_CALLBACK(file_merge_destroy_cb), NULL);
+
+ /* preview widget */
+ prev = preview_new();
+ g_object_set_data(G_OBJECT(file_merge_w), PREVIEW_TABLE_KEY, prev);
+ gtk_widget_show_all(prev);
+ gtk_box_pack_start(GTK_BOX(main_hb), prev, TRUE, TRUE, 0);
+
+ g_signal_connect(GTK_FILE_CHOOSER(file_merge_w), "selection-changed",
+ G_CALLBACK(file_open_entry_changed), file_merge_w);
+ file_open_entry_changed(file_merge_w, file_merge_w);
+
+ g_object_set_data(G_OBJECT(file_merge_w), E_DFILTER_TE_KEY,
+ g_object_get_data(G_OBJECT(w), E_DFILTER_TE_KEY));
+ if (gtk_dialog_run(GTK_DIALOG(file_merge_w)) == GTK_RESPONSE_ACCEPT)
+ {
+ file_merge_ok_cb(file_merge_w, file_merge_w);
+ }
+ else window_destroy(file_merge_w);
+#endif /* _WIN32 */
+}
+
+static void file_merge_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
+{
+ switch(btn) {
+ case(ESD_BTN_OK):
+ /* save file first */
+ file_save_as_cmd(after_save_merge_dialog, data, FALSE);
+ break;
+ case(ESD_BTN_CANCEL):
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+void
+file_merge_cmd_cb(GtkWidget *widget, gpointer data _U_) {
+ gpointer dialog;
+
+ if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
+ /* user didn't saved his current file, ask him */
+ dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_OK_CANCEL,
+ "%sSave the capture file before merging to another one?%s\n\n"
+ "A temporary capture file can't be merged.",
+ simple_dialog_primary_start(), simple_dialog_primary_end());
+ simple_dialog_set_cb(dialog, file_merge_answered_cb, widget);
+ } else {
+ /* unchanged file, just start to merge */
+ file_merge_cmd(widget);
+ }
+}
+
+
+static void
+file_merge_ok_cb(GtkWidget *w, gpointer fs) {
+ gchar *cf_name, *s;
+ const gchar *rfilter;
+ GtkWidget *ft_combo_box, *filter_te, *rb;
+ dfilter_t *rfcode = NULL;
+ int err;
+ cf_status_t merge_status;
+ char *in_filenames[2];
+ char *tmpname;
+ gpointer ptr;
+ int file_type;
+
+
+ cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
+ filter_te = (GtkWidget *)g_object_get_data(G_OBJECT(w), E_RFILTER_TE_KEY);
+ rfilter = gtk_entry_get_text(GTK_ENTRY(filter_te));
+ if (!dfilter_compile(rfilter, &rfcode)) {
+ bad_dfilter_alert_box(rfilter);
+ g_free(cf_name);
+ return;
+ }
+
+ ft_combo_box = (GtkWidget *)g_object_get_data(G_OBJECT(w), E_FILE_TYPE_COMBO_BOX_KEY);
+ if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(ft_combo_box), &ptr)) {
+ g_assert_not_reached(); /* Programming error: somehow nothing is active */
+ }
+ file_type = GPOINTER_TO_INT(ptr);
+
+ /* Perhaps the user specified a directory instead of a file.
+ Check whether they did. */
+ if (test_for_directory(cf_name) == EISDIR) {
+ /* It's a directory - set the file selection box to display that
+ directory, don't try to open the directory as a capture file. */
+ set_last_open_dir(cf_name);
+ g_free(cf_name);
+ file_selection_set_current_folder(fs, get_last_open_dir());
+ return;
+ }
+
+ /* merge or append the two files */
+ rb = (GtkWidget *)g_object_get_data(G_OBJECT(w), E_MERGE_CHRONO_KEY);
+ tmpname = NULL;
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rb))) {
+ /* chronological order */
+ in_filenames[0] = cfile.filename;
+ in_filenames[1] = cf_name;
+ merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type, FALSE);
+ } else {
+ rb = (GtkWidget *)g_object_get_data(G_OBJECT(w), E_MERGE_PREPEND_KEY);
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rb))) {
+ /* prepend file */
+ in_filenames[0] = cf_name;
+ in_filenames[1] = cfile.filename;
+ merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type,
+ TRUE);
+ } else {
+ /* append file */
+ in_filenames[0] = cfile.filename;
+ in_filenames[1] = cf_name;
+ merge_status = cf_merge_files(&tmpname, 2, in_filenames, file_type,
+ TRUE);
+ }
+ }
+
+ g_free(cf_name);
+
+ if (merge_status != CF_OK) {
+ if (rfcode != NULL)
+ dfilter_free(rfcode);
+ g_free(tmpname);
+ return;
+ }
+
+ cf_close(&cfile);
+
+ /* We've crossed the Rubicon; get rid of the file selection box. */
+ window_destroy(GTK_WIDGET (fs));
+
+ /* Try to open the merged capture file. */
+ if (cf_open(&cfile, tmpname, TRUE /* temporary file */, &err) != CF_OK) {
+ /* We couldn't open it; don't dismiss the open dialog box,
+ just leave it around so that the user can, after they
+ dismiss the alert box popped up for the open error,
+ try again. */
+ if (rfcode != NULL)
+ dfilter_free(rfcode);
+ g_free(tmpname);
+ /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
+ * as this will prevent the user from closing the now existing error
+ * message, simply close the dialog (this is the best we can do here). */
+ if (file_open_w)
+ window_destroy(file_open_w);
+ return;
+ }
+ g_free(tmpname);
+
+ /* Attach the new read filter to "cf" ("cf_open()" succeeded, so
+ it closed the previous capture file, and thus destroyed any
+ previous read filter attached to "cf"). */
+ cfile.rfcode = rfcode;
+
+ switch (cf_read(&cfile, FALSE)) {
+
+ case CF_READ_OK:
+ case CF_READ_ERROR:
+ /* Just because we got an error, that doesn't mean we were unable
+ to read any of the file; we handle what we could get from the
+ file. */
+ break;
+
+ case CF_READ_ABORTED:
+ /* The user bailed out of re-reading the capture file; the
+ capture file has been closed - just free the capture file name
+ string and return (without changing the last containing
+ directory). */
+ return;
+ }
+
+ /* Save the name of the containing directory specified in the path name,
+ if any; we can write over cf_merged_name, which is a good thing, given that
+ "get_dirname()" does write over its argument. */
+ s = get_dirname(tmpname);
+ set_last_open_dir(s);
+}
+
+static void
+file_merge_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+ /* Note that we no longer have a "Merge Capture File" dialog box. */
+ file_merge_w = NULL;
+}
+
+
+static void file_close_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
+{
+ switch(btn) {
+ case(ESD_BTN_SAVE):
+ /* save file first */
+ file_save_as_cmd(after_save_close_file, NULL, FALSE);
+ break;
+ case(ESD_BTN_DONT_SAVE):
+ cf_close(&cfile);
+ break;
+ case(ESD_BTN_CANCEL):
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+/* Close a file */
+void
+file_close_cmd_cb(GtkWidget *widget _U_, gpointer data _U_) {
+ gpointer dialog;
+
+ if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
+ /* user didn't saved his current file, ask him */
+ dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE_CANCEL,
+ "%sSave capture file before closing it?%s\n\n"
+ "If you close without saving, your capture data will be discarded.",
+ simple_dialog_primary_start(), simple_dialog_primary_end());
+
+ simple_dialog_set_cb(dialog, file_close_answered_cb, NULL);
+ } else {
+ /* unchanged file, just close it */
+ cf_close(&cfile);
+ }
+}
+
+void
+file_save_cmd_cb(GtkWidget *w _U_, gpointer data _U_) {
+ /* If the file's already been saved, do nothing. */
+ if (cfile.user_saved)
+ return;
+
+ /* Do a "Save As". */
+ file_save_as_cmd(after_save_no_action, NULL, FALSE);
+}
+
+static gboolean
+can_save_with_wiretap(int ft)
+{
+ /* To save a file with Wiretap, Wiretap has to handle that format,
+ and its code to handle that format must be able to write a file
+ with this file's encapsulation type. */
+ return wtap_dump_can_open(ft) && wtap_dump_can_write_encap(ft, cfile.lnk_t);
+}
+
+
+/* Attach a list of the valid 'save as' file types to a combo_box by
+ checking what Wiretap supports. Make the default type the first
+ in the list.
+ */
+static void
+set_file_type_list(GtkWidget *combo_box, int default_file_type)
+{
+ int ft;
+ int other_file_type = -1;
+
+ /* Can we save this file in the default file type? */
+ if (!can_save_with_wiretap(default_file_type)) {
+ /* No - can we save it as a pcap-NG file? */
+ if (can_save_with_wiretap(WTAP_FILE_PCAPNG)) {
+ /* Yes - default to pcap-NG, instead. */
+ default_file_type = WTAP_FILE_PCAPNG;
+ } else {
+ /* OK, find the first file type we *can* save it as. */
+ for (ft = 0; ft < WTAP_NUM_FILE_TYPES; ft++) {
+ if (can_save_with_wiretap(ft)) {
+ /* OK, got it. */
+ default_file_type = ft;
+ }
+ }
+ }
+ }
+
+ /* We should not get here unless there's at least one file type
+ we can save this as - otherwise, the "Save As..." menu item
+ should be disabled. */
+
+ /* Put the default file format first in the list. */
+ ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(combo_box),
+ wtap_file_type_string(default_file_type),
+ GINT_TO_POINTER(default_file_type));
+ /* If it's pcap, put pcap-NG right after it; otherwise, if it's pcap-NG,
+ put pcap right after it. */
+ if (default_file_type == WTAP_FILE_PCAP) {
+ if (can_save_with_wiretap(WTAP_FILE_PCAPNG))
+ other_file_type = WTAP_FILE_PCAPNG;
+ } else if (default_file_type == WTAP_FILE_PCAPNG) {
+ if (can_save_with_wiretap(WTAP_FILE_PCAP))
+ other_file_type = WTAP_FILE_PCAP;
+ }
+ if (other_file_type != -1) {
+ ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(combo_box),
+ wtap_file_type_string(other_file_type),
+ GINT_TO_POINTER(other_file_type));
+ }
+
+ /* Add all the other file types. */
+ for (ft = 0; ft < WTAP_NUM_FILE_TYPES; ft++) {
+ if (ft == default_file_type || ft == other_file_type)
+ continue; /* we've already done this one */
+ if (can_save_with_wiretap(ft)) {
+ /* OK, we can write it out in this type. */
+ ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(combo_box), wtap_file_type_string(ft), GINT_TO_POINTER(ft));
+ }
+ }
+}
+
+static void
+file_save_as_select_file_type_cb(GtkWidget *w, gpointer data _U_)
+{
+ int new_file_type;
+ gpointer ptr;
+ GtkWidget *compressed_cb;
+
+ if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(w), &ptr)) {
+ g_assert_not_reached(); /* Programming error: somehow nothing is active */
+ }
+ new_file_type = GPOINTER_TO_INT(ptr);
+
+ compressed_cb = (GtkWidget *)g_object_get_data(G_OBJECT(file_save_as_w), E_COMPRESSED_CB_KEY);
+ gtk_widget_set_sensitive(compressed_cb, wtap_dump_can_compress(new_file_type));
+}
+
+/*
+ * Update various dynamic parts of the range controls; called from outside
+ * the file dialog code whenever the packet counts change.
+ */
+void
+file_save_update_dynamics(void)
+{
+ if (file_save_as_w == NULL) {
+ /* We don't currently have a "Save As..." dialog box up. */
+ return;
+ }
+
+ range_update_dynamics(range_tb);
+}
+
+
+action_after_save_e action_after_save_g;
+gpointer action_after_save_data_g;
+
+
+void
+file_save_as_cmd(action_after_save_e action_after_save, gpointer action_after_save_data, gboolean save_only_displayed)
+{
+#if _WIN32
+ win32_save_as_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), action_after_save, action_after_save_data);
+#else /* _WIN32 */
+ GtkWidget *main_vb, *ft_hb, *ft_lb, *ft_combo_box, *range_fr, *compressed_cb;
+
+ if (file_save_as_w != NULL) {
+ /* There's already an "Save Capture File As" dialog box; reactivate it. */
+ reactivate_window(file_save_as_w);
+ return;
+ }
+
+ /* Default to saving all packets, in the file's current format. */
+
+ /* init the packet range */
+ packet_range_init(&range);
+ range.process_filtered = save_only_displayed;
+
+ /* build the file selection */
+ file_save_as_w = file_selection_new ("Wireshark: Save Capture File As",
+ FILE_SELECTION_SAVE);
+
+ /* as the dialog might already be gone, when using this values, we cannot
+ * set data to the dialog object, but keep global values */
+ action_after_save_g = action_after_save;
+ action_after_save_data_g = action_after_save_data;
+
+ /* Container for each row of widgets */
+
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ file_selection_set_extra_widget(file_save_as_w, main_vb);
+ gtk_widget_show(main_vb);
+
+ /*** Packet Range frame ***/
+ range_fr = gtk_frame_new("Packet Range");
+ gtk_box_pack_start(GTK_BOX(main_vb), range_fr, FALSE, FALSE, 0);
+ gtk_widget_show(range_fr);
+
+ /* range table */
+ range_tb = range_new(&range);
+ gtk_container_add(GTK_CONTAINER(range_fr), range_tb);
+ gtk_widget_show(range_tb);
+
+ /* File type row */
+ ft_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(main_vb), ft_hb);
+ gtk_widget_show(ft_hb);
+
+ ft_lb = gtk_label_new("File type:");
+ gtk_box_pack_start(GTK_BOX(ft_hb), ft_lb, FALSE, FALSE, 0);
+ gtk_widget_show(ft_lb);
+
+ ft_combo_box = ws_combo_box_new_text_and_pointer();
+
+ /* Generate the list of file types we can save. */
+ set_file_type_list(ft_combo_box, cfile.cd_t);
+ gtk_box_pack_start(GTK_BOX(ft_hb), ft_combo_box, FALSE, FALSE, 0);
+ gtk_widget_show(ft_combo_box);
+ g_object_set_data(G_OBJECT(file_save_as_w), E_FILE_TYPE_COMBO_BOX_KEY, ft_combo_box);
+
+ /* dynamic values in the range frame */
+ range_update_dynamics(range_tb);
+
+ /* compressed */
+ compressed_cb = gtk_check_button_new_with_label("Compress with gzip");
+ gtk_container_add(GTK_CONTAINER(ft_hb), compressed_cb);
+ /* XXX - disable output compression for now, as this doesn't work with the
+ * current optimization to simply copy a capture file if it's using the same
+ * encapsulation ... */
+ /* the rest of the implementation is just working fine :-( */
+#if 0
+ gtk_widget_show(compressed_cb);
+#endif
+ g_object_set_data(G_OBJECT(file_save_as_w), E_COMPRESSED_CB_KEY, compressed_cb);
+ /* Ok: now "select" the default filetype which invokes file_save_as_select_file_type_cb */
+ g_signal_connect(ft_combo_box, "changed", G_CALLBACK(file_save_as_select_file_type_cb), NULL);
+ ws_combo_box_set_active(GTK_COMBO_BOX(ft_combo_box), 0);
+
+ g_signal_connect(file_save_as_w, "destroy",
+ G_CALLBACK(file_save_as_destroy_cb), NULL);
+
+ if (gtk_dialog_run(GTK_DIALOG(file_save_as_w)) == GTK_RESPONSE_ACCEPT) {
+ file_save_as_ok_cb(file_save_as_w, file_save_as_w);
+ } else {
+ window_destroy(file_save_as_w);
+ }
+#endif /* _WIN32 */
+}
+
+void
+file_save_as_cmd_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ file_save_as_cmd(after_save_no_action, NULL, TRUE);
+}
+
+
+/* all tests ok, we only have to save the file */
+/* (and probably continue with a pending operation) */
+static void
+file_save_as_cb(GtkWidget *w _U_, gpointer fs) {
+ GtkWidget *ft_combo_box;
+ GtkWidget *compressed_cb;
+ gchar *cf_name;
+ gchar *dirname;
+ gpointer ptr;
+ int file_type;
+
+ cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
+
+ compressed_cb = (GtkWidget *)g_object_get_data(G_OBJECT(fs), E_COMPRESSED_CB_KEY);
+ ft_combo_box = (GtkWidget *)g_object_get_data(G_OBJECT(fs), E_FILE_TYPE_COMBO_BOX_KEY);
+
+ if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(ft_combo_box), &ptr)) {
+ g_assert_not_reached(); /* Programming error: somehow nothing is active */
+ }
+ file_type = GPOINTER_TO_INT(ptr);
+
+ /* XXX - if the user requests to save to an already existing filename, */
+ /* ask in a dialog if that's intended */
+ /* currently, cf_save() will simply deny it */
+
+ /* Write out the packets (all, or only the ones from the current
+ range) to the file with the specified name. */
+ if (cf_save(&cfile, cf_name, &range, file_type,
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compressed_cb))) != CF_OK) {
+ /* The write failed; don't dismiss the open dialog box,
+ just leave it around so that the user can, after they
+ dismiss the alert box popped up for the error, try again. */
+ g_free(cf_name);
+ /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
+ * as this will prevent the user from closing the now existing error
+ * message, simply close the dialog (this is the best we can do here). */
+ if (file_save_as_w)
+ window_destroy(GTK_WIDGET (fs));
+ return;
+ }
+
+ /* The write succeeded; get rid of the file selection box. */
+ /* cf_save() might already closed our dialog! */
+ if (file_save_as_w)
+ window_destroy(GTK_WIDGET (fs));
+
+ /* Save the directory name for future file dialogs. */
+ dirname = get_dirname(cf_name); /* Overwrites cf_name */
+ set_last_open_dir(dirname);
+ g_free(cf_name);
+
+ /* we have finished saving, do we have pending things to do? */
+ switch(action_after_save_g) {
+ case(after_save_no_action):
+ break;
+ case(after_save_open_dialog):
+ file_open_cmd(action_after_save_data_g);
+ break;
+ case(after_save_open_recent_file):
+ menu_open_recent_file_cmd(action_after_save_data_g);
+ break;
+ case(after_save_open_dnd_file):
+ dnd_open_file_cmd(action_after_save_data_g);
+ break;
+ case(after_save_merge_dialog):
+ file_merge_cmd(action_after_save_data_g);
+ break;
+#ifdef HAVE_LIBPCAP
+ case(after_save_capture_dialog):
+ capture_start_confirmed();
+ break;
+#endif
+ case(after_save_close_file):
+ cf_close(&cfile);
+ break;
+ case(after_save_exit):
+ main_do_quit();
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ action_after_save_g = after_save_no_action;
+}
+
+
+static void file_save_as_exists_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
+{
+ gchar *cf_name;
+
+ cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(data));
+
+ switch(btn) {
+ case(ESD_BTN_OK):
+ /* save file */
+ ws_unlink(cf_name);
+ file_save_as_cb(NULL, data);
+ break;
+ case(ESD_BTN_CANCEL):
+ /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
+ * as this will prevent the user from closing the now existing error
+ * message, simply close the dialog (this is the best we can do here). */
+ if (file_save_as_w)
+ window_destroy(file_save_as_w);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ g_free(cf_name);
+}
+
+
+/* user pressed "Save" dialog "Ok" button */
+static void
+file_save_as_ok_cb(GtkWidget *w _U_, gpointer fs) {
+ gchar *cf_name;
+ gpointer dialog;
+
+ cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
+
+ /* Perhaps the user specified a directory instead of a file.
+ Check whether they did. */
+ if (test_for_directory(cf_name) == EISDIR) {
+ /* It's a directory - set the file selection box to display that
+ directory, and leave the selection box displayed. */
+ set_last_open_dir(cf_name);
+ g_free(cf_name);
+ file_selection_set_current_folder(fs, get_last_open_dir());
+ return;
+ }
+
+ /* Check whether the range is valid. */
+ if (!range_check_validity(&range)) {
+ /* The range isn't valid; don't dismiss the open dialog box,
+ just leave it around so that the user can, after they
+ dismiss the alert box popped up for the error, try again. */
+ g_free(cf_name);
+ /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
+ * as this will prevent the user from closing the now existing error
+ * message, simply close the dialog (this is the best we can do here). */
+ if (file_save_as_w)
+ window_destroy(GTK_WIDGET (fs));
+
+ return;
+ }
+
+ /*
+ * Check that the from file is not the same as to file
+ * We do it here so we catch all cases ...
+ * Unfortunately, the file requester gives us an absolute file
+ * name and the read file name may be relative (if supplied on
+ * the command line). From Joerg Mayer.
+ */
+ if (files_identical(cfile.filename, cf_name)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%sCapture file: \"%s\" identical to loaded file!%s\n\n"
+ "Please choose a different filename.",
+ simple_dialog_primary_start(), cf_name, simple_dialog_primary_end());
+ g_free(cf_name);
+ /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
+ * as this will prevent the user from closing the now existing error
+ * message, simply close the dialog (this is the best we can do here). */
+ if (file_save_as_w)
+ window_destroy(GTK_WIDGET (fs));
+
+ return;
+ }
+
+ /* don't show the dialog while saving (or asking) */
+ gtk_widget_hide(GTK_WIDGET (fs));
+
+ /* it the file doesn't exist, simply try to save it */
+ if (!file_exists(cf_name)) {
+ file_save_as_cb(NULL, fs);
+ g_free(cf_name);
+ return;
+ }
+
+ /* the file exists, ask the user to remove it first */
+ dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_OK_CANCEL,
+ "%sA file named \"%s\" already exists.%s\n\n"
+ "Do you want to replace it with the capture you are saving?",
+ simple_dialog_primary_start(), cf_name, simple_dialog_primary_end());
+ simple_dialog_set_cb(dialog, file_save_as_exists_answered_cb, fs);
+
+ g_free(cf_name);
+}
+
+void
+file_save_as_destroy(void)
+{
+ if (file_save_as_w)
+ window_destroy(file_save_as_w);
+}
+
+static void
+file_save_as_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+ /* Note that we no longer have a "Save Capture File As" dialog box. */
+ file_save_as_w = NULL;
+}
+
+/* Reload a file using the current read and display filters */
+void
+file_reload_cmd_cb(GtkWidget *w _U_, gpointer data _U_) {
+ cf_reload(&cfile);
+}
+
+/******************** Color Filters *********************************/
+/*
+ * Keep a static pointer to the current "Color Export" window, if
+ * any, so that if somebody tries to do "Export"
+ * while there's already a "Color Export" window up, we just pop
+ * up the existing one, rather than creating a new one.
+ */
+static GtkWidget *file_color_import_w;
+
+/* sets the file path to the global color filter file.
+ WARNING: called by both the import and the export dialog.
+*/
+static void
+color_global_cb(GtkWidget *widget _U_, gpointer data)
+{
+ GtkWidget *fs_widget = (GtkWidget *)data;
+ gchar *path;
+
+ /* decide what file to open (from dfilter code) */
+ path = get_datafile_path("colorfilters");
+
+ gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(fs_widget), path);
+
+ g_free(path);
+}
+
+/* Import color filters */
+void
+file_color_import_cmd_cb(GtkWidget *color_filters, gpointer filter_list _U_)
+{
+#if _WIN32
+ win32_import_color_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), color_filters);
+#else /* _WIN32 */
+ GtkWidget *main_vb, *cfglobal_but;
+
+ /* No Apply button, and "OK" just sets our text widget, it doesn't
+ activate it (i.e., it doesn't cause us to try to open the file). */
+
+ if (file_color_import_w != NULL) {
+ /* There's already an "Import Color Filters" dialog box; reactivate it. */
+ reactivate_window(file_color_import_w);
+ return;
+ }
+
+ file_color_import_w = file_selection_new("Wireshark: Import Color Filters",
+ FILE_SELECTION_OPEN);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ file_selection_set_extra_widget(file_color_import_w, main_vb);
+ gtk_widget_show(main_vb);
+
+
+ cfglobal_but = gtk_button_new_with_label("Global Color Filter File");
+ gtk_container_add(GTK_CONTAINER(main_vb), cfglobal_but);
+ g_signal_connect(cfglobal_but, "clicked",
+ G_CALLBACK(color_global_cb), file_color_import_w);
+ gtk_widget_show(cfglobal_but);
+
+ g_signal_connect(file_color_import_w, "destroy",
+ G_CALLBACK(file_color_import_destroy_cb), NULL);
+
+
+ if (gtk_dialog_run(GTK_DIALOG(file_color_import_w)) == GTK_RESPONSE_ACCEPT)
+ {
+ file_color_import_ok_cb(file_color_import_w, color_filters);
+ }
+ else window_destroy(file_color_import_w);
+#endif /* _WIN32 */
+}
+
+static void
+file_color_import_ok_cb(GtkWidget *w, gpointer color_filters) {
+ gchar *cf_name, *s;
+ GtkWidget *fs = gtk_widget_get_toplevel(w);
+
+ cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
+
+ /* Perhaps the user specified a directory instead of a file.
+ Check whether they did. */
+ if (test_for_directory(cf_name) == EISDIR) {
+ /* It's a directory - set the file selection box to display that
+ directory, don't try to open the directory as a color filter file. */
+ set_last_open_dir(cf_name);
+ g_free(cf_name);
+ file_selection_set_current_folder(fs, get_last_open_dir());
+ return;
+ }
+
+ /* Try to open the color filter file. */
+
+ if (!color_filters_import(cf_name, color_filters)) {
+ /* We couldn't open it; don't dismiss the open dialog box,
+ just leave it around so that the user can, after they
+ dismiss the alert box popped up for the open error,
+ try again. */
+ g_free(cf_name);
+ /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
+ * as this will prevent the user from closing the now existing error
+ * message, simply close the dialog (this is the best we can do here). */
+ window_destroy(GTK_WIDGET (fs));
+
+ return;
+ }
+
+ /* We've crossed the Rubicon; get rid of the file selection box. */
+ window_destroy(GTK_WIDGET (fs));
+
+ /* Save the name of the containing directory specified in the path name,
+ if any; we can write over cf_name, which is a good thing, given that
+ "get_dirname()" does write over its argument. */
+ s = get_dirname(cf_name);
+ set_last_open_dir(s);
+
+ g_free(cf_name);
+}
+
+static void
+file_color_import_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+ /* Note that we no longer have a "Open Capture File" dialog box. */
+ file_color_import_w = NULL;
+}
+
+static GtkWidget *file_color_export_w;
+/*
+ * Set the "Export only selected filters" toggle button as appropriate for
+ * the current output file type and count of selected filters.
+ *
+ * Called when the "Export" dialog box is created and when the selected
+ * count changes.
+ */
+static void
+color_set_export_selected_sensitive(GtkWidget * cfselect_cb)
+{
+ if (file_color_export_w == NULL) {
+ /* We don't currently have an "Export" dialog box up. */
+ return;
+ }
+
+ /* We can request that only the selected filters be saved only if
+ there *are* selected filters. */
+ if (color_selected_count() != 0)
+ gtk_widget_set_sensitive(cfselect_cb, TRUE);
+ else {
+ /* Force the "Export only selected filters" toggle to "false", turn
+ off the flag it controls. */
+ color_selected = FALSE;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfselect_cb), FALSE);
+ gtk_widget_set_sensitive(cfselect_cb, FALSE);
+ }
+}
+
+static void
+color_toggle_selected_cb(GtkWidget *widget, gpointer data _U_)
+{
+ color_selected = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
+}
+
+void
+file_color_export_cmd_cb(GtkWidget *w _U_, gpointer filter_list)
+{
+#if _WIN32
+ win32_export_color_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), filter_list);
+#else /* _WIN32 */
+ GtkWidget *main_vb, *cfglobal_but;
+ GtkWidget *cfselect_cb;
+
+ if (file_color_export_w != NULL) {
+ /* There's already an "Color Filter Export" dialog box; reactivate it. */
+ reactivate_window(file_color_export_w);
+ return;
+ }
+
+ color_selected = FALSE;
+
+ file_color_export_w = file_selection_new("Wireshark: Export Color Filters",
+ FILE_SELECTION_SAVE);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ file_selection_set_extra_widget(file_color_export_w, main_vb);
+ gtk_widget_show(main_vb);
+
+ cfselect_cb = gtk_check_button_new_with_label("Export only selected filters");
+ gtk_container_add(GTK_CONTAINER(main_vb), cfselect_cb);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cfselect_cb), FALSE);
+ g_signal_connect(cfselect_cb, "toggled",
+ G_CALLBACK(color_toggle_selected_cb), NULL);
+ gtk_widget_show(cfselect_cb);
+ color_set_export_selected_sensitive(cfselect_cb);
+
+ cfglobal_but = gtk_button_new_with_label("Global Color Filter File");
+ gtk_container_add(GTK_CONTAINER(main_vb), cfglobal_but);
+ g_signal_connect(cfglobal_but, "clicked",
+ G_CALLBACK(color_global_cb), file_color_export_w);
+ gtk_widget_show(cfglobal_but);
+
+ g_signal_connect(file_color_export_w, "destroy",
+ G_CALLBACK(file_color_export_destroy_cb), NULL);
+
+ if (gtk_dialog_run(GTK_DIALOG(file_color_export_w)) == GTK_RESPONSE_ACCEPT)
+ {
+ file_color_export_ok_cb(file_color_export_w, filter_list);
+ }
+ else window_destroy(file_color_export_w);
+#endif /* _WIN32 */
+}
+
+static void
+file_color_export_ok_cb(GtkWidget *w, gpointer filter_list) {
+ gchar *cf_name;
+ gchar *dirname;
+ GtkWidget *fs = gtk_widget_get_toplevel(w);
+
+ cf_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
+
+ /* Perhaps the user specified a directory instead of a file.
+ Check whether they did. */
+ if (test_for_directory(cf_name) == EISDIR) {
+ /* It's a directory - set the file selection box to display that
+ directory, and leave the selection box displayed. */
+ set_last_open_dir(cf_name);
+ g_free(cf_name);
+ file_selection_set_current_folder(fs, get_last_open_dir());
+ return;
+ }
+
+ /* Write out the filters (all, or only the ones that are currently
+ displayed or selected) to the file with the specified name. */
+
+ if (!color_filters_export(cf_name, filter_list, color_selected))
+ {
+ /* The write failed; don't dismiss the open dialog box,
+ just leave it around so that the user can, after they
+ dismiss the alert box popped up for the error, try again. */
+ g_free(cf_name);
+
+ /* XXX - as we cannot start a new event loop (using gtk_dialog_run()),
+ * as this will prevent the user from closing the now existing error
+ * message, simply close the dialog (this is the best we can do here). */
+ window_destroy(GTK_WIDGET (fs));
+
+ return;
+ }
+
+ /* The write succeeded; get rid of the file selection box. */
+ window_destroy(GTK_WIDGET (fs));
+
+ /* Save the directory name for future file dialogs. */
+ dirname = get_dirname(cf_name); /* Overwrites cf_name */
+ set_last_open_dir(dirname);
+ g_free(cf_name);
+}
+
+static void
+file_color_export_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+ file_color_export_w = NULL;
+}
diff --git a/ui/gtk/capture_file_dlg.h b/ui/gtk/capture_file_dlg.h
new file mode 100644
index 0000000000..0353e08b7c
--- /dev/null
+++ b/ui/gtk/capture_file_dlg.h
@@ -0,0 +1,121 @@
+/* capture_file_dlg.h
+ * Definitions for dialog boxes for handling files
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __CAPTURE_FILE_DLG_H__
+#define __CAPTURE_FILE_DLG_H__
+
+/** @file
+ * "Open" / "Close" / "Save" / "Save As" / etc dialog boxes.
+ * @ingroup dialog_group
+ */
+
+/** the action to take, after save has been done */
+typedef enum {
+ after_save_no_action, /**< no action to take */
+ after_save_close_file, /**< close the file */
+ after_save_open_dialog, /**< open the file open dialog */
+ after_save_open_recent_file, /**< open the specified recent file */
+ after_save_open_dnd_file, /**< open the specified file from drag and drop */
+ after_save_merge_dialog, /**< open the file merge dialog */
+ after_save_capture_dialog, /**< open the capture dialog */
+ after_save_exit /**< exit program */
+} action_after_save_e;
+
+/** Open the "Save As" dialog box.
+ *
+ * @param action_after_save the action to take, when save completed
+ * @param action_after_save_data data for action_after_save
+ * @param save_only_displayed Save only the displayed packets
+ */
+void file_save_as_cmd(action_after_save_e action_after_save, gpointer action_after_save_data, gboolean save_only_displayed);
+
+/** Destroy the save as dialog.
+ */
+void file_save_as_destroy(void);
+
+/** User requested the "Open" dialog box.
+ *
+ * @param widget parent widget
+ * @param data unused
+ */
+void file_open_cmd_cb(GtkWidget *widget, gpointer data);
+
+/** User requested the "Merge" dialog box.
+ *
+ * @param widget parent widget
+ * @param data unused
+ */
+void file_merge_cmd_cb(GtkWidget *widget, gpointer data);
+
+/** User requested the "Save" dialog box.
+ *
+ * @param widget parent widget
+ * @param data unused
+ */
+void file_save_cmd_cb(GtkWidget *widget, gpointer data);
+
+/** User requested the "Save As" dialog box.
+ *
+ * @param widget parent widget
+ * @param data unused
+ */
+void file_save_as_cmd_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Close".
+ *
+ * @param widget parent widget
+ * @param data unused
+ */
+void file_close_cmd_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Reload".
+ *
+ * @param widget parent widget
+ * @param data unused
+ */
+void file_reload_cmd_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Import". Currently only called from the color dialog.
+ *
+ * @param widget parent widget
+ * @param data unused
+ */
+void file_color_import_cmd_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Export". Currently only called from the color dialog.
+ *
+ * @param widget parent widget
+ * @param data unused
+ */
+void file_color_export_cmd_cb(GtkWidget *widget, gpointer data);
+
+/*
+ * Set the "Save only marked packets" toggle button as appropriate for
+ * the current output file type and count of marked packets.
+ * Called when the "Save As..." dialog box is created and when either
+ * the file type or the marked count changes.
+ */
+void file_save_update_dynamics(void);
+
+#endif /* capture_file_dlg.h */
diff --git a/ui/gtk/capture_globals.h b/ui/gtk/capture_globals.h
new file mode 100644
index 0000000000..9482557079
--- /dev/null
+++ b/ui/gtk/capture_globals.h
@@ -0,0 +1,30 @@
+/* capture_globals.h
+ * Capture-related globals.
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __CAPTURE_GLOBALS_H__
+#define __CAPTURE_GLOBALS_H__
+
+extern capture_options global_capture_opts;
+
+#endif /* __CAPTURE_GLOBALS_H__ */
diff --git a/ui/gtk/capture_if_details_dlg_win32.c b/ui/gtk/capture_if_details_dlg_win32.c
new file mode 100644
index 0000000000..c941d121a2
--- /dev/null
+++ b/ui/gtk/capture_if_details_dlg_win32.c
@@ -0,0 +1,2530 @@
+/* capture_if_details_dlg.c
+ * Routines for capture interface details window (only Win32!)
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+
+#if defined HAVE_LIBPCAP && defined _WIN32
+
+#include <time.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/value_string.h>
+#include <epan/addr_resolv.h>
+
+#include "../file.h"
+#include "../capture.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/main.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/capture_if_details_dlg_win32.h"
+
+#include <winsock2.h> /* Needed here to force a definition of WINVER */
+ /* for some (all ?) Microsoft compilers newer than vc6. */
+ /* (If windows.h were used instead, there might be */
+ /* issues re winsock.h included before winsock2.h ) */
+#include <windowsx.h>
+
+#ifdef HAVE_NTDDNDIS_H
+#include <Ntddndis.h>
+/* VC 6.0 with SDK has the same Ntddndis.h as MSVC2003 */
+# if (_MSC_VER <= 1200)
+# undef _MSC_VER
+# define _MSC_VER 1310
+# endif
+#endif
+
+#include "../capture_wpcap_packet.h"
+
+/* packet32.h requires sockaddr_storage
+ * whether sockaddr_storage is defined or not depends on the Platform SDK
+ * version installed. The only one not defining it is the SDK that comes
+ * with MSVC 6.0 (WINVER 0x0400).
+ *
+ * copied from RFC2553 (and slightly modified because of datatypes) ...
+ * XXX - defined more than once, move this to a header file */
+#ifndef WINVER
+#error WINVER not defined ...
+#endif
+#if (WINVER <= 0x0400) && defined(_MSC_VER)
+typedef unsigned short eth_sa_family_t;
+
+/*
+ * Desired design of maximum size and alignment
+ */
+#define ETH_SS_MAXSIZE 128 /* Implementation specific max size */
+#define ETH_SS_ALIGNSIZE (sizeof (gint64 /*int64_t*/))
+ /* Implementation specific desired alignment */
+/*
+ * Definitions used for sockaddr_storage structure paddings design.
+ */
+#define ETH_SS_PAD1SIZE (ETH_SS_ALIGNSIZE - sizeof (eth_sa_family_t))
+#define ETH_SS_PAD2SIZE (ETH_SS_MAXSIZE - (sizeof (eth_sa_family_t) + \
+ ETH_SS_PAD1SIZE + ETH_SS_ALIGNSIZE))
+
+struct sockaddr_storage {
+ eth_sa_family_t __ss_family; /* address family */
+ /* Following fields are implementation specific */
+ char __ss_pad1[ETH_SS_PAD1SIZE];
+ /* 6 byte pad, this is to make implementation */
+ /* specific pad up to alignment field that */
+ /* follows explicit in the data structure */
+ gint64 /*int64_t*/ __ss_align; /* field to force desired structure */
+ /* storage alignment */
+ char __ss_pad2[ETH_SS_PAD2SIZE];
+ /* 112 byte pad to achieve desired size, */
+ /* _SS_MAXSIZE value minus size of ss_family */
+ /* __ss_pad1, __ss_align fields is 112 */
+};
+/* ... copied from RFC2553 */
+#endif /* WINVER */
+
+#include <Packet32.h>
+
+#define DETAILS_STR_MAX 1024
+
+
+/* The informations and definitions used here are coming from various places on the web:
+ *
+ * ndiswrapper (various NDIS related definitions)
+ * http://cvs.sourceforge.net/viewcvs.py/ndiswrapper/ndiswrapper/driver/
+ *
+ * ReactOS (various NDIS related definitions)
+ * http://www.reactos.org/generated/doxygen/d2/d6d/ndis_8h-source.html
+ *
+ * IEEE802.11 "Detailed NDIS OID Log for a 802.11b Miniport"
+ * http://www.ndis.com/papers/ieee802_11_log.htm
+ *
+ * FreeBSD (various NDIS related definitions)
+ * http://lists.freebsd.org/pipermail/p4-projects/2004-January/003433.html
+ *
+ * MS WHDC "Network Drivers and Windows"
+ * http://www.microsoft.com/whdc/archive/netdrv_up.mspx
+ *
+ * IEEE "Get IEEE 802" (the various 802.11 docs)
+ * http://standards.ieee.org/getieee802/802.11.html
+ *
+ * MS MSDN "Network Devices: Windows Driver Kit"
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/NetXP_r/hh/NetXP_r/netref_4c297a96-2ba5-41ed-ab21-b7a9cfaa9b4d.xml.asp
+ *
+ * MS MSDN "Microsoft Windows CE .NET 4.2 Network Driver Reference"
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wceddk40/html/cxgrfNetworkDriverReference.asp
+ *
+ * MS MSDN (some explanations of a special MS 802.11 Information Element)
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/randz/protocol/securing_public_wi-fi_hotspots.asp
+ *
+ * "WLANINFO fuer Windows XP"
+ * http://www-pc.uni-regensburg.de/systemsw/TOOLS/wlaninfo.htm
+ */
+
+/********************************************************************************/
+/* definitions that would usually come from the windows DDK (device driver kit) */
+/* and are not part of the ntddndis.h file delivered with WinPcap */
+
+/* Required OIDs */
+#define OID_GEN_VLAN_ID 0x0001021C
+
+/* Optional OIDs */
+#define OID_GEN_MEDIA_CAPABILITIES 0x00010201
+#define OID_GEN_PHYSICAL_MEDIUM 0x00010202
+
+/* Optional OIDs */
+#define OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118
+#define OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119
+
+
+/* This will probably break if you're using VC++ 6 _and_ have a newer SDK. */
+#if (_MSC_VER <= 1200) || (_MSC_VER == 1310)
+#if (_MSC_VER <= 1200)
+/* Physical medium (OID_GEN_PHYSICAL_MEDIUM) */
+typedef enum ndis_phys_medium {
+ NdisPhysicalMediumUnspecified,
+ NdisPhysicalMediumWirelessLan,
+ NdisPhysicalMediumCableModem,
+ NdisPhysicalMediumPhoneLine,
+ NdisPhysicalMediumPowerLine,
+ NdisPhysicalMediumDSL,
+ NdisPhysicalMediumFibreChannel,
+ NdisPhysicalMedium1394,
+ NdisPhysicalMediumWirelessWan,
+ NdisPhysicalMediumMax
+};
+
+/* flag definitions for OID_GEN_MAC_OPTIONS */
+#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA 0x00000001
+#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED 0x00000002
+#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND 0x00000004
+#define NDIS_MAC_OPTION_NO_LOOPBACK 0x00000008
+#define NDIS_MAC_OPTION_FULL_DUPLEX 0x00000010
+#define NDIS_MAC_OPTION_EOTX_INDICATION 0x00000020
+#define NDIS_MAC_OPTION_8021P_PRIORITY 0x00000040
+#define NDIS_MAC_OPTION_8021Q_VLAN 0x00000080
+
+/* 802.11 OIDs */
+#define OID_802_11_BSSID 0x0D010101
+#define OID_802_11_SSID 0x0D010102
+#define OID_802_11_NETWORK_TYPES_SUPPORTED 0x0D010203
+#define OID_802_11_NETWORK_TYPE_IN_USE 0x0D010204
+#define OID_802_11_TX_POWER_LEVEL 0x0D010205
+#define OID_802_11_RSSI 0x0D010206
+#define OID_802_11_RSSI_TRIGGER 0x0D010207
+#define OID_802_11_INFRASTRUCTURE_MODE 0x0D010108
+#define OID_802_11_FRAGMENTATION_THRESHOLD 0x0D010209
+#define OID_802_11_RTS_THRESHOLD 0x0D01020A
+#define OID_802_11_NUMBER_OF_ANTENNAS 0x0D01020B
+#define OID_802_11_RX_ANTENNA_SELECTED 0x0D01020C
+#define OID_802_11_TX_ANTENNA_SELECTED 0x0D01020D
+#define OID_802_11_SUPPORTED_RATES 0x0D01020E
+#define OID_802_11_DESIRED_RATES 0x0D010210
+#define OID_802_11_CONFIGURATION 0x0D010211
+#define OID_802_11_STATISTICS 0x0D020212
+#define OID_802_11_ADD_WEP 0x0D010113
+#define OID_802_11_REMOVE_WEP 0x0D010114
+#define OID_802_11_DISASSOCIATE 0x0D010115
+#define OID_802_11_POWER_MODE 0x0D010216
+#define OID_802_11_BSSID_LIST 0x0D010217
+#define OID_802_11_AUTHENTICATION_MODE 0x0D010118
+#define OID_802_11_PRIVACY_FILTER 0x0D010119
+#define OID_802_11_BSSID_LIST_SCAN 0x0D01011A
+#define OID_802_11_WEP_STATUS 0x0D01011B
+
+#endif /*(_MSC_VER <= 1200)*/
+
+#define OID_802_11_ENCRYPTION_STATUS OID_802_11_WEP_STATUS
+#define OID_802_11_RELOAD_DEFAULTS 0x0D01011C
+#define OID_802_11_ADD_KEY 0x0D01011D
+#define OID_802_11_REMOVE_KEY 0x0D01011E
+#define OID_802_11_ASSOCIATION_INFORMATION 0x0D01011F
+#define OID_802_11_TEST 0x0D010120
+#define OID_802_11_CAPABILITY 0x0D010122
+#define OID_802_11_PMKID 0x0D010123
+
+#if (_MSC_VER <= 1200)
+
+/* PnP and power management OIDs */
+#define OID_PNP_CAPABILITIES 0xFD010100
+#define OID_PNP_SET_POWER 0xFD010101
+#define OID_PNP_QUERY_POWER 0xFD010102
+#define OID_PNP_ADD_WAKE_UP_PATTERN 0xFD010103
+#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xFD010104
+#define OID_PNP_WAKE_UP_PATTERN_LIST 0xFD010105
+#define OID_PNP_ENABLE_WAKE_UP 0xFD010106
+
+/* Current infrastructure mode (OID_802_11_INFRASTRUCTURE_MODE) */
+enum network_infrastructure {
+ Ndis802_11IBSS,
+ Ndis802_11Infrastructure,
+ Ndis802_11AutoUnknown,
+ Ndis802_11InfrastructureMax
+};
+
+/* Current authentication mode (OID_802_11_AUTHENTICATION_MODE) */
+enum authentication_mode {
+ Ndis802_11AuthModeOpen,
+ Ndis802_11AuthModeShared,
+ Ndis802_11AuthModeAutoSwitch,
+ Ndis802_11AuthModeWPA,
+ Ndis802_11AuthModeWPAPSK,
+ Ndis802_11AuthModeWPANone,
+ Ndis802_11AuthModeWPA2,
+ Ndis802_11AuthModeWPA2PSK,
+ Ndis802_11AuthModeMax
+};
+
+/* Current network type (OID_802_11_NETWORK_TYPES_SUPPORTED / OID_802_11_NETWORK_TYPE_IN_USE) */
+enum network_type {
+ Ndis802_11FH,
+ Ndis802_11DS,
+ Ndis802_11OFDM5,
+ Ndis802_11OFDM24,
+ /* MSDN site uses Ndis802_11Automode, which is not mentioned
+ * in DDK, so add one and assign it to
+ * Ndis802_11NetworkTypeMax */
+ Ndis802_11Automode,
+ Ndis802_11NetworkTypeMax = Ndis802_11Automode
+};
+
+/* Current encryption status (OID_802_11_ENCRYPTION_STATUS) */
+enum encryption_status {
+ Ndis802_11WEPEnabled,
+ Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled,
+ Ndis802_11WEPDisabled,
+ Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled,
+ Ndis802_11WEPKeyAbsent,
+ Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent,
+ Ndis802_11WEPNotSupported,
+ Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported,
+ Ndis802_11Encryption2Enabled,
+ Ndis802_11Encryption2KeyAbsent,
+ Ndis802_11Encryption3Enabled,
+ Ndis802_11Encryption3KeyAbsent
+};
+
+#endif /*(_MSC_VER <= 1200)*/
+
+typedef struct _NDIS_802_11_FIXED_IEs
+{
+ UCHAR Timestamp[8];
+ USHORT BeaconInterval;
+ USHORT Capabilities;
+} NDIS_802_11_FIXED_IEs, *PNDIS_802_11_FIXED_IEs;
+
+#endif /* (_MSC_VER <= 1200) || (_MSC_VER == 1310) */
+
+
+/* Currently associated SSID (OID_802_11_SSID) */
+#define NDIS_ESSID_MAX_SIZE 32
+struct ndis_essid {
+ ULONG length;
+ UCHAR essid[NDIS_ESSID_MAX_SIZE+1];
+};
+
+
+/* some definitions needed for the following structs */
+#define NDIS_MAX_RATES_EX 16
+typedef UCHAR mac_address[/* ETH_ALEN */ 6];
+typedef UCHAR ndis_rates[NDIS_MAX_RATES_EX];
+
+/* configuration, e.g. frequency (OID_802_11_CONFIGURATION / OID_802_11_BSSID_LIST) */
+struct /*packed*/ ndis_configuration {
+ ULONG length;
+ ULONG beacon_period;
+ ULONG atim_window;
+ ULONG ds_config;
+ struct ndis_configuration_fh {
+ ULONG length;
+ ULONG hop_pattern;
+ ULONG hop_set;
+ ULONG dwell_time;
+ } fh_config;
+};
+
+/* bssid list item (OID_802_11_BSSID_LIST) */
+struct ndis_ssid_item {
+ ULONG length;
+ mac_address mac;
+ UCHAR reserved[2];
+ struct ndis_essid ssid;
+ ULONG privacy;
+ LONG rssi;
+ UINT net_type;
+ struct ndis_configuration config;
+ UINT mode;
+ ndis_rates rates;
+ ULONG ie_length;
+ UCHAR ies[1];
+};
+
+
+/* bssid list (OID_802_11_BSSID_LIST) */
+struct ndis_bssid_list {
+ ULONG num_items;
+ struct ndis_ssid_item items[1];
+};
+
+
+/******************************************************************************/
+/* OID_TCP_TASK_OFFLOAD specific definitions that would usually come from the */
+/* windows DDK (device driver kit) and are not part of the ntddndis.h file */
+/* delivered with WinPcap */
+
+/* optional OID */
+#define OID_TCP_TASK_OFFLOAD 0xFC010201
+
+/* task id's */
+typedef enum _NDIS_TASK {
+ TcpIpChecksumNdisTask,
+ IpSecNdisTask,
+ TcpLargeSendNdisTask,
+ MaxNdisTask
+} NDIS_TASK, *PNDIS_TASK;
+
+/* TaskBuffer content on TcpIpChecksumNdisTask */
+typedef struct _NDIS_TASK_TCP_IP_CHECKSUM
+{
+ struct
+ {
+ ULONG IpOptionsSupported:1;
+ ULONG TcpOptionsSupported:1;
+ ULONG TcpChecksum:1;
+ ULONG UdpChecksum:1;
+ ULONG IpChecksum:1;
+ } V4Transmit;
+
+ struct
+ {
+ ULONG IpOptionsSupported:1;
+ ULONG TcpOptionsSupported:1;
+ ULONG TcpChecksum:1;
+ ULONG UdpChecksum:1;
+ ULONG IpChecksum:1;
+ } V4Receive;
+
+ struct
+ {
+ ULONG IpOptionsSupported:1;
+ ULONG TcpOptionsSupported:1;
+ ULONG TcpChecksum:1;
+ ULONG UdpChecksum:1;
+ } V6Transmit;
+
+ struct
+ {
+ ULONG IpOptionsSupported:1;
+ ULONG TcpOptionsSupported:1;
+ ULONG TcpChecksum:1;
+ ULONG UdpChecksum:1;
+ } V6Receive;
+} NDIS_TASK_TCP_IP_CHECKSUM, *PNDIS_TASK_TCP_IP_CHECKSUM;
+
+/* TaskBuffer content on TcpLargeSendNdisTask */
+typedef struct _NDIS_TASK_TCP_LARGE_SEND
+{
+ ULONG Version;
+ ULONG MaxOffLoadSize;
+ ULONG MinSegmentCount;
+ BOOLEAN TcpOptions;
+ BOOLEAN IpOptions;
+} NDIS_TASK_TCP_LARGE_SEND, *PNDIS_TASK_TCP_LARGE_SEND;
+
+/* Encapsulations */
+typedef enum _NDIS_ENCAPSULATION {
+ UNSPECIFIED_Encapsulation,
+ NULL_Encapsulation,
+ IEEE_802_3_Encapsulation,
+ IEEE_802_5_Encapsulation,
+ LLC_SNAP_ROUTED_Encapsulation,
+ LLC_SNAP_BRIDGED_Encapsulation
+} NDIS_ENCAPSULATION;
+
+/* Encapsulation format */
+typedef struct _NDIS_ENCAPSULATION_FORMAT {
+ NDIS_ENCAPSULATION Encapsulation;
+ struct {
+ ULONG FixedHeaderSize : 1;
+ ULONG Reserved : 31;
+ } Flags;
+ ULONG EncapsulationHeaderSize;
+} NDIS_ENCAPSULATION_FORMAT, *PNDIS_ENCAPSULATION_FORMAT;
+
+/* request struct */
+typedef struct _NDIS_TASK_OFFLOAD_HEADER
+{
+ ULONG Version;
+ ULONG Size;
+ ULONG Reserved;
+ UCHAR OffsetFirstTask;
+ NDIS_ENCAPSULATION_FORMAT EncapsulationFormat;
+} NDIS_TASK_OFFLOAD_HEADER, *PNDIS_TASK_OFFLOAD_HEADER;
+
+/* response struct */
+#define NDIS_TASK_OFFLOAD_VERSION 1
+typedef struct _NDIS_TASK_OFFLOAD
+{
+ ULONG Version;
+ ULONG Size;
+ NDIS_TASK Task;
+ ULONG OffsetNextTask;
+ ULONG TaskBufferLength;
+ UCHAR TaskBuffer[1];
+} NDIS_TASK_OFFLOAD, *PNDIS_TASK_OFFLOAD;
+
+
+/***********************************************************************/
+/* value_string's for info functions */
+
+
+/* NDIS driver medium (OID_GEN_MEDIA_SUPPORTED / OID_GEN_MEDIA_IN_USE) */
+static const value_string win32_802_3_medium_vals[] = {
+ { NdisMedium802_3, "802.3 (Ethernet)" }, /* might as well be WLAN, ... (see NDIS_PHYSICAL_MEDIUM) */
+ { NdisMedium802_5, "802.5 (Token Ring)" },
+ { NdisMediumFddi, "FDDI" },
+ { NdisMediumWan, "WAN" },
+ { NdisMediumLocalTalk, "Local Talk" },
+ { NdisMediumDix, "Dix" },
+ { NdisMediumArcnetRaw, "Arcnet Raw" },
+ { NdisMediumArcnet878_2,"Arcnet 878_2" },
+ { NdisMediumAtm, "ATM" },
+ { NdisMediumWirelessWan,"Wireless WAN" },
+ { NdisMediumIrda, "Irda" },
+ { 0, NULL }
+};
+
+/* NDIS physical driver medium (OID_GEN_PHYSICAL_MEDIUM) */
+static const value_string win32_802_3_physical_medium_vals[] = {
+ { NdisPhysicalMediumUnspecified, "Unspecified" },
+ { NdisPhysicalMediumWirelessLan, "Wireless LAN" },
+ { NdisPhysicalMediumCableModem, "Cable Modem (DOCSIS)" },
+ { NdisPhysicalMediumPhoneLine, "Phone Line" },
+ { NdisPhysicalMediumPowerLine, "Power Line" },
+ { NdisPhysicalMediumDSL, "DSL" },
+ { NdisPhysicalMediumFibreChannel, "Fibre Channel" },
+ { NdisPhysicalMedium1394, "IEEE 1394" },
+ { NdisPhysicalMediumWirelessWan, "Wireless WAN" },
+ { 0, NULL }
+};
+
+static const value_string win32_802_11_infra_mode_vals[] = {
+ { Ndis802_11IBSS, "Ad Hoc" },
+ { Ndis802_11Infrastructure, "Access Point" },
+ { Ndis802_11AutoUnknown, "Auto or unknown" },
+ { 0, NULL }
+};
+
+static const value_string win32_802_11_auth_mode_vals[] = {
+ { Ndis802_11AuthModeOpen, "Open System" },
+ { Ndis802_11AuthModeShared, "Shared Key" },
+ { Ndis802_11AuthModeAutoSwitch, "Auto Switch" },
+#if (_MSC_VER != 1310)
+ { Ndis802_11AuthModeWPA, "WPA" },
+ { Ndis802_11AuthModeWPAPSK, "WPA-PSK (pre shared key)" },
+ { Ndis802_11AuthModeWPANone, "WPA (ad hoc)" },
+#if (_MSC_VER != 1400) /* These are not defined in Ntddndis.h in MSVC2005/MSVC2005EE PSDK */
+ { Ndis802_11AuthModeWPA2, "WPA2" },
+ { Ndis802_11AuthModeWPA2PSK, "WPA2-PSK (pre shared key)" },
+#endif
+#endif
+ { 0, NULL }
+};
+
+static const value_string win32_802_11_network_type_vals[] = {
+ { Ndis802_11FH, "FH (frequency-hopping spread-spectrum)" },
+ { Ndis802_11DS, "DS (direct-sequence spread-spectrum)" },
+#if (_MSC_VER != 1310)
+ { Ndis802_11OFDM5, "5-GHz OFDM" },
+ { Ndis802_11OFDM24, "2.4-GHz OFDM" },
+#if (_MSC_VER != 1400) /* These are not defined in Ntddndis.h in MSVC2005/MSVC2005EE PSDK */
+ { Ndis802_11Automode, "Auto" },
+#endif
+#endif
+ { 0, NULL }
+};
+
+static const value_string win32_802_11_encryption_status_vals[] = {
+#if (_MSC_VER != 1310)
+ { Ndis802_11Encryption1Enabled, "WEP enabled, TKIP & AES disabled, transmit key available" },
+ { Ndis802_11EncryptionDisabled, "WEP & TKIP & AES disabled, transmit key available" },
+ { Ndis802_11Encryption1KeyAbsent, "WEP enabled, TKIP & AES disabled, transmit key unavailable" },
+ { Ndis802_11EncryptionNotSupported, "WEP & TKIP & AES not supported" },
+ { Ndis802_11Encryption2Enabled, "WEP & TKIP enabled, AES disabled, transmit key available" },
+ { Ndis802_11Encryption2KeyAbsent, "WEP & TKIP enabled, AES disabled, transmit key unavailable" },
+ { Ndis802_11Encryption3Enabled, "WEP & TKIP & AES enabled, transmit key available" },
+ { Ndis802_11Encryption3KeyAbsent, "WEP & TKIP & AES enabled, transmit key unavailable" },
+#endif
+ { 0, NULL }
+};
+
+/* frequency to channel mapping (OID_802_11_CONFIGURATION) */
+static const value_string win32_802_11_channel_freq_vals[] = {
+ { 2412000, "1 (2412 MHz)" },
+ { 2417000, "2 (2417 MHz)" },
+ { 2422000, "3 (2422 MHz)" },
+ { 2427000, "4 (2427 MHz)" },
+ { 2432000, "5 (2432 MHz)" },
+ { 2437000, "6 (2437 MHz)" },
+ { 2442000, "7 (2442 MHz)" },
+ { 2447000, "8 (2447 MHz)" },
+ { 2452000, "9 (2452 MHz)" },
+ { 2457000, "10 (2457 MHz)" },
+ { 2462000, "11 (2462 MHz)" },
+ { 2467000, "12 (2467 MHz)" },
+ { 2472000, "13 (2472 MHz)" },
+ { 2484000, "14 (2484 MHz)" },
+ { 0, NULL }
+};
+
+/* frequency to channel mapping (OID_802_11_CONFIGURATION) */
+static const value_string win32_802_11_channel_vals[] = {
+ { 2412000, "1" },
+ { 2417000, "2" },
+ { 2422000, "3" },
+ { 2427000, "4" },
+ { 2432000, "5" },
+ { 2437000, "6" },
+ { 2442000, "7" },
+ { 2447000, "8" },
+ { 2452000, "9" },
+ { 2457000, "10" },
+ { 2462000, "11" },
+ { 2467000, "12" },
+ { 2472000, "13" },
+ { 2484000, "14" },
+ { 0, NULL }
+};
+
+
+/* Information Element IDs (802.11 Spec: "7.3.2 Information elements") */
+#define IE_ID_SSID 0
+#define IE_ID_SUPPORTED_RATES 1
+#define IE_ID_DS_PARAMETER_SET 3
+#define IE_ID_TIM 5
+#define IE_ID_COUNTRY 7
+#define IE_ID_ERP_INFORMATION 42
+#define IE_ID_WPA2 48
+#define IE_ID_EXTENDED_SUPPORT_RATES 50
+#define IE_ID_VENDOR_SPECIFIC 221
+
+/* ElementID in NDIS_802_11_VARIABLE_IEs */
+static const value_string ie_id_vals[] = {
+ { IE_ID_SSID, "SSID, 802.11" },
+ { IE_ID_SUPPORTED_RATES, "Supported Rates, 802.11" },
+ { 2, "FH Parameter Set, 802.11" },
+ { IE_ID_DS_PARAMETER_SET, "DS Parameter Set, 802.11" },
+ { 4, "CF Parameter Set, 802.11" },
+ { IE_ID_TIM, "TIM, 802.11" },
+ { 6, "IBSS Parameter Set, 802.11" },
+ { IE_ID_COUNTRY, "Country, 802.11d" },
+ { 8, "Hopping Pattern Parameters, 802.11d" },
+ { 9, "Hopping Pattern Table, 802.11d" },
+ { 10, "Request, 802.11d" },
+ /* 11-15 reserved, 802.11d */
+ { 16, "Challenge text, 802.11" },
+ /* 17-31 reserved, 802.11h */
+ { 32, "Power Constraint, 802.11h" },
+ { 33, "Power Capability, 802.11h" },
+ { 34, "TPC Request, 802.11h" },
+ { 35, "TPC Report, 802.11h" },
+ { 36, "Supported Channels, 802.11h" },
+ { 37, "Channel Switch Announcement, 802.11h" },
+ { 38, "Measurement Request, 802.11h" },
+ { 39, "Measurement Report, 802.11h" },
+ { 40, "Quiet, 802.11h" },
+ { 41, "IBSS DFS, 802.11h" },
+ { IE_ID_ERP_INFORMATION, "ERP information, 802.11g" },
+ /* 43-47 reserved, 802.11i */
+ { IE_ID_WPA2, "WPA2/RSN (Robust Secure Network), 802.11i" },
+ /* 49 reserved, 802.11i */
+ { IE_ID_EXTENDED_SUPPORT_RATES, "Extended Supported Rates, 802.11g" },
+ /* 51-255 reserved, 802.11g */
+ { IE_ID_VENDOR_SPECIFIC, "WPA, (not 802.11!)" },
+ { 0, NULL }
+};
+
+
+static const value_string oid_vals[] = {
+ { OID_GEN_SUPPORTED_LIST, "OID_GEN_SUPPORTED_LIST" },
+ { OID_GEN_HARDWARE_STATUS, "OID_GEN_HARDWARE_STATUS (only internally interesting)" },
+ { OID_GEN_MEDIA_SUPPORTED, "OID_GEN_MEDIA_SUPPORTED" },
+ { OID_GEN_MEDIA_IN_USE, "OID_GEN_MEDIA_IN_USE" },
+ { OID_GEN_MAXIMUM_LOOKAHEAD, "OID_GEN_MAXIMUM_LOOKAHEAD (unused)" },
+ { OID_GEN_MAXIMUM_FRAME_SIZE, "OID_GEN_MAXIMUM_FRAME_SIZE (unused)" },
+ { OID_GEN_LINK_SPEED, "OID_GEN_LINK_SPEED" },
+ { OID_GEN_TRANSMIT_BUFFER_SPACE, "OID_GEN_TRANSMIT_BUFFER_SPACE" },
+ { OID_GEN_RECEIVE_BUFFER_SPACE, "OID_GEN_RECEIVE_BUFFER_SPACE" },
+ { OID_GEN_TRANSMIT_BLOCK_SIZE, "OID_GEN_TRANSMIT_BLOCK_SIZE" },
+ { OID_GEN_RECEIVE_BLOCK_SIZE, "OID_GEN_RECEIVE_BLOCK_SIZE" },
+ { OID_GEN_VENDOR_ID, "OID_GEN_VENDOR_ID" },
+ { OID_GEN_VENDOR_DESCRIPTION, "OID_GEN_VENDOR_DESCRIPTION" },
+ { OID_GEN_CURRENT_PACKET_FILTER, "OID_GEN_CURRENT_PACKET_FILTER (info seems to be constant)" },
+ { OID_GEN_CURRENT_LOOKAHEAD, "OID_GEN_CURRENT_LOOKAHEAD (only internally interesting)" },
+ { OID_GEN_DRIVER_VERSION, "OID_GEN_DRIVER_VERSION" },
+ { OID_GEN_MAXIMUM_TOTAL_SIZE, "OID_GEN_MAXIMUM_TOTAL_SIZE" },
+ { OID_GEN_PROTOCOL_OPTIONS, "OID_GEN_PROTOCOL_OPTIONS (info not interesting)" },
+ { OID_GEN_MAC_OPTIONS, "OID_GEN_MAC_OPTIONS" },
+ { OID_GEN_MEDIA_CONNECT_STATUS, "OID_GEN_MEDIA_CONNECT_STATUS" },
+ { OID_GEN_MAXIMUM_SEND_PACKETS, "OID_GEN_MAXIMUM_SEND_PACKETS (only internally interesting)" },
+ { OID_GEN_VENDOR_DRIVER_VERSION, "OID_GEN_VENDOR_DRIVER_VERSION" },
+ { OID_GEN_XMIT_OK, "OID_GEN_XMIT_OK" },
+ { OID_GEN_RCV_OK, "OID_GEN_RCV_OK" },
+ { OID_GEN_XMIT_ERROR, "OID_GEN_XMIT_ERROR" },
+ { OID_GEN_RCV_ERROR, "OID_GEN_RCV_ERROR" },
+ { OID_GEN_RCV_NO_BUFFER, "OID_GEN_RCV_NO_BUFFER" },
+ { OID_GEN_DIRECTED_BYTES_XMIT, "OID_GEN_DIRECTED_BYTES_XMIT" },
+ { OID_GEN_DIRECTED_FRAMES_XMIT, "OID_GEN_DIRECTED_FRAMES_XMIT" },
+ { OID_GEN_MULTICAST_BYTES_XMIT, "OID_GEN_MULTICAST_BYTES_XMIT" },
+ { OID_GEN_MULTICAST_FRAMES_XMIT, "OID_GEN_MULTICAST_FRAMES_XMIT" },
+ { OID_GEN_BROADCAST_BYTES_XMIT, "OID_GEN_BROADCAST_BYTES_XMIT" },
+ { OID_GEN_BROADCAST_FRAMES_XMIT, "OID_GEN_BROADCAST_FRAMES_XMIT" },
+ { OID_GEN_DIRECTED_BYTES_RCV, "OID_GEN_DIRECTED_BYTES_RCV" },
+ { OID_GEN_DIRECTED_FRAMES_RCV, "OID_GEN_DIRECTED_FRAMES_RCV" },
+ { OID_GEN_MULTICAST_BYTES_RCV, "OID_GEN_MULTICAST_BYTES_RCV" },
+ { OID_GEN_MULTICAST_FRAMES_RCV, "OID_GEN_MULTICAST_FRAMES_RCV" },
+ { OID_GEN_BROADCAST_BYTES_RCV, "OID_GEN_BROADCAST_BYTES_RCV" },
+ { OID_GEN_BROADCAST_FRAMES_RCV, "OID_GEN_BROADCAST_FRAMES_RCV" },
+ { OID_GEN_RCV_CRC_ERROR, "OID_GEN_RCV_CRC_ERROR" },
+ { OID_GEN_TRANSMIT_QUEUE_LENGTH, "OID_GEN_TRANSMIT_QUEUE_LENGTH" },
+ { OID_GEN_GET_TIME_CAPS, "OID_GEN_GET_TIME_CAPS (unsupp, unused)" },
+ { OID_GEN_GET_NETCARD_TIME, "OID_GEN_GET_NETCARD_TIME (unsupp, unused)" },
+
+ { OID_GEN_PHYSICAL_MEDIUM, "OID_GEN_PHYSICAL_MEDIUM" },
+ /*{ OID_GEN_MACHINE_NAME, "OID_GEN_MACHINE_NAME (unsupp, unused)" },*/
+ { OID_GEN_VLAN_ID, "OID_GEN_VLAN_ID" },
+ { OID_GEN_MEDIA_CAPABILITIES, "OID_GEN_MEDIA_CAPABILITIES (unsupp, unused)" },
+
+ { OID_GEN_NETWORK_LAYER_ADDRESSES, "OID_GEN_NETWORK_LAYER_ADDRESSES (write only)" },
+ { OID_GEN_TRANSPORT_HEADER_OFFSET, "OID_GEN_TRANSPORT_HEADER_OFFSET (write only)" },
+
+ { OID_802_3_PERMANENT_ADDRESS, "OID_802_3_PERMANENT_ADDRESS" },
+ { OID_802_3_CURRENT_ADDRESS, "OID_802_3_CURRENT_ADDRESS" },
+ { OID_802_3_MAXIMUM_LIST_SIZE, "OID_802_3_MAXIMUM_LIST_SIZE (unused)" },
+ { OID_802_3_MULTICAST_LIST, "OID_802_3_MULTICAST_LIST (unused)" }, /* XXX */
+ { OID_802_3_MAC_OPTIONS, "OID_802_3_MAC_OPTIONS (unsupp, unused)" },
+
+ { OID_802_3_RCV_ERROR_ALIGNMENT, "OID_802_3_RCV_ERROR_ALIGNMENT" },
+ { OID_802_3_XMIT_ONE_COLLISION, "OID_802_3_XMIT_ONE_COLLISION" },
+ { OID_802_3_XMIT_MORE_COLLISIONS, "OID_802_3_XMIT_MORE_COLLISIONS" },
+ { OID_802_3_XMIT_DEFERRED, "OID_802_3_XMIT_DEFERRED" },
+ { OID_802_3_XMIT_MAX_COLLISIONS, "OID_802_3_XMIT_MAX_COLLISIONS" },
+ { OID_802_3_RCV_OVERRUN, "OID_802_3_RCV_OVERRUN" },
+ { OID_802_3_XMIT_UNDERRUN, "OID_802_3_XMIT_UNDERRUN" },
+ { OID_802_3_XMIT_HEARTBEAT_FAILURE, "OID_802_3_XMIT_HEARTBEAT_FAILURE (unsupp, used)" },
+ { OID_802_3_XMIT_TIMES_CRS_LOST, "OID_802_3_XMIT_TIMES_CRS_LOST (unsupp, used)" },
+ { OID_802_3_XMIT_LATE_COLLISIONS, "OID_802_3_XMIT_LATE_COLLISIONS" },
+
+ { OID_802_11_BSSID, "OID_802_11_BSSID" },
+ { OID_802_11_SSID, "OID_802_11_SSID" },
+ { OID_802_11_NETWORK_TYPES_SUPPORTED, "OID_802_11_NETWORK_TYPES_SUPPORTED (info not interesting)" },
+ { OID_802_11_NETWORK_TYPE_IN_USE, "OID_802_11_NETWORK_TYPE_IN_USE" },
+ { OID_802_11_TX_POWER_LEVEL, "OID_802_11_TX_POWER_LEVEL (unsupp, used)" },
+ { OID_802_11_RSSI, "OID_802_11_RSSI" },
+ { OID_802_11_RSSI_TRIGGER, "OID_802_11_RSSI_TRIGGER (unsupp, unused)" },
+ { OID_802_11_INFRASTRUCTURE_MODE, "OID_802_11_INFRASTRUCTURE_MODE" },
+ { OID_802_11_FRAGMENTATION_THRESHOLD, "OID_802_11_FRAGMENTATION_THRESHOLD (unused)" },
+ { OID_802_11_RTS_THRESHOLD, "OID_802_11_RTS_THRESHOLD (unused)" },
+ { OID_802_11_NUMBER_OF_ANTENNAS, "OID_802_11_NUMBER_OF_ANTENNAS (unsupp, unused)" },
+ { OID_802_11_RX_ANTENNA_SELECTED, "OID_802_11_RX_ANTENNA_SELECTED (unsupp, unused)" },
+ { OID_802_11_TX_ANTENNA_SELECTED, "OID_802_11_TX_ANTENNA_SELECTED (unsupp, unused)" },
+ { OID_802_11_SUPPORTED_RATES, "OID_802_11_SUPPORTED_RATES" },
+ { OID_802_11_DESIRED_RATES, "OID_802_11_DESIRED_RATES (unsupp, used)" },
+ { OID_802_11_CONFIGURATION, "OID_802_11_CONFIGURATION" },
+ { OID_802_11_STATISTICS, "OID_802_11_STATISTICS (unsupp, unused)" },
+ { OID_802_11_ADD_WEP, "OID_802_11_ADD_WEP (write only)" },
+ { OID_802_11_REMOVE_WEP, "OID_802_11_REMOVE_WEP (write only)" },
+ { OID_802_11_DISASSOCIATE, "OID_802_11_DISASSOCIATE (write only)" },
+ { OID_802_11_POWER_MODE, "OID_802_11_POWER_MODE (info not interesting)" },
+ { OID_802_11_BSSID_LIST, "OID_802_11_BSSID_LIST" },
+ { OID_802_11_AUTHENTICATION_MODE, "OID_802_11_AUTHENTICATION_MODE" },
+ { OID_802_11_PRIVACY_FILTER, "OID_802_11_PRIVACY_FILTER (info not interesting)" },
+ { OID_802_11_BSSID_LIST_SCAN, "OID_802_11_BSSID_LIST_SCAN" },
+ { OID_802_11_WEP_STATUS, "OID_802_11_WEP_STATUS (info not interesting?)" },
+ { OID_802_11_ENCRYPTION_STATUS, "OID_802_11_ENCRYPTION_STATUS (unsupp, used)" },
+ { OID_802_11_RELOAD_DEFAULTS, "OID_802_11_RELOAD_DEFAULTS (write only)" },
+ { OID_802_11_ADD_KEY, "OID_802_11_ADD_KEY (write only)" },
+ { OID_802_11_REMOVE_KEY, "OID_802_11_REMOVE_KEY (write only)" },
+ { OID_802_11_ASSOCIATION_INFORMATION, "OID_802_11_ASSOCIATION_INFORMATION (unused)" }, /* XXX */
+ { OID_802_11_TEST, "OID_802_11_TEST (write only)" },
+#if (_MSC_VER != 1400) /* These are not defined in Ntddndis.h in MSVC2005/MSVC2005EE PSDK */
+ { OID_802_11_CAPABILITY, "OID_802_11_CAPABILITY (unsupp, unused)" },
+ { OID_802_11_PMKID, "OID_802_11_PMKID (unsupp, unused)" },
+#endif
+
+ /* Token-Ring list is utterly incomplete (contains only the values for MS Loopback Driver) */
+ { OID_802_5_PERMANENT_ADDRESS, "OID_802_5_PERMANENT_ADDRESS (unused)" },
+ { OID_802_5_CURRENT_ADDRESS, "OID_802_5_CURRENT_ADDRESS (unused)" },
+ { OID_802_5_CURRENT_FUNCTIONAL, "OID_802_5_CURRENT_FUNCTIONAL (unused)" },
+ { OID_802_5_CURRENT_GROUP, "OID_802_5_CURRENT_GROUP (unused)" },
+ { OID_802_5_LAST_OPEN_STATUS, "OID_802_5_LAST_OPEN_STATUS (unused)" },
+ { OID_802_5_CURRENT_RING_STATUS, "OID_802_5_CURRENT_RING_STATUS (unused)" },
+ { OID_802_5_CURRENT_RING_STATE, "OID_802_5_CURRENT_RING_STATE (unused)" },
+ { OID_802_5_LINE_ERRORS, "OID_802_5_LINE_ERRORS (unused)" },
+ { OID_802_5_LOST_FRAMES, "OID_802_5_LOST_FRAMES (unused)" },
+
+ /* FDDI list is utterly incomplete (contains only the values for MS Loopback Driver) */
+ { OID_FDDI_LONG_PERMANENT_ADDR, "OID_FDDI_LONG_PERMANENT_ADDR (unused)" },
+ { OID_FDDI_LONG_CURRENT_ADDR, "OID_FDDI_LONG_CURRENT_ADDR (unused)" },
+ { OID_FDDI_LONG_MULTICAST_LIST, "OID_FDDI_LONG_MULTICAST_LIST (unused)" },
+ { OID_FDDI_LONG_MAX_LIST_SIZE, "OID_FDDI_LONG_MAX_LIST_SIZE (unused)" },
+ { OID_FDDI_SHORT_PERMANENT_ADDR, "OID_FDDI_SHORT_PERMANENT_ADDR (unused)" },
+ { OID_FDDI_SHORT_CURRENT_ADDR, "OID_FDDI_SHORT_CURRENT_ADDR (unused)" },
+ { OID_FDDI_SHORT_MULTICAST_LIST, "OID_FDDI_SHORT_MULTICAST_LIST (unused)" },
+ { OID_FDDI_SHORT_MAX_LIST_SIZE, "OID_FDDI_SHORT_MAX_LIST_SIZE (unused)" },
+
+ /* LocalTalk list is utterly incomplete (contains only the values for MS Loopback Driver) */
+ { OID_LTALK_CURRENT_NODE_ID, "OID_LTALK_CURRENT_NODE_ID (unused)" },
+
+ /* Arcnet list is utterly incomplete (contains only the values for MS Loopback Driver) */
+ { OID_ARCNET_PERMANENT_ADDRESS, "OID_ARCNET_PERMANENT_ADDRESS (unused)" },
+ { OID_ARCNET_CURRENT_ADDRESS, "OID_ARCNET_CURRENT_ADDRESS (unused)" },
+
+ { OID_TCP_TASK_OFFLOAD, "OID_TCP_TASK_OFFLOAD" },
+
+ /* PnP and power management OIDs */
+ { OID_PNP_CAPABILITIES, "OID_PNP_CAPABILITIES (unused)" },
+ { OID_PNP_SET_POWER, "OID_PNP_SET_POWER (write only)" },
+ { OID_PNP_QUERY_POWER, "OID_PNP_QUERY_POWER (unused)" },
+ { OID_PNP_ADD_WAKE_UP_PATTERN, "OID_PNP_ADD_WAKE_UP_PATTERN (write only)" },
+ { OID_PNP_REMOVE_WAKE_UP_PATTERN, "OID_PNP_REMOVE_WAKE_UP_PATTERN (write only)" },
+ { OID_PNP_WAKE_UP_PATTERN_LIST, "OID_PNP_WAKE_UP_PATTERN_LIST (unused)" },
+ { OID_PNP_ENABLE_WAKE_UP, "OID_PNP_ENABLE_WAKE_UP (unused)" },
+
+ /* Unknown OID's (seen on an "Intel(R) PRO/Wireless 2200BG" 802.11 interface) */
+ { 0xFF100000, "Unknown 0xFF100000 (unused)" },
+ { 0xFF100002, "Unknown 0xFF100002 (unused)" },
+ { 0xFF100003, "Unknown 0xFF100003 (unused)" },
+ { 0xFF100004, "Unknown 0xFF100004 (unused)" },
+ { 0xFF100005, "Unknown 0xFF100005 (unused)" },
+ { 0xFF100006, "Unknown 0xFF100006 (unused)" },
+ { 0xFF100007, "Unknown 0xFF100007 (unused)" },
+ { 0xFF100009, "Unknown 0xFF100009 (unused)" },
+ { 0xFF10000b, "Unknown 0xFF10000b (unused)" },
+ { 0xFF10000c, "Unknown 0xFF10000c (unused)" },
+ { 0xFF10000e, "Unknown 0xFF10000e (unused)" },
+ { 0xFF10000f, "Unknown 0xFF10000f (unused)" },
+ /* continued by a lot more 0xFF... values */
+
+ { 0, NULL }
+};
+
+
+/***************************************************************************/
+/* debug functions, query or list supported NDIS OID's */
+
+
+static void
+supported_list(LPADAPTER adapter)
+{
+ unsigned char values[10000];
+ int length;
+
+
+ g_warning("supported_list_unhandled");
+ length = sizeof(values);
+ if (wpcap_packet_request(adapter, OID_GEN_SUPPORTED_LIST, FALSE /* !set */, values, &length)) {
+ guint32 *value = (guint32 *) values;
+
+ while (length>=4) {
+ printf("OID: 0x%08X %s\n", *value, val_to_str(*value, oid_vals, "unknown"));
+
+ value++;
+ length-=4;
+ }
+ }
+}
+
+
+static gboolean
+supported_query_oid(LPADAPTER adapter, guint32 oid)
+{
+ unsigned char values[10000];
+ int length;
+
+
+ length = sizeof(values);
+ if (wpcap_packet_request(adapter, OID_GEN_SUPPORTED_LIST, FALSE /* !set */, values, &length)) {
+ guint32 *value = (guint32 *) values;
+
+ while (length>=4) {
+ if(*value == oid) {
+ return TRUE;
+ }
+ value++;
+ length-=4;
+ }
+ }
+
+ return FALSE;
+}
+
+
+/******************************************************************************/
+/* info functions, get and display various NDIS driver values */
+
+
+#if 0
+ GtkWidget *meter;
+ GtkWidget *val_lb;
+
+static GtkWidget *
+add_meter_to_table(GtkWidget *list, guint *row, gchar *title,
+ int value, gchar *value_title,
+ int min, int max,
+ int yellow_level,
+ GList *scale)
+{
+ GtkWidget *label;
+ gchar *indent;
+ GtkWidget *main_hb;
+
+
+ /* the label */
+ indent = g_strdup_printf(" %s", title);
+ label = gtk_label_new(indent);
+ g_free(indent);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+ gtk_table_attach(GTK_TABLE(list), label, 0, 1, *row, *row+1, GTK_EXPAND|GTK_FILL, 0, 0,0);
+
+ /* the level meter */
+ main_hb = gtk_hbox_new(FALSE, 6);
+
+ meter = gtk_vumeter_new ();
+
+ gtk_vumeter_set_orientation(GTK_VUMETER(meter), GTK_VUMETER_LEFT_TO_RIGHT);
+
+ gtk_vumeter_set_min_max(GTK_VUMETER(meter), &min, &max);
+ gtk_vumeter_set_yellow_level (GTK_VUMETER(meter), yellow_level);
+ gtk_vumeter_set_thickness (GTK_VUMETER(meter), 10);
+ gtk_vumeter_set_thickness_reduction (GTK_VUMETER(meter), 2);
+ gtk_vumeter_set_scale_hole_size (GTK_VUMETER(meter), 2);
+ gtk_vumeter_set_colors_inverted (GTK_VUMETER(meter), TRUE);
+
+ if(scale) {
+ gtk_vumeter_set_scale_items(GTK_VUMETER(meter), scale);
+ }
+
+ gtk_vumeter_set_level(GTK_VUMETER(meter), value);
+
+ gtk_box_pack_start (GTK_BOX(main_hb),
+ meter,
+ TRUE /*expand*/,
+ TRUE /*fill*/,
+ 0 /* padding */);
+
+ val_lb = gtk_label_new(value_title);
+ gtk_widget_set_size_request(val_lb, 50, -1);
+ gtk_misc_set_alignment(GTK_MISC(val_lb), 1.0, 0.5);
+
+ gtk_box_pack_start (GTK_BOX(main_hb),
+ val_lb,
+ FALSE /*expand*/,
+ FALSE /*fill*/,
+ 0 /* padding */);
+
+ gtk_table_attach(GTK_TABLE(list), main_hb, 1, 2, *row, *row+1, GTK_EXPAND|GTK_FILL, 0, 0,0);
+
+ *row = *row + 1;
+
+ return meter;
+}
+
+#endif
+
+static void
+add_row_to_table(GtkWidget *list, guint *row, gchar *title, const gchar *value, gboolean sensitive)
+{
+ GtkWidget *label;
+ gchar *indent;
+
+ if(strlen(value) != 0) {
+ indent = g_strdup_printf(" %s", title);
+ } else {
+ indent = g_strdup(title);
+ }
+ label = gtk_label_new(indent);
+ g_free(indent);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+ gtk_widget_set_sensitive(label, sensitive);
+ gtk_table_attach(GTK_TABLE(list), label, 0, 1, *row, *row+1, GTK_EXPAND | GTK_FILL, 0, 0,0);
+
+ label = gtk_label_new(value);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+ gtk_widget_set_sensitive(label, sensitive);
+ gtk_table_attach(GTK_TABLE(list), label, 1, 2, *row, *row+1, GTK_EXPAND | GTK_FILL, 0, 0,0);
+
+ *row = *row + 1;
+}
+
+
+static void
+add_string_to_table_sensitive(GtkWidget *list, guint *row, gchar *title, gchar *value, gboolean sensitive)
+{
+ add_row_to_table(list, row, title, value, sensitive);
+}
+
+
+static void
+add_string_to_table(GtkWidget *list, guint *row, gchar *title, const gchar *value)
+{
+ add_row_to_table(list, row, title, value, TRUE);
+}
+
+
+static void
+ssid_details(GtkWidget *table, guint *row, struct ndis_essid *ssid_in) {
+ struct ndis_essid ssid[2]; /* prevent an off by one error */
+
+
+ ssid[0] = *ssid_in;
+ g_assert(ssid->length <= NDIS_ESSID_MAX_SIZE);
+
+ if(ssid->length != 0) {
+ ssid->essid[ssid->length] = '\0';
+ add_string_to_table(table, row, "SSID (Service Set IDentifier)", ssid->essid);
+ } else {
+ add_string_to_table(table, row, "SSID (Service Set IDentifier)", "(currently not associated with an SSID)");
+ }
+}
+
+
+static GString *
+rates_details(unsigned char *values, int length) {
+ int i;
+ GString *Rates;
+ float float_value;
+ int int_value;
+
+
+ Rates = g_string_new("");
+
+ if(length != 0) {
+ i = 0;
+ while(length--) {
+ if(values[i]) {
+ if(i != 0) {
+ g_string_append(Rates, "/");
+ }
+
+ float_value = ((float) (values[i] & 0x7F)) / 2;
+
+ /* reduce the screen estate by showing fractions only where required */
+ int_value = (int)float_value;
+ if(float_value == (float)int_value) {
+ g_string_append_printf(Rates, "%.0f", float_value);
+ } else {
+ g_string_append_printf(Rates, "%.1f", float_value);
+ }
+ }
+ i++;
+ }
+ Rates = g_string_append(Rates, " MBits/s");
+ } else {
+ Rates = g_string_append(Rates, "-");
+ }
+
+ return Rates;
+}
+
+
+#if 0
+static GList *
+rates_vu_list(unsigned char *values, int length, int *max)
+{
+ int i;
+ GList *Rates = NULL;
+ GtkVUMeterScaleItem * item;
+
+
+ *max = 0;
+
+ if(length == 0) {
+ return NULL;
+ }
+
+ /* add a zero scale point */
+ item = g_malloc(sizeof(GtkVUMeterScaleItem));
+ item->level = 0;
+ item->large = TRUE;
+ item->label = "0";
+ Rates = g_list_append(Rates, item);
+
+ /* get the maximum rate */
+ for(i=0; i<length; i++) {
+ gint level;
+
+ if(values[i]) {
+ level = (values[i] & 0x7F) / 2;
+
+ if(level > *max) {
+ *max = level;
+ }
+ }
+ }
+
+ /* debug: fake the 108MBit entry (I don't own one :-) */
+ *max = 108;
+
+ item = g_malloc(sizeof(GtkVUMeterScaleItem));
+ item->level = 108;
+ item->large = TRUE;
+ item->label = "108";
+ Rates = g_list_append(Rates, item);
+
+ for(i=0; i<length; i++) {
+ if(values[i]) {
+ /* reduce the screen estate by showing fractions only where required */
+ item = g_malloc(sizeof(GtkVUMeterScaleItem));
+
+ item->level = (values[i] & 0x7F) / 2;
+
+ /* common data rates: */
+ /* 802.11 (15.1) : mandatory: 2, 1 */
+ /* 802.11a (17.1) : mandatory: 24, 12, 6 optional: 54, 48, 36, 18, 9 */
+ /* 802.11b (18.1) : mandatory: 11, 5.5 (+ 2, 1) */
+ /* 802.11g (19.1.1): mandatory: 24, 12, 11, 6, 5.5, 2, 1 optional: 54, 48, 36, 33, 22, 18, 9 */
+ /* 802.11n: ? */
+ /* proprietary: 108 */
+
+ switch(item->level) {
+ case 2:
+ if(*max >= 108) {
+ item->large = FALSE;
+ item->label = NULL;
+ } else {
+ item->large = TRUE;
+ item->label = "2";
+ }
+ break;
+ case 5:
+ item->large = TRUE;
+ item->label = "5.5";
+ break;
+ case 11:
+ item->large = TRUE;
+ item->label = "11";
+ break;
+ case 18:
+ item->large = TRUE;
+ item->label = "18";
+ break;
+ case 24:
+ item->large = TRUE;
+ item->label = "24";
+ break;
+ case 36:
+ item->large = TRUE;
+ item->label = "36";
+ break;
+ case 48:
+ item->large = TRUE;
+ item->label = "48";
+ break;
+ case 54:
+ item->large = TRUE;
+ item->label = "54";
+ break;
+ case 72: /* XXX */
+ item->large = TRUE;
+ item->label = "72";
+ break;
+ case 96: /* XXX */
+ item->large = TRUE;
+ item->label = "96";
+ break;
+ case 108:
+ item->large = TRUE;
+ item->label = "108";
+ break;
+ default:
+ item->large = FALSE;
+ item->label = NULL;
+ }
+
+ Rates = g_list_append(Rates, item);
+ }
+ }
+
+ return Rates;
+}
+#endif
+
+
+/* debugging only */
+static void
+hex(unsigned char *p, int len) {
+ int i = 0;
+ while(len) {
+ g_warning("%u: 0x%x (%u) '%c'", i, *p, *p,
+ isprint(*p) ? *p : '.');
+
+ i++;
+ p++;
+ len--;
+ }
+}
+
+
+static void
+capture_if_details_802_11_bssid_list(GtkWidget *main_vb, struct ndis_bssid_list *bssid_list)
+{
+ struct ndis_ssid_item *bssid_item;
+ unsigned char mac[6];
+ const gchar *manuf_name;
+ GString *Rates;
+
+
+ if(bssid_list->num_items != 0) {
+ char *titles[] = { "SSID", "MAC", "Vendor", "Privacy", "RSSI" , "Network Type" , "Infra. Mode" , "Ch." , "Rates", "Country" };
+ GtkWidget *list;
+ gboolean privacy_required;
+ gboolean privacy_wpa;
+ gboolean privacy_wpa2;
+
+ gchar ssid_buff[DETAILS_STR_MAX];
+ gchar mac_buff[DETAILS_STR_MAX];
+ gchar vendor_buff[DETAILS_STR_MAX];
+ gchar privacy_buff[DETAILS_STR_MAX];
+ gchar rssi_buff[DETAILS_STR_MAX];
+ gchar nettype_buff[DETAILS_STR_MAX];
+ gchar infra_buff[DETAILS_STR_MAX];
+ gchar freq_buff[DETAILS_STR_MAX];
+ gchar country_buff[DETAILS_STR_MAX] = "";
+
+ list = simple_list_new(10, titles);
+ gtk_box_pack_start(GTK_BOX(main_vb), list, TRUE /*expand*/, TRUE /*fill*/, 0 /* padding */);
+
+ bssid_item = &bssid_list->items[0];
+
+ while(bssid_list->num_items--) {
+ privacy_required = FALSE;
+ privacy_wpa = FALSE;
+ privacy_wpa2 = FALSE;
+
+ /* SSID */
+ if(bssid_item->ssid.length > DETAILS_STR_MAX-1) {
+ bssid_item->ssid.length = DETAILS_STR_MAX-1;
+ }
+ memcpy(ssid_buff, bssid_item->ssid.essid, bssid_item->ssid.length);
+ ssid_buff[bssid_item->ssid.length] = '\0';
+
+ /* MAC */
+ memcpy(mac, &bssid_item->mac, sizeof(mac));
+ g_snprintf(mac_buff, DETAILS_STR_MAX, "%02X:%02X:%02X:%02X:%02X:%02X",
+ mac[0], mac[1], mac[2],
+ mac[3], mac[4], mac[5]);
+
+ /* Vendor */
+ manuf_name = get_manuf_name_if_known(mac);
+ if(manuf_name != NULL) {
+ g_strlcpy(vendor_buff, manuf_name, DETAILS_STR_MAX);
+ } else {
+ vendor_buff[0] = '\0';
+ }
+
+ /* Supported Rates */
+ Rates = rates_details(bssid_item->rates, NDIS_MAX_RATES_EX);
+
+ /* RSSI */
+ g_snprintf(rssi_buff, DETAILS_STR_MAX, "%d dBm", bssid_item->rssi);
+
+ /* Privacy */
+ /* (flag is set, if WEP (or higher) privacy is required) */
+ if(bssid_item->privacy) {
+ privacy_required = TRUE;
+ }
+
+ /* Network Type */
+ g_snprintf(nettype_buff, sizeof(nettype_buff), "%s",
+ val_to_str(bssid_item->net_type, win32_802_11_network_type_vals, "(0x%x)"));
+
+ /* Infrastructure Mode */
+ g_snprintf(infra_buff, sizeof(infra_buff), "%s",
+ val_to_str(bssid_item->mode, win32_802_11_infra_mode_vals, "(0x%x)"));
+
+ /* Channel */
+ g_snprintf(freq_buff, sizeof(freq_buff), "%s",
+ val_to_str(bssid_item->config.ds_config, win32_802_11_channel_vals, "(%u kHz)"));
+
+ /* XXX - IE Length is currently not really supported here */
+ {
+ int len = bssid_item->ie_length;
+ unsigned char *iep = bssid_item->ies;
+ unsigned char id;
+ unsigned el_len;
+ NDIS_802_11_FIXED_IEs *fixed_ies;
+/*#define DEBUG_IE*/
+#ifdef DEBUG_IE
+ const gchar *manuf_name;
+ gchar string_buff[DETAILS_STR_MAX];
+#endif
+
+
+ fixed_ies = (NDIS_802_11_FIXED_IEs *) iep;
+
+#if 0
+ UCHAR Timestamp[8];
+ USHORT BeaconInterval;
+ USHORT Capabilities;
+} NDIS_802_11_FIXED_IEs, *PNDIS_802_11_FIXED_IEs;
+#endif
+
+ iep += sizeof(NDIS_802_11_FIXED_IEs);
+ len = bssid_item->ie_length - sizeof(NDIS_802_11_FIXED_IEs);
+
+ while(len >= 2) {
+ id = *iep;
+ iep++;
+ el_len = *iep;
+ iep++;
+ len-=2;
+
+#ifdef DEBUG_IE
+ g_warning("ID: %s (%u) Len: %u",
+ val_to_str(id, ie_id_vals, "0x%x"), id, el_len);
+#endif
+
+ switch(id) {
+ case(IE_ID_COUNTRY):
+ if(el_len >= 6)
+ g_snprintf(country_buff, sizeof(country_buff), "%c%c: Ch: %u-%u Max: %ddBm",
+ iep[0], iep[1], iep[3], iep[4], iep[5]);
+ break;
+ case(IE_ID_WPA2):
+ privacy_wpa2 = TRUE;
+ break;
+ case(IE_ID_VENDOR_SPECIFIC): /* WPA */
+ privacy_wpa = TRUE;
+
+#ifdef DEBUG_IE
+ /* include information from epan/packet-ieee80211.c dissect_vendor_ie_wpawme() */
+ manuf_name = get_manuf_name_if_known(iep);
+ if(manuf_name != NULL) {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%02X:%02X:%02X (%s) Type: %02X",
+ *(iep), *(iep+1), *(iep+2), manuf_name, *(iep+3));
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%02X:%02X:%02X Type: %02X",
+ *(iep), *(iep+1), *(iep+2), *(iep+3));
+ }
+
+ g_warning("%s", string_buff);
+ iep += 4;
+ el_len-= 4;
+ len -= 4;
+
+ g_warning("WPA IE: %u", id);
+ hex(iep, el_len);
+#endif
+ break;
+
+ case(IE_ID_SSID):
+ case(IE_ID_SUPPORTED_RATES):
+ case(IE_ID_DS_PARAMETER_SET):
+ case(IE_ID_TIM):
+ case(IE_ID_ERP_INFORMATION):
+ case(IE_ID_EXTENDED_SUPPORT_RATES):
+ /* we already have that data, do nothing */
+ break;
+ default:
+ /* unexpected IE_ID, print out hexdump */
+ g_warning("Unknown IE ID: %u Len: %u", id, el_len);
+ hex(iep, el_len);
+ }
+
+ iep += el_len;
+ len -= el_len;
+ }
+ }
+
+ if(privacy_required) {
+ if(privacy_wpa2) {
+ /* XXX - how to detect data encryption (TKIP and AES)? */
+ /* XXX - how to detect authentication (PSK or not)? */
+ g_snprintf(privacy_buff, DETAILS_STR_MAX, "WPA2");
+ } else {
+ if(privacy_wpa) {
+ /* XXX - how to detect data encryption (TKIP and AES)? */
+ /* XXX - how to detect authentication (PSK or not)? */
+ g_snprintf(privacy_buff, DETAILS_STR_MAX, "WPA");
+ } else {
+ /* XXX - how to detect authentication (Open System and Shared Key)? */
+ g_snprintf(privacy_buff, DETAILS_STR_MAX, "WEP");
+ }
+ }
+ } else {
+ g_snprintf(privacy_buff, DETAILS_STR_MAX, "None");
+ }
+
+ simple_list_append(list,
+ 0, ssid_buff,
+ 1, mac_buff,
+ 2, vendor_buff,
+ 3, privacy_buff,
+ 4, rssi_buff,
+ 5, nettype_buff,
+ 6, infra_buff,
+ 7, freq_buff,
+ 8, Rates->str,
+ 9, country_buff,
+ -1);
+
+ g_string_free(Rates, TRUE /* free_segment */);
+
+ /* the bssid_list isn't an array, but a sequence of variable length items */
+ bssid_item = (struct ndis_ssid_item *) (((char *) bssid_item) + bssid_item->length);
+ }
+ }
+}
+
+static int
+capture_if_details_802_11(GtkWidget *table, GtkWidget *main_vb, guint *row, LPADAPTER adapter) {
+ ULONG ulong_value;
+ LONG rssi = -100;
+ unsigned int uint_value;
+ unsigned char values[100];
+ struct ndis_essid ssid;
+ int length;
+ struct ndis_bssid_list *bssid_list;
+ struct ndis_configuration *configuration;
+ gchar string_buff[DETAILS_STR_MAX];
+ GString *Rates;
+ int entries = 0;
+ const gchar *manuf_name;
+
+
+ add_string_to_table(table, row, "Current network", "");
+
+ /* SSID */
+ length = sizeof(struct ndis_essid);
+ memset(&ssid, 0, length);
+ if (wpcap_packet_request(adapter, OID_802_11_SSID, FALSE /* !set */, (char *) &ssid, &length)) {
+ ssid_details(table, row, &ssid);
+ entries++;
+ } else {
+ add_string_to_table(table, row, "SSID (Service Set IDentifier)", "-");
+ }
+
+ /* BSSID */
+ length = sizeof(values);
+ memset(values, 0, 6);
+ if (wpcap_packet_request(adapter, OID_802_11_BSSID, FALSE /* !set */, values, &length)) {
+ manuf_name = get_manuf_name_if_known(values);
+ if(manuf_name != NULL) {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%02X:%02X:%02X:%02X:%02X:%02X (%s)",
+ values[0], values[1], values[2],
+ values[3], values[4], values[5],
+ manuf_name);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%02X:%02X:%02X:%02X:%02X:%02X",
+ values[0], values[1], values[2],
+ values[3], values[4], values[5]);
+ }
+ entries++;
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "BSSID (Basic Service Set IDentifier)", string_buff);
+
+ /* Network type in use */
+ if (wpcap_packet_request_uint(adapter, OID_802_11_NETWORK_TYPE_IN_USE, &uint_value)) {
+ add_string_to_table(table, row, "Network type used",
+ val_to_str(uint_value, win32_802_11_network_type_vals, "(0x%x)"));
+ entries++;
+ } else {
+ add_string_to_table(table, row, "Network type used", "-");
+ }
+
+ /* Infrastructure mode */
+ if (wpcap_packet_request_ulong(adapter, OID_802_11_INFRASTRUCTURE_MODE, &uint_value)) {
+ add_string_to_table(table, row, "Infrastructure mode",
+ val_to_str(uint_value, win32_802_11_infra_mode_vals, "(0x%x)"));
+ entries++;
+ } else {
+ add_string_to_table(table, row, "Infrastructure mode", "-");
+ }
+
+ /* Authentication mode */
+ if (wpcap_packet_request_ulong(adapter, OID_802_11_AUTHENTICATION_MODE, &uint_value)) {
+ add_string_to_table(table, row, "Authentication mode",
+ val_to_str(uint_value, win32_802_11_auth_mode_vals, "(0x%x)"));
+ entries++;
+ } else {
+ add_string_to_table(table, row, "Authentication mode", "-");
+ }
+
+ /* Encryption (WEP) status */
+ if (wpcap_packet_request_ulong(adapter, OID_802_11_ENCRYPTION_STATUS, &uint_value)) {
+ add_string_to_table(table, row, "Encryption status",
+ val_to_str(uint_value, win32_802_11_encryption_status_vals, "(0x%x)"));
+ entries++;
+ } else {
+ add_string_to_table(table, row, "Encryption status", "-");
+ }
+
+ /* TX power */
+ if (wpcap_packet_request_ulong(adapter, OID_802_11_TX_POWER_LEVEL, &ulong_value)) {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%ld mW", ulong_value);
+ add_string_to_table(table, row, "TX power", string_buff);
+ entries++;
+ } else {
+ add_string_to_table(table, row, "TX power", "-");
+ }
+
+ /* RSSI */
+ if (wpcap_packet_request_ulong(adapter, OID_802_11_RSSI, &rssi)) {
+#if 0
+ int i;
+ GList * scale_items = NULL;
+ GList * current;
+ GtkVUMeterScaleItem *item;
+
+
+ for (i = 0; i <= 100; i++) {
+ item = g_malloc(sizeof(GtkVUMeterScaleItem));
+
+ item->level = i;
+ item->large = !(i%5);
+ item->label = NULL;
+
+ switch(item->level) {
+ case 0:
+ item->label = "-100 ";
+ break;
+ case 20:
+ item->label = "-80 ";
+ break;
+ case 40:
+ item->label = "-60 ";
+ break;
+ case 60:
+ item->label = "-40 ";
+ break;
+ case 80:
+ item->label = "-20 ";
+ break;
+ case 100:
+ item->label = "0";
+ break;
+ default:
+ item->label = NULL;
+ }
+
+ scale_items = g_list_append(scale_items, item);
+ }
+
+ if(rssi < -100) {
+ rssi = -100;
+ }
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%ld dBm", rssi);
+
+ add_meter_to_table(table, row, "RSSI (Received Signal Strength Indication)",
+ rssi+100 , string_buff, -100+100, 0+100, -80+100, scale_items);
+ current = scale_items;
+ while (current != NULL) {
+ g_free(current->data);
+
+ current = g_list_next(current);
+ }
+ g_list_free(scale_items);
+ entries++;
+#endif
+ } else {
+ add_string_to_table(table, row, "RSSI (Received Signal Strength Indication)", "-");
+ }
+
+ /* Supported Rates */
+ length = sizeof(values);
+ if (!wpcap_packet_request(adapter, OID_802_11_SUPPORTED_RATES, FALSE /* !set */, values, &length)) {
+ length = 0;
+ } else {
+ entries++;
+ }
+
+ /* if we can get the link speed, show Supported Rates in level meter format */
+ if (length != 0 && wpcap_packet_request_uint(adapter, OID_GEN_LINK_SPEED, &uint_value)) {
+#if 0
+ int max;
+ int yellow;
+ GList *rates_list;
+
+ GList * current;
+
+
+ rates_list = rates_vu_list(values, length, &max);
+
+ /* if we don't have a signal, we might not have a valid link speed */
+ if(rssi == -100) {
+ uint_value = 0;
+ }
+
+ uint_value /= 10 * 1000;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%u MBits/s", uint_value);
+
+ if(max >= 54) {
+ yellow = 2;
+ } else {
+ yellow = 1;
+ }
+
+ add_meter_to_table(table, row, "Link Speed",
+ uint_value, string_buff, 0, max, yellow, rates_list);
+ current = rates_list;
+ while (current != NULL) {
+ g_free(current->data);
+
+ current = g_list_next(current);
+ }
+ g_list_free(rates_list);
+#endif
+ }
+
+ /* Supported Rates in String format */
+ Rates = rates_details(values, length);
+ add_string_to_table(table, row, "Supported Rates", Rates->str);
+ g_string_free(Rates, TRUE /* free_segment */);
+
+ /* Desired Rates */
+ length = sizeof(values);
+ if (!wpcap_packet_request(adapter, OID_802_11_DESIRED_RATES, FALSE /* !set */, values, &length)) {
+ length = 0;
+ } else {
+ entries++;
+ }
+
+ Rates = rates_details(values, length);
+ add_string_to_table(table, row, "Desired Rates", Rates->str);
+ g_string_free(Rates, TRUE /* free_segment */);
+
+ /* Configuration (e.g. frequency) */
+ length = sizeof(values);
+ if (wpcap_packet_request(adapter, OID_802_11_CONFIGURATION, FALSE /* !set */, (char *) values, &length)) {
+ configuration = (struct ndis_configuration *) values;
+
+ add_string_to_table(table, row, "Channel",
+ val_to_str(configuration->ds_config, win32_802_11_channel_freq_vals, "(%u kHz)"));
+ entries++;
+ } else {
+ add_string_to_table(table, row, "Channel", "-");
+ }
+
+ /* BSSID list: first trigger a scan */
+ length = sizeof(uint_value);
+ if (wpcap_packet_request(adapter, OID_802_11_BSSID_LIST_SCAN, TRUE /* set */, (char *) &uint_value, &length)) {
+#if 0
+ g_warning("BSSID list scan done");
+ } else {
+ g_warning("BSSID list scan failed");
+#endif
+ }
+
+ /* BSSID list: get scan results */
+ /* XXX - we might have to wait up to 7 seconds! */
+ length = sizeof(ULONG) + sizeof(struct ndis_ssid_item) * 16;
+ bssid_list = g_malloc(length);
+ /* some drivers don't set bssid_list->num_items to 0 if
+ OID_802_11_BSSID_LIST returns no items (prism54 driver, e.g.,) */
+ memset(bssid_list, 0, length);
+
+ if (wpcap_packet_request(adapter, OID_802_11_BSSID_LIST, FALSE /* !set */, (char *) bssid_list, &length)) {
+ add_string_to_table(table, row, "", "");
+ add_string_to_table(table, row, "Available networks (BSSID list)", "");
+
+ capture_if_details_802_11_bssid_list(main_vb, bssid_list);
+ entries += bssid_list->num_items;
+ } else {
+ add_string_to_table(table, row, "Available networks (BSSID list)", "-");
+ }
+
+ g_free(bssid_list);
+
+ return entries;
+}
+
+
+static int
+capture_if_details_802_3(GtkWidget *table, GtkWidget *main_vb, guint *row, LPADAPTER adapter) {
+ unsigned int uint_value;
+ unsigned char values[100];
+ int length;
+ gchar string_buff[DETAILS_STR_MAX];
+ const gchar *manuf_name;
+ int entries = 0;
+
+
+ add_string_to_table(table, row, "Characteristics", "");
+
+ length = sizeof(values);
+ if (wpcap_packet_request(adapter, OID_802_3_PERMANENT_ADDRESS, FALSE /* !set */, values, &length)) {
+ manuf_name = get_manuf_name_if_known(values);
+ if(manuf_name != NULL) {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%02X:%02X:%02X:%02X:%02X:%02X (%s)",
+ values[0], values[1], values[2],
+ values[3], values[4], values[5],
+ manuf_name);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%02X:%02X:%02X:%02X:%02X:%02X",
+ values[0], values[1], values[2],
+ values[3], values[4], values[5]);
+ }
+ entries++;
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Permanent station address", string_buff);
+
+ length = sizeof(values);
+ if (wpcap_packet_request(adapter, OID_802_3_CURRENT_ADDRESS, FALSE /* !set */, values, &length)) {
+ manuf_name = get_manuf_name_if_known(values);
+ if(manuf_name != NULL) {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%02X:%02X:%02X:%02X:%02X:%02X (%s)",
+ values[0], values[1], values[2],
+ values[3], values[4], values[5],
+ manuf_name);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%02X:%02X:%02X:%02X:%02X:%02X",
+ values[0], values[1], values[2],
+ values[3], values[4], values[5]);
+ }
+ entries++;
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Current station address", string_buff);
+
+
+ add_string_to_table(table, row, "", "");
+ add_string_to_table(table, row, "Statistics", "");
+
+ if (wpcap_packet_request_uint(adapter, OID_802_3_RCV_ERROR_ALIGNMENT, &uint_value)) {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ entries++;
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Packets received with alignment error", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_802_3_XMIT_ONE_COLLISION, &uint_value)) {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ entries++;
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Packets transmitted with one collision", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_802_3_XMIT_MORE_COLLISIONS, &uint_value)) {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ entries++;
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Packets transmitted with more than one collision", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_802_3_RCV_OVERRUN, &uint_value)) {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ entries++;
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Packets not received due to overrun", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_802_3_XMIT_DEFERRED, &uint_value)) {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ entries++;
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Packets transmitted after deferred", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_802_3_XMIT_MAX_COLLISIONS, &uint_value)) {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ entries++;
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Packets not transmitted due to collisions", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_802_3_XMIT_UNDERRUN, &uint_value)) {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ entries++;
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Packets not transmitted due to underrun", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_802_3_XMIT_HEARTBEAT_FAILURE, &uint_value)) {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ entries++;
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Packets transmitted with heartbeat failure", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_802_3_XMIT_TIMES_CRS_LOST, &uint_value)) {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ entries++;
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Times carrier sense signal lost during transmission", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_802_3_XMIT_LATE_COLLISIONS, &uint_value)) {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ entries++;
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Times late collisions detected", string_buff);
+
+ return entries;
+}
+
+static int
+capture_if_details_task_offload(GtkWidget *table, GtkWidget *main_vb, guint *row, LPADAPTER adapter) {
+ NDIS_TASK_OFFLOAD_HEADER *offload;
+ unsigned char values[10000];
+ int length;
+ gchar string_buff[DETAILS_STR_MAX];
+ int entries = 0;
+ int TcpIpChecksumSupported = 0;
+ int IpSecSupported = 0;
+ int TcpLargeSendSupported = 0;
+
+
+ /* Task Offload */
+ offload = (NDIS_TASK_OFFLOAD_HEADER *) values;
+ offload->Version = NDIS_TASK_OFFLOAD_VERSION;
+ offload->Size = sizeof(NDIS_TASK_OFFLOAD_HEADER);
+ offload->Reserved = 0;
+ offload->OffsetFirstTask = 0;
+ /* the EncapsulationFormat seems to be ignored on a query (using Ethernet values) */
+ offload->EncapsulationFormat.Encapsulation = IEEE_802_3_Encapsulation;
+ offload->EncapsulationFormat.Flags.FixedHeaderSize = 1;
+ offload->EncapsulationFormat.Flags.Reserved = 0;
+ offload->EncapsulationFormat.EncapsulationHeaderSize = 14; /* sizeof(struct ether_header) */;
+
+ length = sizeof(values);
+ if (wpcap_packet_request(adapter, OID_TCP_TASK_OFFLOAD, FALSE /* !set */, values, &length)) {
+ NDIS_TASK_OFFLOAD *of;
+ /* XXX - hmmm, using a tvb for this? */
+ unsigned char *valuep = values + offload->OffsetFirstTask;
+ length -= offload->OffsetFirstTask;
+
+ do {
+ of = (NDIS_TASK_OFFLOAD *) valuep;
+
+ switch(of->Task) {
+ case TcpIpChecksumNdisTask:
+ {
+ NDIS_TASK_TCP_IP_CHECKSUM *tic = (NDIS_TASK_TCP_IP_CHECKSUM *) (of->TaskBuffer);
+
+ entries++;
+ TcpIpChecksumSupported++;
+
+ add_string_to_table(table, row, "TCP/IP Checksum", "");
+
+ g_snprintf(string_buff, DETAILS_STR_MAX, "");
+ add_string_to_table(table, row, "V4 transmit checksum", "");
+
+ g_snprintf(string_buff, DETAILS_STR_MAX, "TCP: %s, UDP: %s, IP: %s",
+ tic->V4Transmit.TcpChecksum ? "Yes" : "No",
+ tic->V4Transmit.UdpChecksum ? "Yes" : "No",
+ tic->V4Transmit.IpChecksum ? "Yes" : "No");
+ add_string_to_table(table, row, "Calculation supported", string_buff);
+
+ g_snprintf(string_buff, DETAILS_STR_MAX, "TCP: %s, IP: %s",
+ tic->V4Transmit.TcpOptionsSupported ? "Yes" : "No",
+ tic->V4Transmit.IpOptionsSupported ? "Yes" : "No");
+ add_string_to_table(table, row, "Options fields supported", string_buff);
+
+ g_snprintf(string_buff, DETAILS_STR_MAX, "");
+ add_string_to_table(table, row, "V4 receive checksum", "");
+
+ g_snprintf(string_buff, DETAILS_STR_MAX, "TCP: %s, UDP: %s, IP: %s",
+ tic->V4Receive.TcpChecksum ? "Yes" : "No",
+ tic->V4Receive.UdpChecksum ? "Yes" : "No",
+ tic->V4Receive.IpChecksum ? "Yes" : "No");
+ add_string_to_table(table, row, "Validation supported", string_buff);
+
+ g_snprintf(string_buff, DETAILS_STR_MAX, "TCP: %s, IP: %s",
+ tic->V4Receive.TcpOptionsSupported ? "Yes" : "No",
+ tic->V4Receive.IpOptionsSupported ? "Yes" : "No");
+ add_string_to_table(table, row, "Options fields supported", string_buff);
+
+ g_snprintf(string_buff, DETAILS_STR_MAX, "");
+ add_string_to_table(table, row, "V6 transmit checksum", "");
+
+ g_snprintf(string_buff, DETAILS_STR_MAX, "TCP: %s, UDP: %s",
+ tic->V6Transmit.TcpChecksum ? "Yes" : "No",
+ tic->V6Transmit.UdpChecksum ? "Yes" : "No");
+ add_string_to_table(table, row, "Calculation supported", string_buff);
+
+ g_snprintf(string_buff, DETAILS_STR_MAX, "TCP: %s, IP: %s",
+ tic->V6Transmit.TcpOptionsSupported ? "Yes" : "No",
+ tic->V6Transmit.IpOptionsSupported ? "Yes" : "No");
+ add_string_to_table(table, row, "Options fields supported", string_buff);
+
+ g_snprintf(string_buff, DETAILS_STR_MAX, "");
+ add_string_to_table(table, row, "V6 receive checksum", "");
+
+ g_snprintf(string_buff, DETAILS_STR_MAX, "TCP: %s, UDP: %s",
+ tic->V6Receive.TcpChecksum ? "Yes" : "No",
+ tic->V6Receive.UdpChecksum ? "Yes" : "No");
+ add_string_to_table(table, row, "Validation supported", string_buff);
+
+ g_snprintf(string_buff, DETAILS_STR_MAX, "TCP: %s, IP: %s",
+ tic->V6Receive.TcpOptionsSupported ? "Yes" : "No",
+ tic->V6Receive.IpOptionsSupported ? "Yes" : "No");
+ add_string_to_table(table, row, "Options fields supported", string_buff);
+ }
+ break;
+ case IpSecNdisTask:
+ entries++;
+ IpSecSupported++;
+
+ add_string_to_table(table, row, "IPSEC", "");
+ g_snprintf(string_buff, DETAILS_STR_MAX, "IPSEC (TaskID 1) not decoded yet");
+ add_string_to_table(table, row, "Task", string_buff);
+ break;
+ case TcpLargeSendNdisTask:
+ {
+ NDIS_TASK_TCP_LARGE_SEND *tls = (NDIS_TASK_TCP_LARGE_SEND *) (of->TaskBuffer);
+
+ entries++;
+ TcpLargeSendSupported++;
+
+ add_string_to_table(table, row, "TCP large send", "");
+ /* XXX - while MSDN tells about version 0, we see version 1?!? */
+ if(tls->Version == 1) {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%u", tls->MaxOffLoadSize);
+ add_string_to_table(table, row, "Max Offload Size", string_buff);
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%u", tls->MinSegmentCount);
+ add_string_to_table(table, row, "Min Segment Count", string_buff);
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%s", tls->TcpOptions ? "Yes" : "No");
+ add_string_to_table(table, row, "TCP option fields", string_buff);
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%s", tls->IpOptions ? "Yes" : "No");
+ add_string_to_table(table, row, "IP option fields", string_buff);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%u (unknown)", tls->Version);
+ add_string_to_table(table, row, "Version", string_buff);
+ }
+ }
+ break;
+ default:
+ g_snprintf(string_buff, DETAILS_STR_MAX, "Unknown task %u", of->Task);
+ add_string_to_table(table, row, "Task", string_buff);
+
+ }
+
+ add_string_to_table(table, row, "", "");
+
+ valuep += of->OffsetNextTask;
+ length -= of->OffsetNextTask;
+ } while(of->OffsetNextTask != 0);
+ }
+
+ if(TcpIpChecksumSupported == 0) {
+ add_string_to_table(table, row, "TCP/IP Checksum", "");
+ add_string_to_table(table, row, "Offload not supported", "-");
+ }
+
+ if(IpSecSupported == 0) {
+ add_string_to_table(table, row, "IpSec", "");
+ add_string_to_table(table, row, "Offload not supported", "-");
+ }
+
+ if(TcpLargeSendSupported == 0) {
+ add_string_to_table(table, row, "TCP Large Send", "");
+ add_string_to_table(table, row, "Offload not supported", "-");
+ }
+
+ return entries;
+}
+
+static int
+capture_if_details_general(GtkWidget *table, GtkWidget *main_vb, guint *row, LPADAPTER adapter, gchar *iface) {
+ gchar string_buff[DETAILS_STR_MAX];
+ const gchar *manuf_name;
+ unsigned int uint_value;
+ unsigned int uint_array[50];
+ int uint_array_size;
+ unsigned int physical_medium;
+ int i;
+ unsigned char values[100];
+ int length;
+ unsigned short ushort_value;
+ int entries = 0;
+
+
+ /* general */
+ add_string_to_table(table, row, "Characteristics", "");
+
+ /* Vendor description */
+ length = sizeof(values);
+ if (wpcap_packet_request(adapter, OID_GEN_VENDOR_DESCRIPTION, FALSE /* !set */, values, &length)) {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%s", values);
+ entries++;
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Vendor description", string_buff);
+
+ /* Interface */
+ add_string_to_table(table, row, "Interface", iface);
+
+ /* link status (connected/disconnected) */
+ if (wpcap_packet_request_uint(adapter, OID_GEN_MEDIA_CONNECT_STATUS, &uint_value)) {
+ entries++;
+ if(uint_value == 0) {
+ add_string_to_table(table, row, "Link status", "Connected");
+ } else {
+ add_string_to_table(table, row, "Link status", "Disconnected");
+ }
+ } else {
+ add_string_to_table(table, row, "Link status", "-");
+ }
+
+ /* link speed */
+ if (wpcap_packet_request_uint(adapter, OID_GEN_LINK_SPEED, &uint_value)) {
+ entries++;
+ uint_value *= 100;
+ if(uint_value >= 1000 * 1000) {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d MBits/s", uint_value / 1000 / 1000);
+ } else {
+ if(uint_value >= 1000) {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d KBits/s", uint_value / 1000);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d Bits/s", uint_value);
+ }
+ }
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Link speed", string_buff);
+
+
+
+ uint_array_size = sizeof(uint_array);
+ if (wpcap_packet_request(adapter, OID_GEN_MEDIA_SUPPORTED, FALSE /* !set */, (char *) uint_array, &uint_array_size)) {
+ entries++;
+ uint_array_size /= sizeof(unsigned int);
+ i=0;
+ while(uint_array_size--) {
+ add_string_to_table(table, row, "Media supported",
+ val_to_str(uint_array[i], win32_802_3_medium_vals, "(0x%x)"));
+ i++;
+ }
+ } else {
+ add_string_to_table(table, row, "Media supported", "-");
+ }
+
+ uint_array_size = sizeof(uint_array);
+ if (wpcap_packet_request(adapter, OID_GEN_MEDIA_IN_USE, FALSE /* !set */, (char *) uint_array, &uint_array_size)) {
+ entries++;
+ uint_array_size /= sizeof(unsigned int);
+ i=0;
+ while(uint_array_size--) {
+ add_string_to_table(table, row, "Medium in use",
+ val_to_str(uint_array[i], win32_802_3_medium_vals, "(0x%x)"));
+ i++;
+ }
+ } else {
+ add_string_to_table(table, row, "Medium in use", "-");
+ }
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_PHYSICAL_MEDIUM, &physical_medium)) {
+ entries++;
+ add_string_to_table(table, row, "Physical medium",
+ val_to_str(physical_medium, win32_802_3_physical_medium_vals, "(0x%x)"));
+ } else {
+ add_string_to_table(table, row, "Physical medium", "-");
+ }
+
+ length = sizeof(ushort_value);
+ if (wpcap_packet_request(adapter, OID_GEN_DRIVER_VERSION, FALSE /* !set */, (char *) &ushort_value, &length)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%u.%u", ushort_value / 0x100, ushort_value % 0x100);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "NDIS Driver Version", string_buff);
+
+ length = sizeof(uint_value);
+ if (wpcap_packet_request(adapter, OID_GEN_VENDOR_DRIVER_VERSION, FALSE /* !set */, (char *) &uint_value, &length)) {
+ entries++;
+ /* XXX - what's the correct output format? */
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%u.%u (Hex: %X.%X)",
+ (uint_value / 0x10000 ) % 0x10000,
+ uint_value % 0x10000,
+ (uint_value / 0x10000 ) % 0x10000,
+ uint_value % 0x10000);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Vendor Driver Version", string_buff);
+
+ length = sizeof(values);
+ if (wpcap_packet_request(adapter, OID_GEN_VENDOR_ID, FALSE /* !set */, values, &length)) {
+ entries++;
+ manuf_name = get_manuf_name_if_known(values);
+ if(manuf_name != NULL) {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%02X:%02X:%02X (%s) NIC: %02X",
+ values[0], values[1], values[2], manuf_name, values[3]);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%02X:%02X:%02X NIC: %02X",
+ values[0], values[1], values[2], values[3]);
+ }
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Vendor ID", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_MAC_OPTIONS, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX,
+ "802.1P Priority: %s, 802.1Q VLAN: %s",
+ (uint_value & NDIS_MAC_OPTION_8021P_PRIORITY) ? "Supported" : "Unsupported",
+ (uint_value & NDIS_MAC_OPTION_8021Q_VLAN) ? "Supported" : "Unsupported" );
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "MAC Options", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_VLAN_ID, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%u", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "VLAN ID", string_buff);
+
+#if 0
+ /* value seems to be constant */
+ if (wpcap_packet_request_uint(adapter, OID_GEN_CURRENT_PACKET_FILTER, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Packet filter", string_buff);
+#endif
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_TRANSMIT_BUFFER_SPACE, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Transmit Buffer Space", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_RECEIVE_BUFFER_SPACE, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Receive Buffer Space", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_TRANSMIT_BLOCK_SIZE , &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Transmit Block Size", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_RECEIVE_BLOCK_SIZE, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Receive Block Size", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_MAXIMUM_TOTAL_SIZE, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Maximum Packet Size", string_buff);
+
+ return entries;
+}
+
+
+static int
+capture_if_details_stats(GtkWidget *table, GtkWidget *main_vb, guint *row, LPADAPTER adapter) {
+ gchar string_buff[DETAILS_STR_MAX];
+ unsigned int uint_value;
+ int entries = 0;
+
+
+ add_string_to_table(table, row, "Statistics", "");
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_XMIT_OK, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Transmit OK", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_XMIT_ERROR, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Transmit Error", string_buff);
+
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_RCV_OK, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Receive OK", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_RCV_ERROR, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Receive Error", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_RCV_NO_BUFFER, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Receive but no Buffer", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_DIRECTED_BYTES_XMIT, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Directed bytes transmitted w/o errors", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_DIRECTED_FRAMES_XMIT, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Directed packets transmitted w/o errors", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_MULTICAST_BYTES_XMIT, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Multicast bytes transmitted w/o errors", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_MULTICAST_FRAMES_XMIT, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Multicast packets transmitted w/o errors", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_BROADCAST_BYTES_XMIT, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Broadcast bytes transmitted w/o errors", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_BROADCAST_FRAMES_XMIT, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Broadcast packets transmitted w/o errors", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_DIRECTED_BYTES_RCV, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Directed bytes received w/o errors", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_DIRECTED_FRAMES_RCV, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Directed packets received w/o errors", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_MULTICAST_BYTES_RCV, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Multicast bytes received w/o errors", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_MULTICAST_FRAMES_RCV, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Multicast packets received w/o errors", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_BROADCAST_BYTES_RCV, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Broadcast bytes received w/o errors", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_BROADCAST_FRAMES_RCV, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Broadcast packets received w/o errors", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_RCV_CRC_ERROR, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Packets received with CRC or FCS errors", string_buff);
+
+ if (wpcap_packet_request_uint(adapter, OID_GEN_TRANSMIT_QUEUE_LENGTH, &uint_value)) {
+ entries++;
+ g_snprintf(string_buff, DETAILS_STR_MAX, "%d", uint_value);
+ } else {
+ g_snprintf(string_buff, DETAILS_STR_MAX, "-");
+ }
+ add_string_to_table(table, row, "Packets queued for transmission", string_buff);
+
+ return entries;
+}
+
+
+static GtkWidget *
+capture_if_details_page_new(GtkWidget **table)
+{
+ GtkWidget *main_vb;
+
+ main_vb = gtk_vbox_new(FALSE, 6);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 12);
+
+ /* table */
+ *table = gtk_table_new(1, 2, FALSE);
+ gtk_table_set_col_spacings(GTK_TABLE(*table), 6);
+ gtk_table_set_row_spacings(GTK_TABLE(*table), 3);
+ gtk_container_add(GTK_CONTAINER(main_vb), *table);
+
+ return main_vb;
+}
+
+
+static void
+capture_if_details_open_win(char *iface)
+{
+ GtkWidget *details_open_w,
+ *main_vb, *bbox, *close_bt, *help_bt;
+ GtkWidget *page_general, *page_stats, *page_802_3, *page_802_11, *page_task_offload;
+ GtkWidget *page_lb;
+ GtkWidget *table, *notebook, *label;
+ guint row;
+ LPADAPTER adapter;
+ int entries;
+
+
+ /* open the network adapter */
+ adapter = wpcap_packet_open(iface);
+ if(adapter == NULL) {
+ /*
+ * We have an adapter that is not exposed through normal APIs (e.g. TurboCap)
+ * or an adapter that isn't plugged in.
+ *
+ * XXX - We should use the TurboCap API to get info about TurboCap adapters.
+ */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%sCould not open adapter %s!%s"
+ "\n\nHas it been unplugged?",
+ simple_dialog_primary_start(), iface, simple_dialog_primary_end());
+ return;
+ }
+
+ /* open a new window */
+ details_open_w = dlg_window_new("Wireshark: Interface Details"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(details_open_w), TRUE);
+
+ /* Container for the window contents */
+ main_vb = gtk_vbox_new(FALSE, 12);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 12);
+ gtk_container_add(GTK_CONTAINER(details_open_w), main_vb);
+
+ /* notebook */
+ notebook = gtk_notebook_new();
+ gtk_box_pack_start(GTK_BOX(main_vb), notebook, TRUE /*expand*/, TRUE /*fill*/, 0 /*padding*/);
+
+ /* General page */
+ page_general = capture_if_details_page_new(&table);
+ page_lb = gtk_label_new("Characteristics");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_general, page_lb);
+ row = 0;
+ entries = capture_if_details_general(table, page_general, &row, adapter, iface);
+ if(entries == 0) {
+ gtk_widget_set_sensitive(page_lb, FALSE);
+ }
+
+ /* Statistics page */
+ page_stats = capture_if_details_page_new(&table);
+ page_lb = gtk_label_new("Statistics");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_stats, page_lb);
+ row = 0;
+ entries = capture_if_details_stats(table, page_stats, &row, adapter);
+ if(entries == 0) {
+ gtk_widget_set_sensitive(page_lb, FALSE);
+ }
+
+ /* 802.3 (Ethernet) page */
+ page_802_3 = capture_if_details_page_new(&table);
+ page_lb = gtk_label_new("802.3 (Ethernet)");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_802_3, page_lb);
+ row = 0;
+ entries = capture_if_details_802_3(table, page_802_3, &row, adapter);
+ if(entries == 0) {
+ gtk_widget_set_sensitive(page_lb, FALSE);
+ }
+
+ /* 802_11 (WI-FI) page */
+ page_802_11 = capture_if_details_page_new(&table);
+ page_lb = gtk_label_new("802.11 (WLAN)");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_802_11, page_lb);
+ row = 0;
+ entries = capture_if_details_802_11(table, page_802_11, &row, adapter);
+ if(entries == 0) {
+ gtk_widget_set_sensitive(page_lb, FALSE);
+ }
+
+ /* Task offload page */
+ page_task_offload = capture_if_details_page_new(&table);
+ page_lb = gtk_label_new("Task Offload");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_task_offload, page_lb);
+ row = 0;
+ entries = capture_if_details_task_offload(table, page_task_offload, &row, adapter);
+ if(entries == 0) {
+ gtk_widget_set_sensitive(page_lb, FALSE);
+ }
+
+ wpcap_packet_close(adapter);
+
+ label = gtk_label_new("Note: the accuracy of all of these values relies only on the network card driver!");
+ gtk_box_pack_start(GTK_BOX(main_vb), label, FALSE /*expand*/, FALSE /*fill*/, 0 /*padding*/);
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE /*expand*/, FALSE /*fill*/, 0 /*padding*/);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(details_open_w, close_bt, window_cancel_button_cb);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer) (HELP_CAPTURE_INTERFACES_DETAILS_DIALOG));
+
+ gtk_widget_grab_focus(close_bt);
+
+ g_signal_connect(details_open_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+
+ gtk_widget_show_all(details_open_w);
+ window_present(details_open_w);
+}
+
+
+static void capture_if_details_open_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
+{
+ switch(btn) {
+ case(ESD_BTN_OK):
+ capture_if_details_open_win(data);
+ break;
+ case(ESD_BTN_CANCEL):
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+
+void
+capture_if_details_open(char *iface)
+{
+ char *version;
+ gboolean version_ok = FALSE;
+ gpointer dialog;
+
+
+ /* check packet.dll version */
+ version = wpcap_packet_get_version();
+
+ if(version == NULL) {
+ /* couldn't even get the packet.dll version, must be a very old one or just not existing -> give up */
+ /* (this seems to be the case for 2.3 beta and all previous releases) */
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "%sCouldn't obtain WinPcap packet.dll version!%s"
+ "\n\nThe WinPcap packet.dll is not installed or the version you use seems to be very old!"
+ "\n\nPlease update/install WinPcap.",
+ simple_dialog_primary_start(), simple_dialog_primary_end());
+ return;
+ }
+
+ /* XXX - add more known DLL versions here */
+ /* (all versions since the 2.3 release seems to be working (although the 2.3 beta did not) */
+ if(
+ /*
+ * 4.0 version strings:
+ * 4.0.0.703: 4.0 beta 3
+ * 4.0.0.655: 4.0 beta 2
+ * 4.0.0.592: 4.0 beta 1
+ */
+ strcmp(version, "3" ) > 0 || /* 4.0 and above (including betas) */
+ strcmp(version, "3, 2, 0, 29") == 0 || /* 3.2 beta 1 */
+ strcmp(version, "3, 1, 0, 27") == 0 || /* 3.1 release */
+ strcmp(version, "3, 1, 0, 24") == 0 || /* 3.1 beta 4 */
+ strcmp(version, "3, 1, 0, 23") == 0 || /* 3.1 beta 3 */
+ strcmp(version, "3, 1, 0, 22") == 0 || /* 3.1 beta 2 */
+ strcmp(version, "3, 1, 0, 20") == 0 || /* 3.1 beta */
+ strcmp(version, "3.0 alpha3" ) == 0 || /* 3.0 release or 3.0 beta (yes, both versions report alpha3!) */
+ strcmp(version, "2.3" ) == 0 /* 2.3 release */
+ ) {
+ version_ok = TRUE;
+ }
+
+ if(!version_ok) {
+ /* packet.dll version not known to us, warn user but try to continue */
+ dialog = simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK | ESD_BTN_CANCEL,
+ "%sUnknown WinPcap version might crash or fail!%s"
+ "\n\nThe WinPcap packet.dll version \"%s\" is unknown if it supports required functions!"
+ "\n\nOnly WinPcap versions 3.0 and 3.1 are known to work with this feature."
+ "\n\nCrashes or unexpected behaviour might occur, you have been warned!"
+ "\n\nContinue anyway?",
+ simple_dialog_primary_start(), simple_dialog_primary_end(), version);
+ simple_dialog_set_cb(dialog, capture_if_details_open_answered_cb, iface);
+ } else {
+ capture_if_details_open_win(iface);
+ }
+}
+
+gboolean
+capture_if_has_details(char *iface) {
+ LPADAPTER adapter;
+
+ if (!iface) {
+ return FALSE;
+ }
+
+ adapter = wpcap_packet_open(iface);
+ if (adapter) {
+ wpcap_packet_close(adapter);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+#endif /* HAVE_LIBPCAP && _WIN32 */
diff --git a/ui/gtk/capture_if_details_dlg_win32.h b/ui/gtk/capture_if_details_dlg_win32.h
new file mode 100644
index 0000000000..0873e1bd34
--- /dev/null
+++ b/ui/gtk/capture_if_details_dlg_win32.h
@@ -0,0 +1,45 @@
+/* capture_if_details_dlg.h
+ * Definitions for capture interface details window (only Win32!)
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __CAPTURE_IF_DETAILS_DLG_H__
+#define __CAPTURE_IF_DETAILS_DLG_H__
+
+/** @file
+ * Capture "Interface Details" dialog box
+ * @ingroup dialog_group
+ */
+
+/** Open the dialog box.
+ *
+ * @param iface the interface name to show
+ */
+extern void capture_if_details_open(char *iface);
+
+/** See if we have a detail-able interface.
+ *
+ * @param iface the interface name to test
+ */
+extern gboolean capture_if_has_details(char *iface);
+
+#endif /* __CAPTURE_IF_DETAILS_DLG_H__ */
diff --git a/ui/gtk/capture_if_dlg.c b/ui/gtk/capture_if_dlg.c
new file mode 100644
index 0000000000..958e8cbae5
--- /dev/null
+++ b/ui/gtk/capture_if_dlg.c
@@ -0,0 +1,1059 @@
+/* capture_if_dlg.c
+ * Routines for the capture interface dialog
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#ifdef HAVE_LIBPCAP
+
+#include <string.h>
+
+#ifdef __linux__
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+
+#include <epan/prefs.h>
+
+#include "../capture_ifinfo.h"
+#include "../simple_dialog.h"
+#include "../capture.h"
+#include "../capture-pcap-util.h"
+#include "../capture_ui_utils.h"
+#include "wsutil/file_util.h"
+#include <wiretap/wtap.h>
+
+#ifdef _WIN32
+#include "ui/gtk/capture_if_details_dlg_win32.h"
+#endif
+
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/capture_dlg.h"
+#include "ui/gtk/capture_if_dlg.h"
+#include "ui/gtk/recent.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/main_toolbar.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/webbrowser.h"
+#include "ui/gtk/capture_globals.h"
+#include "ui/gtk/network_icons.h"
+#include "ui/gtk/main_welcome.h"
+#include "ui/gtk/menus.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+#ifdef HAVE_AIRPCAP
+#include "../image/toolbar/capture_airpcap_16.xpm"
+#endif
+
+#if defined(HAVE_PCAP_REMOTE)
+#include "ui/gtk/remote_icons.h"
+#endif
+
+#ifdef _WIN32
+#include "../image/toolbar/capture_ethernet_16.xpm"
+#include "../image/toolbar/modem_16.xpm"
+#endif
+
+#include "../image/toolbar/network_virtual_16.xpm"
+
+/* new buttons to be used instead of labels for 'Capture','Prepare',' */
+/*#include "../image/toolbar/capture_capture_16.xpm"*/
+/*#include "../image/toolbar/capture_prepare_16.xpm"*/
+/*#include "../image/toolbar/capture_details_16.xpm"*/
+
+
+#ifdef HAVE_AIRPCAP
+#include "airpcap.h"
+#include "airpcap_loader.h"
+#include "airpcap_gui_utils.h"
+#include "airpcap_dlg.h"
+#endif
+
+#define CAPTURE_IF_IP_ADDR_LABEL "capture_if_ip_addr_label"
+#define CAPTURE_IF_SELECTED_IP_ADDR "capture_if_selected_ip_addr"
+
+/*
+ * Keep a static pointer to the current "Capture Interfaces" window, if
+ * any, so that if somebody tries to do "Capture:Start" while there's
+ * already a "Capture Interfaces" window up, we just pop up the existing
+ * one, rather than creating a new one.
+ */
+static GtkWidget *cap_if_w;
+
+static GList *if_data_list = NULL;
+
+static guint timer_id;
+
+static GtkWidget *stop_bt, *capture_bt, *options_bt;
+
+static GList *if_list;
+
+static guint currently_selected = 0;
+
+static if_stat_cache_t *sc;
+
+/*
+ * Timeout, in milliseconds, for reads from the stream of captured packets.
+ */
+#define CAP_READ_TIMEOUT 250
+
+
+/* the "runtime" data of one interface */
+typedef struct if_dlg_data_s {
+ GtkWidget *device_lb;
+ GtkWidget *descr_lb;
+ GtkWidget *ip_lb;
+ GtkWidget *curr_lb;
+ GtkWidget *last_lb;
+ GtkWidget *choose_bt;
+#ifdef _WIN32
+ GtkWidget *details_bt;
+#endif
+ guint32 last_packets;
+ gchar *device;
+ if_info_t if_info;
+ gboolean selected;
+} if_dlg_data_t;
+
+static gboolean gbl_capture_in_progress = FALSE;
+
+void
+update_selected_interface(gchar *name, gboolean activate)
+{
+ guint ifs;
+ GList *curr;
+ if_dlg_data_t *temp;
+
+ for (ifs = 0; ifs < g_list_length(if_data_list); ifs++) {
+ curr = g_list_nth(if_data_list, ifs);
+ temp = (if_dlg_data_t *)(curr->data);
+ if (strcmp(name, temp->if_info.name) == 0) {
+ if (activate) {
+ gtk_toggle_button_set_active((GtkToggleButton *)temp->choose_bt, TRUE);
+ } else {
+ gtk_toggle_button_set_active((GtkToggleButton *)temp->choose_bt, FALSE);
+ }
+ break;
+ }
+ }
+}
+
+static void
+store_selected(GtkWidget *choose_bt, gpointer if_data)
+{
+ if_dlg_data_t *if_dlg_data = (if_dlg_data_t *)if_data, *temp;
+ GList *curr;
+ unsigned int ifs, i;
+ gboolean found;
+ cap_settings_t cap_settings;
+ interface_options interface_opts;
+
+ for (ifs = 0; ifs < g_list_length(if_data_list); ifs++) {
+ curr = g_list_nth(if_data_list, ifs);
+ temp = (if_dlg_data_t *)(curr->data);
+ found = FALSE;
+ if (strcmp(if_dlg_data->if_info.name, temp->if_info.name) == 0) {
+ temp->selected ^=1;
+ if_data_list = g_list_remove(if_data_list, curr->data);
+ if_data_list = g_list_insert(if_data_list, temp, ifs);
+
+ for (i = 0; i < global_capture_opts.ifaces->len; i++) {
+ if (strcmp(g_array_index(global_capture_opts.ifaces, interface_options, i).name, temp->if_info.name) == 0) {
+ found = TRUE;
+ if (!temp->selected) {
+ interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, i);
+ global_capture_opts.ifaces = g_array_remove_index(global_capture_opts.ifaces, i);
+ if (gtk_widget_is_focus(choose_bt) && get_welcome_window()) {
+ change_interface_selection(interface_opts.name, FALSE);
+ }
+ if (gtk_widget_is_focus(choose_bt) && dlg_window_present()) {
+ enable_selected_interface(interface_opts.name, FALSE);
+ }
+ g_free(interface_opts.name);
+ g_free(interface_opts.descr);
+ g_free(interface_opts.cfilter);
+#ifdef HAVE_PCAP_REMOTE
+ g_free(interface_opts.remote_host);
+ g_free(interface_opts.remote_port);
+ g_free(interface_opts.auth_username);
+ g_free(interface_opts.auth_password);
+#endif
+ break;
+ }
+ }
+ }
+ if (!found && temp->selected) {
+ interface_opts.name = g_strdup(temp->if_info.name);
+ interface_opts.descr = get_interface_descriptive_name(interface_opts.name);
+ interface_opts.linktype = capture_dev_user_linktype_find(interface_opts.name);
+ interface_opts.cfilter = g_strdup(global_capture_opts.default_options.cfilter);
+ interface_opts.has_snaplen = global_capture_opts.default_options.has_snaplen;
+ interface_opts.snaplen = global_capture_opts.default_options.snaplen;
+ cap_settings = capture_get_cap_settings (interface_opts.name);;
+ interface_opts.promisc_mode = global_capture_opts.default_options.promisc_mode;
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ interface_opts.buffer_size = global_capture_opts.default_options.buffer_size;
+#endif
+ interface_opts.monitor_mode = cap_settings.monitor_mode;
+#ifdef HAVE_PCAP_REMOTE
+ interface_opts.src_type = global_capture_opts.default_options.src_type;
+ interface_opts.remote_host = g_strdup(global_capture_opts.default_options.remote_host);
+ interface_opts.remote_port = g_strdup(global_capture_opts.default_options.remote_port);
+ interface_opts.auth_type = global_capture_opts.default_options.auth_type;
+ interface_opts.auth_username = g_strdup(global_capture_opts.default_options.auth_username);
+ interface_opts.auth_password = g_strdup(global_capture_opts.default_options.auth_password);
+ interface_opts.datatx_udp = global_capture_opts.default_options.datatx_udp;
+ interface_opts.nocap_rpcap = global_capture_opts.default_options.nocap_rpcap;
+ interface_opts.nocap_local = global_capture_opts.default_options.nocap_local;
+#endif
+#ifdef HAVE_PCAP_SETSAMPLING
+ interface_opts.sampling_method = global_capture_opts.default_options.sampling_method;
+ interface_opts.sampling_param = global_capture_opts.default_options.sampling_param;
+#endif
+ g_array_append_val(global_capture_opts.ifaces, interface_opts);
+ if (gtk_widget_is_focus(choose_bt) && get_welcome_window() != NULL) {
+ change_interface_selection(g_strdup(temp->if_info.name), TRUE);
+ }
+ if (gtk_widget_is_focus(choose_bt) && dlg_window_present()) {
+ enable_selected_interface(interface_opts.name, TRUE);
+ }
+ }
+
+ if (temp->selected)
+ currently_selected += 1;
+ else
+ currently_selected -= 1;
+ break;
+ }
+ }
+ if (cap_if_w) {
+ gtk_widget_set_sensitive(capture_bt, !gbl_capture_in_progress && (currently_selected > 0));
+ }
+}
+
+/* start capture button was pressed */
+static void
+#ifdef HAVE_AIRPCAP
+capture_do_cb(GtkWidget *capture_bt _U_, gpointer if_data)
+#else
+capture_do_cb(GtkWidget *capture_bt _U_, gpointer if_data _U_)
+#endif
+{
+ if_dlg_data_t *temp;
+ GList *curr;
+ int ifs;
+#ifdef HAVE_AIRPCAP
+ if_dlg_data_t *if_dlg_data = if_data;
+
+ airpcap_if_active = get_airpcap_if_from_name(airpcap_if_list, if_dlg_data->if_info.name);
+ airpcap_if_selected = airpcap_if_active;
+#endif
+
+ for (ifs = 0; (curr = g_list_nth(if_data_list, ifs)); ifs++) {
+ temp = (if_dlg_data_t *)(curr->data);
+ gtk_widget_set_sensitive(temp->choose_bt, FALSE);
+ }
+
+ /* XXX - remove this? */
+ if (global_capture_opts.save_file) {
+ g_free(global_capture_opts.save_file);
+ global_capture_opts.save_file = NULL;
+ }
+
+ if (global_capture_opts.ifaces->len > 1) {
+ global_capture_opts.use_pcapng = TRUE;
+ }
+ /* stop capturing from all interfaces, we are going to do real work now ... */
+ window_destroy(cap_if_w);
+
+ capture_start_cb(NULL, NULL);
+}
+
+
+/* prepare capture button was pressed */
+static void
+capture_prepare_cb(GtkWidget *prepare_bt _U_, gpointer if_data _U_)
+{
+ /* stop capturing from all interfaces, we are going to do real work now ... */
+ window_destroy(cap_if_w);
+ if (global_capture_opts.ifaces->len > 1) {
+ global_capture_opts.use_pcapng = TRUE;
+ }
+ capture_prep_cb(NULL, NULL);
+}
+
+
+#ifdef _WIN32
+/* capture details button was pressed */
+static void
+capture_details_cb(GtkWidget *details_bt _U_, gpointer if_data)
+{
+ if_dlg_data_t *if_dlg_data = if_data;
+
+
+ capture_if_details_open(if_dlg_data->device);
+}
+#endif
+
+/* update a single interface */
+static void
+update_if(if_dlg_data_t *if_dlg_data, if_stat_cache_t *sc)
+{
+ struct pcap_stat stats;
+ gchar *str;
+ guint diff;
+
+
+ /*
+ * Note that some versions of libpcap, on some versions of UN*X,
+ * pcap_stats() returns the number of packets since the last
+ * pcap_stats call.
+ *
+ * That's a bug, and should be fixed; "pcap_stats()" is supposed
+ * to work the same way on all platforms.
+ */
+ if (sc) {
+ if (capture_stats(sc, if_dlg_data->device, &stats)) {
+ diff = stats.ps_recv - if_dlg_data->last_packets;
+ if_dlg_data->last_packets = stats.ps_recv;
+
+ str = g_strdup_printf("%u", if_dlg_data->last_packets);
+ gtk_label_set_text(GTK_LABEL(if_dlg_data->curr_lb), str);
+ g_free(str);
+ str = g_strdup_printf("%u", diff);
+ gtk_label_set_text(GTK_LABEL(if_dlg_data->last_lb), str);
+ g_free(str);
+
+ gtk_widget_set_sensitive(if_dlg_data->curr_lb, diff);
+ gtk_widget_set_sensitive(if_dlg_data->last_lb, diff);
+ } else {
+ gtk_label_set_text(GTK_LABEL(if_dlg_data->curr_lb), "error");
+ gtk_label_set_text(GTK_LABEL(if_dlg_data->last_lb), "error");
+ }
+ }
+}
+
+/* update all interfaces */
+static gboolean
+update_all(gpointer data)
+{
+ GList *curr;
+ int ifs;
+ if_stat_cache_t *sc = (if_stat_cache_t *)data;
+
+ if (!cap_if_w) {
+ return FALSE;
+ }
+
+ for (ifs = 0; (curr = g_list_nth(if_data_list, ifs)); ifs++) {
+ update_if((if_dlg_data_t *)curr->data, sc);
+ }
+
+ return TRUE;
+}
+
+/* a live capture has started or stopped */
+void
+set_capture_if_dialog_for_capture_in_progress(gboolean capture_in_progress)
+{
+ gbl_capture_in_progress = capture_in_progress;
+ if (cap_if_w) {
+ gtk_widget_set_sensitive(stop_bt, capture_in_progress);
+ gtk_widget_set_sensitive(capture_bt, !capture_in_progress && (currently_selected > 0));
+ }
+}
+
+
+/* the window was closed, cleanup things */
+static void
+capture_if_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+ GList *curr;
+ int ifs;
+
+ g_source_remove(timer_id);
+
+ for (ifs = 0; (curr = g_list_nth(if_data_list, ifs)); ifs++) {
+ g_free(curr->data);
+ }
+
+ if_data_list = NULL;
+
+ free_interface_list(if_list);
+
+ /* Note that we no longer have a "Capture Options" dialog box. */
+ cap_if_w = NULL;
+
+ capture_stat_stop(sc);
+
+#ifdef HAVE_AIRPCAP
+ airpcap_set_toolbar_stop_capture(airpcap_if_active);
+#endif
+}
+
+
+/*
+ * Sorts the Interface List in alphabetical order
+ */
+gint if_list_comparator_alph (const void *first_arg, const void *second_arg){
+ const if_info_t *first = first_arg, *second = second_arg;
+
+ if (first != NULL && first->description != NULL &&
+ second != NULL && second->description != NULL) {
+ return g_ascii_strcasecmp(first->description, second->description);
+ } else {
+ return 0;
+ }
+}
+
+
+/*
+ * Used to retrieve the interface icon.
+ * This is hideously platform-dependent.
+ */
+GtkWidget * capture_get_if_icon(const if_info_t* if_info)
+{
+#if defined(__linux__)
+ ws_statb64 statb;
+ char *wireless_path;
+#endif
+
+#ifdef HAVE_PCAP_REMOTE
+ if (if_info->description && strstr(if_info->description, "on remote node") != NULL ) {
+ return pixbuf_to_widget(remote_sat_pb_data);
+ }
+#endif
+#if defined(_WIN32)
+ /*
+ * Much digging failed to reveal any obvious way to get something such
+ * as the SNMP MIB-II ifType value for an interface:
+ *
+ * http://www.iana.org/assignments/ianaiftype-mib
+ *
+ * by making some NDIS request.
+ */
+ if ( if_info->description && ( strstr(if_info->description,"generic dialup") != NULL ||
+ strstr(if_info->description,"PPP/SLIP") != NULL ) ) {
+ return xpm_to_widget(modem_16_xpm);
+ }
+
+ if ( if_info->description && ( strstr(if_info->description,"Wireless") != NULL ||
+ strstr(if_info->description,"802.11") != NULL || strstr(if_info->description,"AirPcap") != NULL ) ) {
+ return pixbuf_to_widget(network_wireless_pb_data);
+ }
+
+ if ( strstr(if_info->name,"airpcap") != NULL ) {
+ return pixbuf_to_widget(network_wireless_pb_data);
+ }
+
+ if ( if_info->description && strstr(if_info->description, "Bluetooth") != NULL ) {
+ return pixbuf_to_widget(network_bluetooth_pb_data);
+ }
+#elif defined(__APPLE__)
+ /*
+ * XXX - yes, fetching all the network addresses for an interface
+ * gets you an AF_LINK address, of type "struct sockaddr_dl", and,
+ * yes, that includes an SNMP MIB-II ifType value.
+ *
+ * However, it's IFT_ETHER, i.e. Ethernet, for AirPort interfaces,
+ * not IFT_IEEE80211 (which isn't defined in OS X in any case).
+ *
+ * Perhaps some other BSD-flavored OSes won't make this mistake;
+ * however, FreeBSD 7.0 and OpenBSD 4.2, at least, appear to have
+ * made the same mistake, at least for my Belkin ZyDAS stick.
+ *
+ * On Mac OS X, one might be able to get the information one wants from
+ * IOKit.
+ */
+ if ( strcmp(if_info->name, "en1") == 0) {
+ return pixbuf_to_widget(network_wireless_pb_data);
+ }
+
+ /*
+ * XXX - PPP devices have names beginning with "ppp" and an IFT_ of
+ * IFT_PPP, but they could be dial-up, or PPPoE, or mobile phone modem,
+ * or VPN, or... devices. One might have to dive into the bowels of
+ * IOKit to find out.
+ */
+
+ /*
+ * XXX - there's currently no support for raw Bluetooth capture,
+ * and IP-over-Bluetooth devices just look like fake Ethernet
+ * devices. There's also Bluetooth modem support, but that'll
+ * probably just give you a device that looks like a PPP device.
+ */
+#elif defined(__linux__)
+ /*
+ * Look for /sys/class/net/{device}/wireless.
+ */
+ wireless_path = g_strdup_printf("/sys/class/net/%s/wireless", if_info->name);
+ if (wireless_path != NULL) {
+ if (ws_stat64(wireless_path, &statb) == 0) {
+ g_free(wireless_path);
+ return pixbuf_to_widget(network_wireless_pb_data);
+ }
+ g_free(wireless_path);
+ }
+
+ /*
+ * Bluetooth devices.
+ *
+ * XXX - this is for raw Bluetooth capture; what about IP-over-Bluetooth
+ * devices?
+ */
+ if ( strstr(if_info->name,"bluetooth") != NULL) {
+ return pixbuf_to_widget(network_bluetooth_pb_data);
+ }
+
+ /*
+ * USB devices.
+ */
+ if ( strstr(if_info->name,"usbmon") != NULL ) {
+ return pixbuf_to_widget(network_usb_pb_data);
+ }
+#endif
+
+ /*
+ * TODO: find a better icon!
+ * Bridge, NAT, or host-only interfaces on VMWare hosts have the name
+ * vmnet[0-9]+ or VMnet[0-9+ on Windows. Guests might use a native
+ * (LANCE or E1000) driver or the vmxnet driver. These devices have an
+ * IFT_ of IFT_ETHER, so we have to check the name.
+ */
+ if ( g_ascii_strncasecmp(if_info->name, "vmnet", 5) == 0) {
+ return xpm_to_widget(network_virtual_16_xpm);
+ }
+
+ if ( g_ascii_strncasecmp(if_info->name, "vmxnet", 6) == 0) {
+ return xpm_to_widget(network_virtual_16_xpm);
+ }
+
+ if ( if_info->description && strstr(if_info->description, "VMware") != NULL ) {
+ return xpm_to_widget(network_virtual_16_xpm);
+ }
+
+ return pixbuf_to_widget(network_wired_pb_data);
+}
+
+
+static int
+get_ip_addr_count(GSList *addr_list)
+{
+ GSList *curr_addr;
+ if_addr_t *addr;
+ int count;
+
+ count = 0;
+ for (curr_addr = addr_list; curr_addr != NULL;
+ curr_addr = g_slist_next(curr_addr)) {
+ addr = (if_addr_t *)curr_addr->data;
+ switch (addr->ifat_type) {
+
+ case IF_AT_IPv4:
+ case IF_AT_IPv6:
+ count++;
+ break;
+
+ default:
+ /* In case we add non-IP addresses */
+ break;
+ }
+ }
+ return count;
+}
+
+static const gchar *
+set_ip_addr_label(GSList *addr_list, GtkWidget *ip_lb, guint selected_ip_addr)
+{
+ GSList *curr_addr;
+ if_addr_t *addr;
+ const gchar *addr_str = NULL;
+
+ curr_addr = g_slist_nth(addr_list, selected_ip_addr);
+ if (curr_addr) {
+ addr = (if_addr_t *)curr_addr->data;
+ switch (addr->ifat_type) {
+
+ case IF_AT_IPv4:
+ addr_str = ip_to_str((guint8 *)&addr->addr.ip4_addr);
+ break;
+
+ case IF_AT_IPv6:
+ addr_str = ip6_to_str((struct e_in6_addr *)&addr->addr.ip6_addr);
+ break;
+
+ default:
+ /* Ignore non-IP addresses, in case we ever support them */
+ break;
+ }
+ }
+
+ if (addr_str) {
+ gtk_label_set_text(GTK_LABEL(ip_lb), addr_str);
+ } else {
+ gtk_label_set_text(GTK_LABEL(ip_lb), "none");
+ }
+ g_object_set_data(G_OBJECT(ip_lb), CAPTURE_IF_SELECTED_IP_ADDR, GINT_TO_POINTER(selected_ip_addr));
+
+ return addr_str;
+}
+
+
+static gboolean
+ip_label_enter_cb(GtkWidget *eb, GdkEventCrossing *event _U_, gpointer user_data _U_)
+{
+ gtk_widget_set_state(eb, GTK_STATE_PRELIGHT);
+
+ return FALSE;
+}
+
+
+static gboolean
+ip_label_leave_cb(GtkWidget *eb, GdkEvent *event _U_, gpointer user_data _U_)
+{
+ gtk_widget_set_state(eb, GTK_STATE_NORMAL);
+
+ return FALSE;
+}
+
+
+static gboolean
+ip_label_press_cb(GtkWidget *widget, GdkEvent *event _U_, gpointer data)
+{
+ GtkWidget *ip_lb = (GtkWidget *)g_object_get_data(G_OBJECT(widget), CAPTURE_IF_IP_ADDR_LABEL);
+ GSList *addr_list = (GSList *)data;
+ GSList *curr_addr, *start_addr;
+ if_addr_t *addr;
+ guint selected_ip_addr = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(ip_lb), CAPTURE_IF_SELECTED_IP_ADDR));
+
+ /* Select next IP address */
+ start_addr = g_slist_nth(addr_list, selected_ip_addr);
+ for (;;) {
+ selected_ip_addr++;
+ if (g_slist_length(addr_list) <= selected_ip_addr) {
+ /* Wrap around */
+ selected_ip_addr = 0;
+ }
+ curr_addr = g_slist_nth(addr_list, selected_ip_addr);
+ if (curr_addr == start_addr) {
+ /* We wrapped all the way around */
+ break;
+ }
+
+ addr = (if_addr_t *)curr_addr->data;
+ switch (addr->ifat_type) {
+
+ case IF_AT_IPv4:
+ case IF_AT_IPv6:
+ goto found;
+
+ default:
+ /* In case we add non-IP addresses */
+ break;
+ }
+ }
+
+found:
+ set_ip_addr_label(addr_list, ip_lb, selected_ip_addr);
+
+ return FALSE;
+}
+
+static void
+capture_if_stop_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ guint ifs;
+ GList *curr;
+ if_dlg_data_t *if_data;
+
+ for (ifs = 0; ifs < g_list_length(if_data_list); ifs++) {
+ curr = g_list_nth(if_data_list, ifs);
+ if_data = (if_dlg_data_t *)(curr->data);
+ gtk_widget_set_sensitive(if_data->choose_bt, TRUE);
+ }
+ capture_stop_cb(NULL, NULL);
+}
+
+
+/* start getting capture stats from all interfaces */
+void
+capture_if_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ GtkWidget *main_vb,
+ *main_sw,
+ *bbox,
+ *close_bt,
+ *help_bt,
+ *icon;
+
+#ifdef HAVE_AIRPCAP
+ GtkWidget *decryption_cb;
+#endif
+
+ GtkWidget *if_tb;
+ GtkWidget *if_lb;
+ GtkWidget *eb;
+ int err;
+ gchar *err_str;
+ GtkRequisition requisition;
+ int row, height;
+ if_dlg_data_t *if_dlg_data = NULL;
+ int ifs;
+ GList *curr;
+ if_info_t *if_info;
+ GString *if_tool_str = g_string_new("");
+ const gchar *addr_str;
+ gchar *user_descr;
+ int preselected = 0, i;
+ interface_options interface_opts;
+ gboolean found = FALSE;
+
+ if (cap_if_w != NULL) {
+ /* There's already a "Capture Interfaces" dialog box; reactivate it. */
+ reactivate_window(cap_if_w);
+ return;
+ }
+
+ preselected = global_capture_opts.ifaces->len;
+ /* LOAD THE INTERFACES */
+ if_list = capture_interface_list(&err, &err_str);
+ if_list = g_list_sort (if_list, if_list_comparator_alph);
+ if (if_list == NULL) {
+ switch (err) {
+
+ case CANT_GET_INTERFACE_LIST:
+ case DONT_HAVE_PCAP:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_str);
+ g_free(err_str);
+ break;
+
+ case NO_INTERFACES_FOUND:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "There are no interfaces on which a capture can be done.");
+ break;
+ }
+ return;
+ }
+
+#ifdef HAVE_AIRPCAP
+ /* LOAD AIRPCAP INTERFACES */
+ airpcap_if_list = get_airpcap_interface_list(&err, &err_str);
+ if (airpcap_if_list == NULL)
+ airpcap_if_active = airpcap_if_selected = NULL;
+
+ decryption_cb = g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_DECRYPTION_KEY);
+ update_decryption_mode_list(decryption_cb);
+
+ if (airpcap_if_list == NULL && err == CANT_GET_AIRPCAP_INTERFACE_LIST) {
+#if 0
+ /* XXX - Do we need to show an error here? */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_str);
+#endif
+ g_free(err_str);
+ }
+
+ /* If no airpcap interface is present, gray everything */
+ if (airpcap_if_active == NULL) {
+ if (airpcap_if_list == NULL) {
+ /*No airpcap device found */
+ airpcap_enable_toolbar_widgets(airpcap_tb,FALSE);
+ } else {
+ /* default adapter is not airpcap... or is airpcap but is not found*/
+ airpcap_set_toolbar_stop_capture(airpcap_if_active);
+ airpcap_enable_toolbar_widgets(airpcap_tb,FALSE);
+ }
+ }
+
+ airpcap_set_toolbar_start_capture(airpcap_if_active);
+#endif
+
+ cap_if_w = dlg_window_new("Wireshark: Capture Interfaces"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(cap_if_w), TRUE);
+
+ main_sw = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(main_sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_container_add(GTK_CONTAINER(cap_if_w), main_sw);
+
+ main_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(main_sw), main_vb);
+
+ if_tb = gtk_table_new(1,9, FALSE);
+ gtk_table_set_row_spacings(GTK_TABLE(if_tb), 3);
+ gtk_table_set_col_spacings(GTK_TABLE(if_tb), 3);
+ gtk_box_pack_start(GTK_BOX(main_vb), if_tb, FALSE, FALSE, 0);
+
+ row = 0;
+ height = 0;
+
+ /* This is the icon column, used to display which kind of interface we have */
+ if_lb = gtk_label_new("");
+ gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 0, 1, row, row+1);
+
+#ifndef _WIN32
+ /*
+ * On Windows, device names are generally not meaningful - NT 5
+ * uses long blobs with GUIDs in them, for example - so we don't
+ * bother showing them.
+ */
+ if_lb = gtk_label_new("Device");
+ gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 1, 4, row, row+1);
+#endif
+ if_lb = gtk_label_new("Description");
+ gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 4, 5, row, row+1);
+
+ if_lb = gtk_label_new(" IP ");
+ gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 5, 6, row, row+1);
+
+ if_lb = gtk_label_new("Packets");
+ gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 6, 7, row, row+1);
+
+ if_lb = gtk_label_new(" Packets/s ");
+ gtk_table_attach_defaults(GTK_TABLE(if_tb), if_lb, 7, 8, row, row+1);
+ row++;
+
+ height += 30;
+ /* Start gathering statistics (using dumpcap) */
+ sc = capture_stat_start(if_list);
+
+ /* List the interfaces */
+ currently_selected = 0;
+ for (ifs = 0; (curr = g_list_nth(if_list, ifs)); ifs++) {
+ g_string_assign(if_tool_str, "");
+ if_info = (if_info_t *)curr->data;
+
+ /* Continue if capture device is hidden */
+ if (prefs_is_capture_device_hidden(if_info->name)) {
+ continue;
+ }
+
+ if_dlg_data = g_new0(if_dlg_data_t,1);
+
+ if (preselected > 0) {
+ found = FALSE;
+ for (i = 0; i < (gint)global_capture_opts.ifaces->len; i++) {
+ interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, i);
+ if ((interface_opts.name == NULL) ||
+ (strcmp(interface_opts.name, (char*)if_info->name) != 0)) {
+ continue;
+ } else {
+ found = TRUE;
+ currently_selected++;
+ preselected--;
+ break;
+ }
+ }
+ if_dlg_data->selected = found;
+ } else {
+ if_dlg_data->selected = FALSE;
+ }
+ if_dlg_data->if_info = *if_info;
+
+ if_dlg_data->choose_bt = gtk_check_button_new();
+ gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->choose_bt, 0, 1, row, row+1);
+ if (gbl_capture_in_progress) {
+ gtk_widget_set_sensitive(if_dlg_data->choose_bt, FALSE);
+ } else {
+ gtk_widget_set_sensitive(if_dlg_data->choose_bt, TRUE);
+ }
+ gtk_toggle_button_set_active((GtkToggleButton *)if_dlg_data->choose_bt, if_dlg_data->selected);
+ g_signal_connect(if_dlg_data->choose_bt, "toggled", G_CALLBACK(store_selected), if_dlg_data);
+ /* Kind of adaptor (icon) */
+#ifdef HAVE_AIRPCAP
+ if (get_airpcap_if_from_name(airpcap_if_list,if_info->name) != NULL)
+ icon = xpm_to_widget(capture_airpcap_16_xpm);
+ else
+ icon = capture_get_if_icon(if_info);
+#else
+ icon = capture_get_if_icon(if_info);
+#endif
+ gtk_table_attach_defaults(GTK_TABLE(if_tb), icon, 1, 2, row, row+1);
+
+ /* device name */
+ if_dlg_data->device_lb = gtk_label_new(if_info->name);
+ if_dlg_data->device = if_info->name;
+#ifndef _WIN32
+ gtk_misc_set_alignment(GTK_MISC(if_dlg_data->device_lb), 0.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->device_lb, 2, 4, row, row+1);
+#endif
+ g_string_append(if_tool_str, "Device: ");
+ g_string_append(if_tool_str, if_info->name);
+ g_string_append(if_tool_str, "\n");
+
+ /* description */
+ user_descr = capture_dev_user_descr_find(if_info->name);
+ if (user_descr) {
+ if_dlg_data->descr_lb = gtk_label_new(user_descr);
+ g_free (user_descr);
+ } else {
+ if (if_info->description)
+ if_dlg_data->descr_lb = gtk_label_new(if_info->description);
+ else
+ if_dlg_data->descr_lb = gtk_label_new("");
+ }
+ gtk_misc_set_alignment(GTK_MISC(if_dlg_data->descr_lb), 0.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->descr_lb, 4, 5, row, row+1);
+
+ if (if_info->description) {
+ g_string_append(if_tool_str, "Description: ");
+ g_string_append(if_tool_str, if_info->description);
+ g_string_append(if_tool_str, "\n");
+ }
+
+ /* IP address */
+ /* Only one IP address will be shown, start with the first */
+ g_string_append(if_tool_str, "IP: ");
+ if_dlg_data->ip_lb = gtk_label_new("");
+ addr_str = set_ip_addr_label (if_info->addrs, if_dlg_data->ip_lb, 0);
+ if (addr_str) {
+ gtk_widget_set_sensitive(if_dlg_data->ip_lb, TRUE);
+ g_string_append(if_tool_str, addr_str);
+ } else {
+ gtk_widget_set_sensitive(if_dlg_data->ip_lb, FALSE);
+ g_string_append(if_tool_str, "none");
+ }
+ eb = gtk_event_box_new ();
+ gtk_container_add(GTK_CONTAINER(eb), if_dlg_data->ip_lb);
+ gtk_table_attach_defaults(GTK_TABLE(if_tb), eb, 5, 6, row, row+1);
+ if (get_ip_addr_count(if_info->addrs) > 1) {
+ /* More than one IP address, make it possible to toggle */
+ g_object_set_data(G_OBJECT(eb), CAPTURE_IF_IP_ADDR_LABEL, if_dlg_data->ip_lb);
+ g_signal_connect(eb, "enter-notify-event", G_CALLBACK(ip_label_enter_cb), NULL);
+ g_signal_connect(eb, "leave-notify-event", G_CALLBACK(ip_label_leave_cb), NULL);
+ g_signal_connect(eb, "button-press-event", G_CALLBACK(ip_label_press_cb), if_info->addrs);
+ }
+ g_string_append(if_tool_str, "\n");
+
+ /* packets */
+ if_dlg_data->curr_lb = gtk_label_new("-");
+ gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->curr_lb, 6, 7, row, row+1);
+
+ /* packets/s */
+ if_dlg_data->last_lb = gtk_label_new("-");
+ gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->last_lb, 7, 8, row, row+1);
+
+ /* details button */
+#ifdef _WIN32
+ if_dlg_data->details_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_CAPTURE_DETAILS);
+ gtk_widget_set_tooltip_text(if_dlg_data->details_bt, "Open the capture details dialog of this interface.");
+ gtk_table_attach_defaults(GTK_TABLE(if_tb), if_dlg_data->details_bt, 8, 9, row, row+1);
+ if (capture_if_has_details(if_dlg_data->device)) {
+ g_signal_connect(if_dlg_data->details_bt, "clicked", G_CALLBACK(capture_details_cb), if_dlg_data);
+ } else {
+ gtk_widget_set_sensitive(if_dlg_data->details_bt, FALSE);
+ }
+#endif
+
+ if_data_list = g_list_append(if_data_list, if_dlg_data);
+
+ row++;
+ if (row <= 10) {
+ /* Lets add up 10 rows of interfaces, otherwise the window may become too high */
+ gtk_widget_get_preferred_size(GTK_WIDGET(if_dlg_data->choose_bt), &requisition, NULL);
+ height += requisition.height;
+ }
+ }
+
+ g_string_free(if_tool_str, TRUE);
+
+ /* Button row: close, help, stop, start, and options button */
+ bbox = dlg_button_row_new(GTK_STOCK_HELP, WIRESHARK_STOCK_CAPTURE_START, WIRESHARK_STOCK_CAPTURE_OPTIONS, WIRESHARK_STOCK_CAPTURE_STOP, GTK_STOCK_CLOSE, NULL);
+
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 5);
+ help_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)(HELP_CAPTURE_INTERFACES_DIALOG));
+
+ stop_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_CAPTURE_STOP);
+ g_signal_connect(stop_bt, "clicked", G_CALLBACK(capture_if_stop_cb), NULL);
+ close_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(cap_if_w, close_bt, window_cancel_button_cb);
+ gtk_widget_set_tooltip_text(close_bt, "Close this window.");
+ options_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_CAPTURE_OPTIONS);
+ g_signal_connect(options_bt, "clicked", G_CALLBACK(capture_prepare_cb), if_dlg_data);
+ capture_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_CAPTURE_START);
+ g_signal_connect(capture_bt, "clicked", G_CALLBACK(capture_do_cb), if_dlg_data);
+ gtk_widget_get_preferred_size(GTK_WIDGET(close_bt), &requisition, NULL);
+ /* height + static offset + what the GTK MS Windows Engine needs in addition per interface */
+ height += requisition.height + 40 + ifs;
+ gtk_window_set_default_size(GTK_WINDOW(cap_if_w), -1, height);
+
+ gtk_widget_grab_default(close_bt);
+
+ g_signal_connect(cap_if_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(cap_if_w, "destroy", G_CALLBACK(capture_if_destroy_cb), sc);
+
+ gtk_widget_show_all(cap_if_w);
+ window_present(cap_if_w);
+
+ set_capture_if_dialog_for_capture_in_progress(gbl_capture_in_progress);
+
+ /* update the interface list every 1000ms */
+ timer_id = g_timeout_add(1000, update_all, sc);
+}
+
+gboolean interfaces_dialog_window_present(void)
+{
+ return (cap_if_w?TRUE:FALSE);
+}
+
+void refresh_if_window(void)
+{
+ capture_if_destroy_cb(NULL, NULL);
+ capture_if_cb(NULL, NULL);
+}
+
+void select_all_interfaces(gboolean enable)
+{
+ if_dlg_data_t *temp;
+ guint ifs;
+ GList *curr;
+
+ for (ifs = 0; ifs < g_list_length(if_data_list); ifs++) {
+ curr = g_list_nth(if_data_list, ifs);
+ temp = (if_dlg_data_t *)(curr->data);
+ update_selected_interface(temp->if_info.name, enable);
+ }
+}
+
+void destroy_if_window(void)
+{
+ if (cap_if_w) {
+ window_destroy(cap_if_w);
+ }
+}
+#else /* HAVE_LIBPCAP */
+
+void
+set_capture_if_dialog_for_capture_in_progress(gboolean capture_in_progress _U_)
+{
+}
+
+#endif /* HAVE_LIBPCAP */
diff --git a/ui/gtk/capture_if_dlg.h b/ui/gtk/capture_if_dlg.h
new file mode 100644
index 0000000000..3c2f2359d0
--- /dev/null
+++ b/ui/gtk/capture_if_dlg.h
@@ -0,0 +1,72 @@
+/* capture_if_dlg.h
+ * Definitions for packet capture interface windows
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __CAPTURE_IF_DLG_H__
+#define __CAPTURE_IF_DLG_H__
+
+/** User requested the "Capture Interfaces" dialog box by menu or toolbar.
+ *
+ * @param capture_in_progress capture is in progress
+ */
+void
+set_capture_if_dialog_for_capture_in_progress(gboolean capture_in_progress);
+
+/** User requested the "Capture Interfaces" dialog box by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+void
+capture_if_cb(GtkWidget *widget, gpointer data);
+
+#ifdef HAVE_LIBPCAP
+
+#include "capture_ifinfo.h" /* for if_info_t */
+
+/*
+ * Used to retrieve the interface icon
+ */
+GtkWidget *
+capture_get_if_icon(const if_info_t* if_info);
+
+void
+update_selected_interface(gchar *name, gboolean activate);
+
+gboolean
+interfaces_dialog_window_present(void);
+
+void
+refresh_if_window(void);
+
+void
+select_all_interfaces(gboolean enable);
+
+void
+destroy_if_window(void);
+
+#endif /* HAVE_LIBPCAP */
+
+#endif /* capture_if_dlg.h */
+
+
diff --git a/ui/gtk/capture_info_dlg.c b/ui/gtk/capture_info_dlg.c
new file mode 100644
index 0000000000..d99724fc51
--- /dev/null
+++ b/ui/gtk/capture_info_dlg.c
@@ -0,0 +1,371 @@
+/* capture_info_dlg.c
+ * Routines for packet capture info dialog
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_LIBPCAP
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <string.h>
+#include <time.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+
+#include "../capture.h"
+#include "../capture_info.h"
+#include "../capture_ui_utils.h"
+#include "../capture-pcap-util.h"
+
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/stock_icons.h"
+
+#ifdef HAVE_AIRPCAP
+#include <airpcap.h>
+#include "airpcap_loader.h"
+#include "airpcap_gui_utils.h"
+#include "airpcap_dlg.h"
+#endif
+
+
+/* a single capture counter value (with title, pointer to value and GtkWidgets) */
+/* as the packet_counts is a struct, not an array, keep a pointer to the */
+/* corresponding value packet_counts, to speed up (and simplify) output of values */
+typedef struct {
+ const gchar *title;
+ gint *value_ptr;
+ GtkWidget *label, *value_lb, *percent_pb, *percent_lb;
+} capture_info_counts_t;
+
+/* all data we need to know of this dialog, after creation finished */
+typedef struct {
+ GtkWidget *cap_w;
+ GtkWidget *running_time_lb;
+ capture_info_counts_t counts[PACKET_COUNTS_SIZE];
+ guint timer_id;
+ time_t start_time;
+} capture_info_ui_t;
+
+
+/* Workhorse for stopping capture */
+static void
+capture_info_stop(capture_options *capture_opts)
+{
+#ifdef HAVE_AIRPCAP
+ airpcap_set_toolbar_stop_capture(airpcap_if_active);
+#endif
+ capture_stop(capture_opts);
+}
+
+/* "delete-event" signal callback. Note different signature than "clicked" signal callback */
+static gboolean
+capture_info_delete_cb(GtkWidget *w _U_, GdkEvent *event _U_, gpointer data) {
+ capture_info_stop(data);
+ return TRUE;
+}
+
+/* "clicked" signal callback */
+static void
+capture_info_stop_clicked_cb(GtkButton *w _U_, gpointer data) {
+ capture_info_stop(data);
+}
+
+static gboolean
+capture_info_ui_update_cb(gpointer data)
+{
+ capture_info *cinfo = data;
+ capture_info_ui_t *info = cinfo->ui;
+
+ if (!info) /* ...which might happen on slow displays? */
+ return TRUE;
+
+ cinfo->running_time = time(NULL) - info->start_time;
+ capture_info_ui_update(cinfo);
+ return TRUE; /* call the timer again */
+}
+
+
+/* create the capture info dialog */
+/* will keep pointers to the fields in the counts parameter */
+void capture_info_ui_create(
+capture_info *cinfo,
+capture_options *capture_opts)
+{
+ unsigned int i;
+ GtkWidget *main_vb, *stop_bt, *counts_tb;
+ GtkWidget *counts_fr, *running_tb, *running_label, *bbox, *ci_help;
+ capture_info_ui_t *info;
+ gchar *cap_w_title;
+ gchar *title_iface;
+ gchar *descr;
+ GString *str;
+
+ info = g_malloc0(sizeof(capture_info_ui_t));
+ info->counts[0].title = "Total";
+ info->counts[0].value_ptr = &(cinfo->counts->total);
+ info->counts[1].title = "SCTP";
+ info->counts[1].value_ptr = &(cinfo->counts->sctp);
+ info->counts[2].title = "TCP";
+ info->counts[2].value_ptr = &(cinfo->counts->tcp);
+ info->counts[3].title = "UDP";
+ info->counts[3].value_ptr = &(cinfo->counts->udp);
+ info->counts[4].title = "ICMP";
+ info->counts[4].value_ptr = &(cinfo->counts->icmp);
+ info->counts[5].title = "ARP";
+ info->counts[5].value_ptr = &(cinfo->counts->arp);
+ info->counts[6].title = "OSPF";
+ info->counts[6].value_ptr = &(cinfo->counts->ospf);
+ info->counts[7].title = "GRE";
+ info->counts[7].value_ptr = &(cinfo->counts->gre);
+ info->counts[8].title = "NetBIOS";
+ info->counts[8].value_ptr = &(cinfo->counts->netbios);
+ info->counts[9].title = "IPX";
+ info->counts[9].value_ptr = &(cinfo->counts->ipx);
+ info->counts[10].title = "VINES";
+ info->counts[10].value_ptr = &(cinfo->counts->vines);
+ info->counts[11].title = "Other";
+ info->counts[11].value_ptr = &(cinfo->counts->other);
+ info->counts[12].title = "I2C Events";
+ info->counts[12].value_ptr = &(cinfo->counts->i2c_event);
+ info->counts[13].title = "I2C Data";
+ info->counts[13].value_ptr = &(cinfo->counts->i2c_data);
+
+ /*
+ * Create the dialog window, with a title that includes the interface.
+ *
+ * If we have a descriptive name for the interface, show that,
+ * rather than its raw name. On NT 5.x (2K/XP/Server2K3), the
+ * interface name is something like "\Device\NPF_{242423..."
+ * which is pretty useless to the normal user. On other platforms,
+ * it might be less cryptic, but if a more descriptive name is
+ * available, we should still use that.
+ */
+ str = g_string_new("");
+#ifdef _WIN32
+ if (capture_opts->ifaces->len < 2) {
+#else
+ if (capture_opts->ifaces->len < 4) {
+#endif
+ for (i = 0; i < capture_opts->ifaces->len; i++) {
+ interface_options interface_opts;
+
+ interface_opts = g_array_index(capture_opts->ifaces, interface_options, i);
+ descr = get_interface_descriptive_name(interface_opts.name);
+ if (i > 0) {
+ if (capture_opts->ifaces->len > 2) {
+ g_string_append_printf(str, ",");
+ }
+ g_string_append_printf(str, " ");
+ if (i == capture_opts->ifaces->len - 1) {
+ g_string_append_printf(str, "and ");
+ }
+ }
+ g_string_append_printf(str, "%s", descr);
+ g_free(descr);
+ }
+ } else {
+ g_string_append_printf(str, "%u interfaces", capture_opts->ifaces->len);
+ }
+ title_iface = g_strdup_printf("Wireshark: Capture from %s", str->str);
+ g_string_free(str, TRUE);
+ cap_w_title = create_user_window_title(title_iface);
+ g_free(title_iface);
+ info->cap_w = dlg_window_new(cap_w_title);
+ g_free(cap_w_title);
+
+ /* Container for capture display widgets */
+ main_vb = gtk_vbox_new(FALSE, 1);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(info->cap_w), main_vb);
+ gtk_widget_show(main_vb);
+
+ counts_fr = gtk_frame_new("Captured Packets");
+ gtk_box_pack_start(GTK_BOX(main_vb), counts_fr, FALSE, FALSE, 3);
+ gtk_widget_show(counts_fr);
+
+ /* Individual statistic elements */
+ counts_tb = gtk_table_new(PACKET_COUNTS_SIZE, 4, TRUE);
+ gtk_container_add(GTK_CONTAINER(counts_fr), counts_tb);
+ gtk_container_set_border_width(GTK_CONTAINER(counts_tb), 5);
+ gtk_widget_show(counts_tb);
+
+ gtk_table_set_row_spacings(GTK_TABLE(counts_tb), 0);
+ gtk_table_set_col_spacings(GTK_TABLE(counts_tb), 5);
+
+ for (i = 0; i < PACKET_COUNTS_SIZE; i++) {
+ info->counts[i].label = gtk_label_new(info->counts[i].title);
+ gtk_misc_set_alignment(GTK_MISC(info->counts[i].label), 0.0f, 0.5f);
+
+ info->counts[i].value_lb = gtk_label_new("0");
+ gtk_misc_set_alignment(GTK_MISC(info->counts[i].value_lb), 0.5f, 0.5f);
+
+ if (i == 0) {
+ /* do not build a progress bar for the "total" row */
+ /* (as this could suggest a "buffer full" to the user) */
+ /* simply put a label here */
+ info->counts[i].percent_pb = gtk_label_new("% of total");
+ } else {
+ /* build a progress bar in the other rows */
+ info->counts[i].percent_pb = gtk_progress_bar_new();
+
+ /* downsize the default size of this progress bar in x direction (def:150), */
+ /* otherwise it will become too large and the dialog will look ugly */
+ /* XXX: use a TreeView instead of a table in order to fix this */
+ gtk_widget_set_size_request(info->counts[i].percent_pb, 70, -1);
+ }
+
+ info->counts[i].percent_lb = gtk_label_new("0.0%");
+ gtk_misc_set_alignment(GTK_MISC(info->counts[i].percent_lb), 1.0f, 0.5f);
+
+ gtk_table_attach_defaults(GTK_TABLE(counts_tb),
+ info->counts[i].label, 0, 1, i, i + 1);
+ gtk_table_attach_defaults(GTK_TABLE(counts_tb),
+ info->counts[i].value_lb, 1, 2, i, i + 1);
+ gtk_table_attach_defaults(GTK_TABLE(counts_tb),
+ info->counts[i].percent_pb, 2, 3, i, i + 1);
+ gtk_table_attach_defaults(GTK_TABLE(counts_tb),
+ info->counts[i].percent_lb, 3, 4, i, i + 1);
+
+ gtk_widget_show(info->counts[i].label);
+ gtk_widget_show(info->counts[i].value_lb);
+ gtk_widget_show(info->counts[i].percent_pb);
+ /* don't show percentages for the "total" row */
+ if (i != 0) {
+ gtk_widget_show(info->counts[i].percent_lb);
+ }
+ }
+
+ /* Running time */
+ running_tb = gtk_table_new(1, 4, TRUE);
+ gtk_box_pack_start(GTK_BOX(main_vb), running_tb, FALSE, FALSE, 3);
+ gtk_widget_show(running_tb);
+
+ running_label = gtk_label_new("Running");
+ gtk_misc_set_alignment(GTK_MISC(running_label), 0.0f, 0.0f);
+ gtk_widget_show(running_label);
+ gtk_table_attach_defaults(GTK_TABLE(running_tb),
+ running_label, 0, 1, 0, 1);
+
+ info->running_time_lb = gtk_label_new("00:00:00");
+ gtk_misc_set_alignment(GTK_MISC(info->running_time_lb), 0.0f, 0.0f);
+ gtk_widget_show(info->running_time_lb);
+ gtk_table_attach(GTK_TABLE(running_tb),
+ info->running_time_lb,
+ 1, 2, 0, 1, 0, 0, 5, 0);
+
+ /* allow user to either click a stop button, or the close button on
+ the window to stop a capture in progress. */
+ bbox = dlg_button_row_new(WIRESHARK_STOCK_CAPTURE_STOP, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 3);
+ gtk_widget_show(bbox);
+
+ stop_bt = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_CAPTURE_STOP);
+ window_set_cancel_button(info->cap_w, stop_bt, NULL);
+ g_signal_connect(stop_bt, "clicked", G_CALLBACK(capture_info_stop_clicked_cb), capture_opts);
+ g_signal_connect(info->cap_w, "delete_event", G_CALLBACK(capture_info_delete_cb), capture_opts);
+
+ ci_help = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ gtk_widget_set_tooltip_text(ci_help, "Get help about this dialog");
+ g_signal_connect(ci_help, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_CAPTURE_INFO_DIALOG);
+
+ gtk_widget_show(info->cap_w);
+ window_present(info->cap_w);
+
+ info->start_time = time(NULL);
+
+ cinfo->ui = info;
+
+ /* update the dialog once a second, even if no packets rushing in */
+ info->timer_id = g_timeout_add(1000, capture_info_ui_update_cb,cinfo);
+}
+
+
+/* update the capture info dialog */
+/* As this function is a bit time critical while capturing, */
+/* prepare everything possible in the capture_info_ui_create() function above! */
+void capture_info_ui_update(
+capture_info *cinfo)
+{
+ unsigned int i;
+ gchar label_str[64];
+ capture_info_ui_t *info = cinfo->ui;
+
+ if (!info) /* ...which might happen on slow displays? */
+ return;
+
+ /* display running time */
+ g_snprintf(label_str, sizeof(label_str), "%02ld:%02ld:%02ld",
+ (long)(cinfo->running_time/3600), (long)((cinfo->running_time%3600)/60),
+ (long)(cinfo->running_time%60));
+ gtk_label_set_text(GTK_LABEL(info->running_time_lb), label_str);
+
+ /* if we have new packets, update all rows */
+ if (cinfo->new_packets) {
+ float pb_frac;
+ for (i = 0; i < PACKET_COUNTS_SIZE; i++) {
+ g_snprintf(label_str, sizeof(label_str), "%d", *info->counts[i].value_ptr);
+ gtk_label_set_text(GTK_LABEL(info->counts[i].value_lb), label_str);
+
+ pb_frac = (*info->counts[0].value_ptr != 0) ?
+ ((float)*info->counts[i].value_ptr / *info->counts[0].value_ptr) : 0.0f;
+
+ /* don't try to update the "total" row progress bar */
+ if (i != 0) {
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(info->counts[i].percent_pb), pb_frac);
+ g_snprintf(label_str, sizeof(label_str), "%.1f%%", pb_frac * 100.0);
+ gtk_label_set_text(GTK_LABEL(info->counts[i].percent_lb), label_str);
+ }
+ }
+ }
+}
+
+/* destroy the capture info dialog again */
+void capture_info_ui_destroy(
+capture_info *cinfo)
+{
+ capture_info_ui_t *info = cinfo->ui;
+
+ if (!info) /* ...which probably shouldn't happen */
+ return;
+
+ g_source_remove(info->timer_id);
+
+ /* called from capture engine, so it's ok to destroy the dialog here */
+ gtk_grab_remove(GTK_WIDGET(info->cap_w));
+ window_destroy(GTK_WIDGET(info->cap_w));
+ g_free(info);
+ cinfo->ui = NULL;
+}
+
+
+#endif /* HAVE_LIBPCAP */
diff --git a/ui/gtk/cfilter_combo_utils.c b/ui/gtk/cfilter_combo_utils.c
new file mode 100644
index 0000000000..67cfcff6bf
--- /dev/null
+++ b/ui/gtk/cfilter_combo_utils.c
@@ -0,0 +1,95 @@
+/* cfilter_combo_utils.c
+ * Capture filter combo box routines
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "ui/gtk/main.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/cfilter_combo_utils.h"
+#include "ui/gtk/recent.h"
+
+
+/* XXX: use a preference for this setting! */
+static guint cfilter_combo_max_recent = 20;
+
+static gboolean
+cfilter_combo_add(gchar *s) {
+ GList *li;
+ GList *fl = g_object_get_data(G_OBJECT(top_level), E_CFILTER_FL_KEY);
+
+ li = g_list_first(fl);
+ while (li) {
+ /* If the filter is already in the list, remove the old one and
+ * append the new one at the latest position (at g_list_append() below) */
+ if (li->data && strcmp(s, li->data) == 0) {
+ fl = g_list_remove(fl, li->data);
+ break;
+ }
+ li = li->next;
+ }
+ fl = g_list_append(fl, s);
+ g_object_set_data(G_OBJECT(top_level), E_CFILTER_FL_KEY, fl);
+ return TRUE;
+}
+
+
+/* write all non empty capture filters (until maximum count)
+ * of the combo box GList to the user's recent file */
+void
+ cfilter_combo_recent_write_all(FILE *rf) {
+ GList *cfilter_list = g_object_get_data(G_OBJECT(top_level), E_CFILTER_FL_KEY);
+ GList *li;
+ guint max_count = 0;
+
+ /* write all non empty capture filter strings to the recent file (until max count) */
+ li = g_list_first(cfilter_list);
+ while (li && (max_count++ <= cfilter_combo_max_recent) ) {
+ if (li->data && strlen(li->data)) {
+ fprintf (rf, RECENT_KEY_CAPTURE_FILTER ": %s\n", (char *)li->data);
+ }
+ li = li->next;
+ }
+}
+
+/* add a capture filter coming from the user's recent file to the cfilter combo box */
+gboolean
+ cfilter_combo_add_recent(gchar *s) {
+ gchar *dup;
+
+ if (s) {
+ dup = g_strdup(s);
+ if (!cfilter_combo_add(dup)) {
+ g_free(dup);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
diff --git a/ui/gtk/cfilter_combo_utils.h b/ui/gtk/cfilter_combo_utils.h
new file mode 100644
index 0000000000..f07f770222
--- /dev/null
+++ b/ui/gtk/cfilter_combo_utils.h
@@ -0,0 +1,39 @@
+/* cfilter_combo_utils.h
+ * Capture filter combo box routines
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __CFILTER_COMBO_UTILS_H__
+#define __CFILTER_COMBO_UTILS_H__
+
+/** @file
+ * Capture filter combo box routines
+ */
+
+extern void cfilter_combo_recent_write_all(FILE *rf);
+extern gboolean cfilter_combo_add_recent(gchar *s);
+
+#define E_CFILTER_CM_KEY "capture_filter_combo"
+#define E_CFILTER_FL_KEY "capture_filter_list"
+#define RECENT_KEY_CAPTURE_FILTER "recent.capture_filter"
+
+#endif /* __CFILTER_COMBO_UTILS_H__ */
diff --git a/ui/gtk/color_dlg.c b/ui/gtk/color_dlg.c
new file mode 100644
index 0000000000..0f3ee92558
--- /dev/null
+++ b/ui/gtk/color_dlg.c
@@ -0,0 +1,1064 @@
+/* color_dlg.c
+ * Definitions for dialog boxes for color filters
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/dfilter/dfilter.h>
+#include <epan/prefs.h>
+
+#include "../color.h"
+#include "../color_filters.h"
+#include "../file.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/main.h"
+#include "ui/gtk/color_utils.h"
+#include "ui/gtk/color_dlg.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dfilter_expr_dlg.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/capture_file_dlg.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/color_edit_dlg.h"
+#include "ui/gtk/new_packet_list.h"
+
+
+#define BUTTON_SIZE_X -1
+#define BUTTON_SIZE_Y -1
+
+
+static GtkWidget* colorize_dialog_new(char *filter);
+static void add_filter_to_list(gpointer filter_arg, gpointer list_arg, gboolean prepend);
+static void color_filter_up_cb(GtkButton *button, gpointer user_data);
+static void color_filter_down_cb(GtkButton *button, gpointer user_data);
+static void remember_selected_row(GtkTreeSelection *sel, gpointer list);
+static void color_destroy_cb(GtkButton *button, gpointer user_data);
+static void destroy_edit_dialog_cb(gpointer filter_arg, gpointer dummy);
+static void create_new_color_filter(GtkButton *button, const char *filter);
+static void color_new_cb(GtkButton *button, gpointer user_data);
+static void color_edit_cb(GtkButton *button, gpointer user_data);
+static gboolean color_filters_button_cb(GtkWidget *, GdkEventButton *, gpointer);
+static void color_disable_cb(GtkWidget *widget, gboolean user_data);
+static void color_delete_cb(GtkWidget *widget, gpointer user_data);
+static void color_save_cb(GtkButton *button, gpointer user_data);
+static void color_ok_cb(GtkButton *button, gpointer user_data);
+static void color_cancel_cb(GtkWidget *widget, gpointer user_data);
+static void color_apply_cb(GtkButton *button, gpointer user_data);
+static void color_clear_cb(GtkWidget *button, gpointer user_data);
+static void color_import_cb(GtkButton *button, gpointer user_data );
+static void color_export_cb(GtkButton *button, gpointer user_data );
+
+
+static GtkWidget *colorize_win;
+gint color_dlg_num_of_filters; /* number of filters being displayed */
+gint color_dlg_row_selected; /* row in color_filters that is selected */
+
+static gboolean row_is_moving = FALSE;
+
+/* This is a list of all current color filters in the dialog
+ * (copied from color_filters.c and edited with the dialog).
+ * The color filter items are not identical to the ones used for the
+ * packet list display, so they can be safely edited.
+ *
+ * Keep the temporary filters in a separate list so that they are
+ * not shown in the edit-dialog
+ *
+ * XXX - use the existing GTK list for this purpose and build temporary copies
+ * e.g. for the save/export functions.
+ * Problem: Don't know when able to safely throw away, e.g. while exporting.
+ */
+static GSList *color_filter_edit_list = NULL;
+static GSList *color_filter_tmp_list = NULL;
+
+
+#define COLOR_UP_LB "color_up_lb"
+#define COLOR_DOWN_LB "color_down_lb"
+#define COLOR_EDIT_LB "color_edit_lb"
+#define COLOR_ENABLE_LB "color_enable_lb"
+#define COLOR_DISABLE_LB "color_disable_lb"
+#define COLOR_DELETE_LB "color_delete_lb"
+#define COLOR_FILTERS_CL "color_filters_cl"
+#define COLOR_FILTER_LIST "color_filter_list"
+
+
+/* Callback for the "Display:Coloring Rules" menu item. */
+void
+color_display_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ if (colorize_win != NULL) {
+ /* There's already a color dialog box active; reactivate it. */
+ reactivate_window(colorize_win);
+ } else {
+ /* Create a new "Colorize Display" dialog. */
+ colorize_win = colorize_dialog_new(NULL);
+ }
+}
+
+/* this opens the color dialog and presets the filter string */
+void
+color_display_with_filter(char *filter)
+{
+ if (colorize_win != NULL) {
+ /* There's already a color dialog box active; reactivate it. */
+ reactivate_window(colorize_win);
+ } else {
+ /* Create a new "Colorize Display" dialog. */
+ colorize_win = colorize_dialog_new(filter);
+ }
+}
+
+/* if this filter is selected - count it in the given int* */
+static void
+count_this_select(gpointer filter_arg, gpointer counter_arg)
+{
+ color_filter_t *colorf = filter_arg;
+ int * cnt = counter_arg;
+
+ if (colorf->selected)
+ (*cnt)++;
+}
+
+/* TODO: implement count of selected filters. Plug in to file_dlg update of "export selected" checkbox. */
+int color_selected_count(void)
+{
+ int count = 0;
+
+ g_slist_foreach(color_filter_edit_list, count_this_select, &count);
+
+ return count;
+}
+
+/* Create the "Coloring Rules" dialog. */
+static GtkWidget*
+colorize_dialog_new (char *filter)
+{
+ GtkWidget *color_win;
+ GtkWidget *dlg_vbox;
+ GtkWidget *main_hbox;
+ GtkWidget *ctrl_vbox;
+ GtkWidget *order_fr;
+ GtkWidget *order_vbox;
+ GtkWidget *color_filter_up;
+ GtkWidget *order_move_label;
+ GtkWidget *color_filter_down;
+
+ GtkWidget *list_fr;
+ GtkWidget *list_vbox;
+ GtkWidget *scrolledwindow1;
+ GtkWidget *color_filters;
+ GtkWidget *list_label;
+
+ GtkWidget *edit_fr;
+ GtkWidget *edit_vbox;
+ GtkWidget *color_new;
+ GtkWidget *color_edit;
+ GtkWidget *color_enable;
+ GtkWidget *color_disable;
+ GtkWidget *color_delete;
+
+ GtkWidget *manage_fr;
+ GtkWidget *manage_vbox;
+ GtkWidget *color_import;
+ GtkWidget *color_export;
+ GtkWidget *color_clear;
+
+ GtkWidget *button_ok_hbox;
+ GtkWidget *color_ok;
+ GtkWidget *color_apply;
+ GtkWidget *color_save;
+ GtkWidget *color_cancel;
+ GtkWidget *color_help;
+
+ GtkListStore *store;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *selection;
+ const gchar *titles[] = { "Name", "String" };
+
+
+
+ color_dlg_num_of_filters = 0;
+ color_dlg_row_selected = -1; /* no row selected */
+ /* Resizing of the dialog window is now reasonably done.
+ * Default size is set so that it should fit into every usual screen resolution.
+ * All other widgets are always packed depending on the current window size. */
+ color_win = dlg_conf_window_new ("Wireshark: Coloring Rules");
+ g_object_set_data(G_OBJECT(color_win), "color_win", color_win);
+ gtk_window_set_default_size(GTK_WINDOW(color_win), DEF_WIDTH, DEF_HEIGHT * 2/3);
+ dlg_vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (dlg_vbox), 5);
+ gtk_container_add (GTK_CONTAINER (color_win), dlg_vbox);
+
+ main_hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (dlg_vbox), main_hbox, TRUE, TRUE, 0);
+
+ ctrl_vbox = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (main_hbox), ctrl_vbox, FALSE, FALSE, 0);
+
+ /* edit buttons frame */
+ edit_fr = gtk_frame_new("Edit");
+ gtk_box_pack_start (GTK_BOX (ctrl_vbox), edit_fr, TRUE, TRUE, 0);
+
+ /* edit_vbox is first button column (containing: new, edit and such) */
+ edit_vbox = gtk_vbox_new(TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (edit_vbox), 5);
+ gtk_container_add(GTK_CONTAINER(edit_fr), edit_vbox);
+
+ color_new = gtk_button_new_from_stock(GTK_STOCK_NEW);
+ gtk_box_pack_start (GTK_BOX (edit_vbox), color_new, FALSE, FALSE, 5);
+ gtk_widget_set_tooltip_text(color_new, "Create a new filter at the top of the list");
+
+ color_edit = gtk_button_new_from_stock(WIRESHARK_STOCK_EDIT);
+ gtk_box_pack_start (GTK_BOX (edit_vbox), color_edit, FALSE, FALSE, 5);
+ gtk_widget_set_tooltip_text(color_edit, " If more than one filter is selected, edit the first selected one");
+ gtk_widget_set_sensitive (color_edit, FALSE);
+
+ color_enable = gtk_button_new_from_stock(WIRESHARK_STOCK_ENABLE);
+ gtk_box_pack_start (GTK_BOX (edit_vbox), color_enable, FALSE, FALSE, 5);
+ gtk_widget_set_tooltip_text(color_enable, "Enable the selected filter(s)");
+ gtk_widget_set_sensitive (color_enable, FALSE);
+
+ color_disable = gtk_button_new_from_stock(WIRESHARK_STOCK_DISABLE);
+ gtk_box_pack_start (GTK_BOX (edit_vbox), color_disable, FALSE, FALSE, 5);
+ gtk_widget_set_tooltip_text(color_disable, "Disable the selected filter(s)");
+ gtk_widget_set_sensitive (color_disable, FALSE);
+
+ color_delete = gtk_button_new_from_stock(GTK_STOCK_DELETE);
+ gtk_box_pack_start (GTK_BOX (edit_vbox), color_delete, FALSE, FALSE, 5);
+ gtk_widget_set_tooltip_text(color_delete, "Delete the selected filter(s)");
+ gtk_widget_set_sensitive (color_delete, FALSE);
+ /* End edit buttons frame */
+
+
+ /* manage buttons frame */
+ manage_fr = gtk_frame_new("Manage");
+ gtk_box_pack_start (GTK_BOX (ctrl_vbox), manage_fr, TRUE, TRUE, 0);
+
+ manage_vbox = gtk_vbox_new (TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (manage_vbox), 5);
+ gtk_container_add(GTK_CONTAINER(manage_fr), manage_vbox);
+
+ color_import = gtk_button_new_from_stock(WIRESHARK_STOCK_IMPORT);
+ gtk_box_pack_start (GTK_BOX (manage_vbox), color_import, FALSE, FALSE, 5);
+ gtk_widget_set_tooltip_text(color_import, "Load filters from a file and append them to the list");
+ color_export = gtk_button_new_from_stock(WIRESHARK_STOCK_EXPORT);
+ gtk_box_pack_start (GTK_BOX (manage_vbox), color_export, FALSE, FALSE, 5);
+ gtk_widget_set_tooltip_text(color_export, "Save all/selected filters to a file");
+ color_clear = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
+ gtk_box_pack_start(GTK_BOX (manage_vbox), color_clear, FALSE, FALSE, 5);
+ gtk_widget_set_tooltip_text(color_clear, "Clear the filter list and revert to system-wide default filter set");
+
+ /* filter list frame */
+ list_fr = gtk_frame_new("Filter");
+ gtk_box_pack_start (GTK_BOX (main_hbox), list_fr, TRUE, TRUE, 0);
+
+ list_vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (list_vbox), 5);
+ gtk_container_add(GTK_CONTAINER(list_fr), list_vbox);
+
+ list_label = gtk_label_new (("List is processed in order until match is found"));
+ gtk_box_pack_start (GTK_BOX (list_vbox), list_label, FALSE, FALSE, 0);
+
+ /* create the list of filters */
+ scrolledwindow1 = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwindow1),
+ GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (list_vbox), scrolledwindow1, TRUE, TRUE, 0);
+
+ /* the list store contains : filter name, filter string, foreground
+ * color, background color, pointer to color filter */
+ store = gtk_list_store_new(6,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_BOOLEAN,
+ G_TYPE_POINTER);
+ color_filters = tree_view_new(GTK_TREE_MODEL(store));
+ g_object_unref(store);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(titles[0], renderer,
+ "text", 0,
+ "foreground", 2,
+ "background", 3,
+ "strikethrough", 4,
+ NULL);
+ gtk_tree_view_column_set_fixed_width(column, 80);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(color_filters), column);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(titles[1], renderer,
+ "text", 1,
+ "foreground", 2,
+ "background", 3,
+ "strikethrough", 4,
+ NULL);
+ gtk_tree_view_column_set_fixed_width(column, 300);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(color_filters), column);
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(color_filters), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(color_filters), FALSE);
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(color_filters));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
+
+ gtk_container_add (GTK_CONTAINER (scrolledwindow1), color_filters);
+
+
+ /* order frame */
+ order_fr = gtk_frame_new("Order");
+ gtk_box_pack_start (GTK_BOX (main_hbox), order_fr, FALSE, FALSE, 0);
+
+ order_vbox = gtk_vbox_new (TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (order_vbox), 5);
+ gtk_container_add(GTK_CONTAINER(order_fr), order_vbox);
+
+ color_filter_up = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
+ gtk_box_pack_start (GTK_BOX (order_vbox), color_filter_up, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text(color_filter_up, "Move filter higher in list");
+ gtk_widget_set_sensitive (color_filter_up, FALSE);
+
+ order_move_label = gtk_label_new (("Move\nselected filter\nup or down"));
+ gtk_box_pack_start (GTK_BOX (order_vbox), order_move_label, FALSE, FALSE, 0);
+
+ color_filter_down = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
+ gtk_box_pack_start (GTK_BOX (order_vbox), color_filter_down, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text(color_filter_down, "Move filter lower in list");
+ gtk_widget_set_sensitive (color_filter_down, FALSE);
+
+
+ /* Button row: OK, cancel and help buttons */
+ button_ok_hbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_SAVE, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_start (GTK_BOX (dlg_vbox), button_ok_hbox, FALSE, FALSE, 5);
+
+ color_ok = g_object_get_data(G_OBJECT(button_ok_hbox), GTK_STOCK_OK);
+ gtk_widget_set_tooltip_text(color_ok, "Apply the color filters to the display and close this dialog");
+ color_apply = g_object_get_data(G_OBJECT(button_ok_hbox), GTK_STOCK_APPLY);
+ gtk_widget_set_tooltip_text(color_apply, "Apply the color filters to the display and keep this dialog open");
+
+ color_save = g_object_get_data(G_OBJECT(button_ok_hbox), GTK_STOCK_SAVE);
+ gtk_widget_set_tooltip_text(color_save, "Save the color filters permanently and keep this dialog open");
+ color_cancel = g_object_get_data(G_OBJECT(button_ok_hbox), GTK_STOCK_CANCEL);
+ window_set_cancel_button(color_win, color_cancel, color_cancel_cb);
+ gtk_widget_set_tooltip_text(color_cancel, "Cancel changes done (since last \"Apply\") and close this dialog");
+
+ color_help = g_object_get_data(G_OBJECT(button_ok_hbox), GTK_STOCK_HELP);
+ gtk_widget_set_tooltip_text(color_help, "Get help about this dialog");
+ g_signal_connect(color_help, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_COLORING_RULES_DIALOG);
+
+ gtk_widget_grab_default(color_ok);
+
+ /* signals and such */
+ g_signal_connect(color_win, "destroy", G_CALLBACK(color_destroy_cb), NULL);
+ g_object_set_data(G_OBJECT(color_filter_up), COLOR_FILTERS_CL, color_filters);
+ g_signal_connect(color_filter_up, "clicked", G_CALLBACK(color_filter_up_cb), NULL);
+ g_object_set_data(G_OBJECT(color_filter_down), COLOR_FILTERS_CL, color_filters);
+ g_signal_connect(color_filter_down, "clicked", G_CALLBACK(color_filter_down_cb), NULL);
+ g_signal_connect(selection, "changed", G_CALLBACK(remember_selected_row), color_filters);
+ g_signal_connect(color_filters, "button_press_event", G_CALLBACK(color_filters_button_cb), NULL);
+ g_object_set_data(G_OBJECT(color_filters), COLOR_UP_LB, color_filter_up);
+ g_object_set_data(G_OBJECT(color_filters), COLOR_DOWN_LB, color_filter_down);
+ g_object_set_data(G_OBJECT(color_filters), COLOR_EDIT_LB, color_edit);
+ g_object_set_data(G_OBJECT(color_filters), COLOR_ENABLE_LB, color_enable);
+ g_object_set_data(G_OBJECT(color_filters), COLOR_DISABLE_LB, color_disable);
+ g_object_set_data(G_OBJECT(color_filters), COLOR_DELETE_LB, color_delete);
+ g_object_set_data(G_OBJECT(color_new), COLOR_FILTERS_CL, color_filters);
+ g_signal_connect(color_new, "clicked", G_CALLBACK(color_new_cb), NULL);
+ g_object_set_data(G_OBJECT(color_edit), COLOR_FILTERS_CL, color_filters);
+ g_signal_connect(color_edit, "clicked", G_CALLBACK(color_edit_cb), NULL);
+ g_object_set_data(G_OBJECT(color_enable), COLOR_FILTERS_CL, color_filters);
+ g_signal_connect(color_enable, "clicked", G_CALLBACK(color_disable_cb), FALSE);
+ g_object_set_data(G_OBJECT(color_disable), COLOR_FILTERS_CL, color_filters);
+ g_signal_connect(color_disable, "clicked", G_CALLBACK(color_disable_cb), (gpointer)TRUE);
+ g_object_set_data(G_OBJECT(color_delete), COLOR_EDIT_LB, color_edit);
+ g_object_set_data(G_OBJECT(color_delete), COLOR_FILTERS_CL, color_filters);
+ g_signal_connect(color_delete, "clicked", G_CALLBACK(color_delete_cb), NULL);
+ g_object_set_data(G_OBJECT(color_import), COLOR_FILTERS_CL, color_filters);
+ g_signal_connect(color_import, "clicked", G_CALLBACK(color_import_cb), NULL);
+ g_signal_connect(color_export, "clicked", G_CALLBACK(color_export_cb), NULL);
+ g_object_set_data(G_OBJECT(color_clear), COLOR_FILTERS_CL, color_filters);
+ g_signal_connect(color_clear, "clicked", G_CALLBACK(color_clear_cb), NULL);
+ g_signal_connect(color_ok, "clicked", G_CALLBACK(color_ok_cb), NULL);
+ g_signal_connect(color_apply, "clicked", G_CALLBACK(color_apply_cb), NULL);
+ g_signal_connect(color_save, "clicked", G_CALLBACK(color_save_cb), NULL);
+
+ g_signal_connect(color_win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+
+ gtk_widget_grab_focus(color_filters);
+
+ /* prepare filter list content */
+ color_filters_clone(color_filters);
+ g_object_set_data(G_OBJECT(color_win), COLOR_FILTER_LIST, &color_filter_edit_list);
+
+ gtk_widget_show_all(color_win);
+
+ /* hide the Save button if the user uses implicit save */
+ if(!prefs.gui_use_pref_save) {
+ gtk_widget_hide(color_save);
+ }
+
+ window_present(color_win);
+
+ if(filter){
+ /* if we specified a preset filter string, open the new dialog and
+ set the filter */
+ create_new_color_filter(GTK_BUTTON(color_new), filter);
+ }
+
+ return color_win;
+}
+
+/* move a row in the list +/- one position up/down */
+static void move_this_row (GtkWidget *color_filters,
+ gint filter_number,
+ gint amount) /* only tested with +1(down) and -1(up) */
+{
+ color_filter_t *colorf;
+ GtkTreeModel *model;
+ GtkTreeIter iter1, iter2;
+ gchar *name, *string, *fg_str, *bg_str;
+ gboolean disabled;
+
+ g_assert(amount == +1 || amount == -1);
+ g_assert(amount == +1 || filter_number > 0);
+ g_assert(amount == -1 || filter_number < color_dlg_num_of_filters - 1);
+
+ row_is_moving = TRUE;
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(color_filters));
+ gtk_tree_model_iter_nth_child(model, &iter1, NULL, filter_number);
+ gtk_tree_model_iter_nth_child(model, &iter2, NULL, filter_number + amount);
+
+ gtk_tree_model_get(model, &iter1, 0, &name, 1, &string,
+ 2, &fg_str, 3, &bg_str, 4, &disabled, 5, &colorf, -1);
+ gtk_list_store_remove(GTK_LIST_STORE(model), &iter1);
+ if (amount < 0)
+ gtk_list_store_insert_before(GTK_LIST_STORE(model), &iter1, &iter2);
+ else
+ gtk_list_store_insert_after(GTK_LIST_STORE(model), &iter1, &iter2);
+
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter1,
+ 0, name,
+ 1, string,
+ 2, fg_str,
+ 3, bg_str,
+ 4, disabled,
+ 5, colorf, -1);
+
+ g_free(name);
+ g_free(string);
+ g_free(fg_str);
+ g_free(bg_str);
+ row_is_moving = FALSE;
+
+ /*
+ * re-select the initial row
+ */
+ gtk_widget_grab_focus(color_filters);
+ gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(color_filters)), &iter1);
+
+ color_filter_edit_list = g_slist_remove(color_filter_edit_list, colorf);
+ color_filter_edit_list = g_slist_insert(color_filter_edit_list, colorf, filter_number + amount);
+}
+
+/* User pressed the "Up" button: Move the selected filters up in the list */
+static void
+color_filter_up_cb(GtkButton *button, gpointer user_data _U_)
+{
+ gint amount;
+ gint filter_number;
+ GtkWidget * color_filters;
+ color_filter_t *colorf;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreeSelection *sel;
+
+ amount = -1;
+ color_filters = (GtkWidget *)g_object_get_data(G_OBJECT(button), COLOR_FILTERS_CL);
+
+ for (filter_number = 0; filter_number < color_dlg_num_of_filters; filter_number++)
+ {
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(color_filters));
+ gtk_tree_model_iter_nth_child(model, &iter, NULL, filter_number);
+ gtk_tree_model_get(model, &iter, 5, &colorf, -1);
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(color_filters));
+ if (gtk_tree_selection_iter_is_selected(sel, &iter))
+ move_this_row (color_filters, filter_number, amount);
+ }
+}
+
+/* User pressed the "Down" button: Move the selected filters down in the list */
+static void
+color_filter_down_cb(GtkButton *button, gpointer user_data _U_)
+{
+ gint amount;
+ gint filter_number;
+ GtkWidget * color_filters;
+ color_filter_t *colorf;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ amount = +1;
+ color_filters = (GtkWidget *)g_object_get_data(G_OBJECT(button), COLOR_FILTERS_CL);
+
+ for (filter_number = color_dlg_num_of_filters - 1; filter_number >= 0; filter_number--)
+ {
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(color_filters));
+ gtk_tree_model_iter_nth_child(model, &iter, NULL, filter_number);
+ gtk_tree_model_get(model, &iter, 5, &colorf, -1);
+ if (colorf->selected)
+ move_this_row (color_filters, filter_number, amount);
+ }
+}
+
+
+struct remember_data
+{
+ gint count; /* count of selected filters */
+ gboolean first_selected; /* true if the first filter in the list is selected */
+ gboolean last_selected; /* true if the last filter in the list is selected */
+ gboolean all_enabled; /* true if all selected coloring rules are enabled */
+ gboolean all_disabled; /* true if all selected coloring rules are disabled */
+ gpointer color_filters;
+};
+/* called for each selected row in the tree.
+ */
+static void remember_this_row (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer arg)
+{
+ gint *path_index;
+ color_filter_t *colorf;
+ struct remember_data *data = arg;
+
+ gtk_tree_model_get(model, iter, 5, &colorf, -1);
+ colorf->selected = TRUE;
+
+ data->all_enabled &= (!colorf->disabled);
+ data->all_disabled &= colorf->disabled;
+
+ path_index = gtk_tree_path_get_indices(path); /* not to be freed */
+ if (path_index == NULL) /* can return NULL according to API doc.*/
+ {
+ return;
+ }
+ color_dlg_row_selected = path_index[0];
+
+ if (color_dlg_row_selected == 0)
+ data->first_selected = TRUE;
+ if (color_dlg_row_selected == color_dlg_num_of_filters - 1)
+ data->last_selected = TRUE;
+
+ data->count++;
+
+ gtk_tree_view_scroll_to_cell(data->color_filters, path, NULL, FALSE, 0.0f, 0.0f);
+}
+
+/* clear the selection flag of this filter */
+static void
+clear_select_flag(gpointer filter_arg, gpointer arg _U_)
+{
+ color_filter_t *colorf = filter_arg;
+
+ colorf->selected = FALSE;
+}
+
+/* The gtk+2.0 version gets called for, (maybe multiple,) changes in the selection. */
+static void
+remember_selected_row(GtkTreeSelection *sel, gpointer color_filters)
+{
+ GtkWidget *button;
+ struct remember_data data;
+
+ data.first_selected = data.last_selected = FALSE;
+ data.all_enabled = data.all_disabled = TRUE;
+ data.count = 0;
+ data.color_filters = color_filters;
+
+ g_slist_foreach(color_filter_edit_list, clear_select_flag, NULL);
+ gtk_tree_selection_selected_foreach(sel,remember_this_row, &data);
+
+ if (data.count > 0)
+ {
+ /*
+ * One or more rows are selected, so we can operate on them.
+ */
+
+ /* We can only edit if there is exactly one filter selected */
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(color_filters), COLOR_EDIT_LB);
+ gtk_widget_set_sensitive (button, data.count == 1);
+
+ /* We can enable any number of filters */
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(color_filters), COLOR_ENABLE_LB);
+ gtk_widget_set_sensitive (button, !data.all_enabled);
+
+ /* We can disable any number of filters */
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(color_filters), COLOR_DISABLE_LB);
+ gtk_widget_set_sensitive (button, !data.all_disabled);
+
+ /* We can delete any number of filters */
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(color_filters), COLOR_DELETE_LB);
+ gtk_widget_set_sensitive (button, TRUE);
+
+ /*
+ * We can move them up *if* one of them isn't the top row,
+ * and move them down *if* one of them isn't the bottom row.
+ */
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(color_filters), COLOR_UP_LB);
+ gtk_widget_set_sensitive(button, !data.first_selected);
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(color_filters), COLOR_DOWN_LB);
+ gtk_widget_set_sensitive(button, !data.last_selected);
+ }
+ else
+ {
+ color_dlg_row_selected = -1;
+
+ /*
+ * No row is selected, so we can't do operations that affect the
+ * selected row.
+ */
+ if (!row_is_moving) {
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(color_filters), COLOR_UP_LB);
+ gtk_widget_set_sensitive (button, FALSE);
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(color_filters), COLOR_DOWN_LB);
+ gtk_widget_set_sensitive (button, FALSE);
+ }
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(color_filters), COLOR_EDIT_LB);
+ gtk_widget_set_sensitive (button, FALSE);
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(color_filters), COLOR_ENABLE_LB);
+ gtk_widget_set_sensitive (button, FALSE);
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(color_filters), COLOR_DISABLE_LB);
+ gtk_widget_set_sensitive (button, FALSE);
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(color_filters), COLOR_DELETE_LB);
+ gtk_widget_set_sensitive (button, FALSE);
+ }
+}
+
+
+
+/* destroy a single color edit dialog */
+static void
+destroy_edit_dialog_cb(gpointer filter_arg, gpointer dummy _U_)
+{
+ color_filter_t *colorf = (color_filter_t *)filter_arg;
+
+ if (colorf->edit_dialog != NULL)
+ window_destroy(colorf->edit_dialog);
+}
+
+/* Called when the dialog box is being destroyed; destroy any edit
+ * dialogs opened from this dialog.
+ */
+static void
+color_destroy_cb (GtkButton *button _U_,
+ gpointer user_data _U_)
+{
+ /* Destroy any edit dialogs we have open. */
+ g_slist_foreach(color_filter_edit_list, destroy_edit_dialog_cb, NULL);
+
+ /* destroy the filter list itself */
+ color_filter_list_delete(&color_filter_edit_list);
+ color_filter_list_delete(&color_filter_tmp_list);
+
+ colorize_win = NULL;
+}
+
+
+static void
+select_row(GtkWidget *color_filters, int row)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeSelection *sel;
+
+ /* select the new row */
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(color_filters));
+ gtk_tree_model_iter_nth_child(model, &iter, NULL, row);
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(color_filters));
+ gtk_tree_selection_select_iter(sel, &iter);
+}
+
+
+/* add a single color filter to the list */
+static void
+add_filter_to_list(gpointer filter_arg, gpointer list_arg, gboolean prepend)
+{
+ color_filter_t *colorf = filter_arg;
+ gchar fg_str[14], bg_str[14];
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ if( strstr(colorf->filter_name,CONVERSATION_COLOR_PREFIX)==NULL) {
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list_arg)));
+ if (prepend) {
+ gtk_list_store_prepend(store, &iter);
+ } else {
+ gtk_list_store_append(store, &iter);
+ }
+ g_snprintf(fg_str, sizeof(fg_str), "#%04X%04X%04X",
+ colorf->fg_color.red, colorf->fg_color.green, colorf->fg_color.blue);
+ g_snprintf(bg_str, sizeof(bg_str), "#%04X%04X%04X",
+ colorf->bg_color.red, colorf->bg_color.green, colorf->bg_color.blue);
+ gtk_list_store_set(store, &iter,
+ 0, colorf->filter_name,
+ 1, colorf->filter_text,
+ 2, fg_str,
+ 3, bg_str,
+ 4, colorf->disabled,
+ 5, colorf, -1);
+ if (prepend) {
+ color_filter_edit_list = g_slist_prepend(color_filter_edit_list, colorf);
+ } else {
+ color_filter_edit_list = g_slist_append(color_filter_edit_list, colorf);
+ }
+ color_dlg_num_of_filters++;
+ } else {
+ /* But keep the temporary ones too, so they can be added again
+ * when the user is done editing */
+ color_filter_tmp_list = g_slist_append(color_filter_tmp_list, colorf);
+ }
+}
+
+
+/* a new color filter was read in from a filter file */
+void
+color_filter_add_cb(color_filter_t *colorf, gpointer user_data)
+{
+ GtkWidget *color_filters = user_data;
+
+ add_filter_to_list(colorf, color_filters, FALSE);
+
+ gtk_widget_grab_focus(color_filters);
+}
+
+/* Create a new filter, add it to the list, and pop up an
+ "Edit color filter" dialog box to edit it. */
+static void
+create_new_color_filter(GtkButton *button, const char *filter)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context;
+ GdkRGBA *rgba_bg_color;
+ GdkRGBA *rgba_fg_color;
+#else
+ GtkStyle *style;
+#endif
+ color_filter_t *colorf;
+ color_t bg_color, fg_color;
+ GtkWidget *color_filters;
+ GtkTreeSelection *sel;
+
+ color_filters = (GtkWidget *)g_object_get_data(G_OBJECT(button), COLOR_FILTERS_CL);
+
+ /* unselect all filters */
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(color_filters));
+ gtk_tree_selection_unselect_all (sel);
+
+ /* Use the default background and foreground colors as the colors. */
+#if GTK_CHECK_VERSION(3,0,0)
+ context = gtk_widget_get_style_context (new_packet_list_get_widget());
+ gtk_style_context_get (context, GTK_STATE_NORMAL,
+ "background-color", &rgba_bg_color,
+ NULL);
+ gtk_style_context_get (context, GTK_STATE_NORMAL,
+ "color", &rgba_fg_color,
+ NULL);
+ gdkRGBAcolor_to_color_t(&bg_color, rgba_bg_color);
+ gdkRGBAcolor_to_color_t(&fg_color, rgba_fg_color);
+ gdk_rgba_free (rgba_bg_color);
+ gdk_rgba_free (rgba_fg_color);
+
+#else
+ style = gtk_widget_get_style(new_packet_list_get_widget());
+ gdkcolor_to_color_t(&bg_color, &style->base[GTK_STATE_NORMAL]);
+ gdkcolor_to_color_t(&fg_color, &style->text[GTK_STATE_NORMAL]);
+#endif
+ colorf = color_filter_new("name", filter, &bg_color, &fg_color, FALSE);
+ add_filter_to_list(colorf, color_filters, TRUE);
+ select_row(color_filters, 0);
+
+ /* open the edit dialog */
+ edit_color_filter_dialog(color_filters, TRUE /* is a new filter */);
+
+ gtk_widget_grab_focus(color_filters);
+}
+
+/* User pressed the "New" button: Create a new filter in the list,
+ and pop up an "Edit color filter" dialog box to edit it. */
+static void
+color_new_cb(GtkButton *button, gpointer user_data _U_)
+{
+ create_new_color_filter(button, "filter");
+}
+
+/* User pressed the "Edit" button: Pop up an "Edit color filter" dialog box
+ * to edit an existing filter. */
+static void
+color_edit_cb(GtkButton *button, gpointer user_data _U_)
+{
+ GtkWidget *color_filters;
+
+ color_filters = (GtkWidget *)g_object_get_data(G_OBJECT(button), COLOR_FILTERS_CL);
+ g_assert(color_dlg_row_selected != -1);
+ edit_color_filter_dialog(color_filters, FALSE /* is not a new filter */);
+}
+
+/* User double-clicked on the coloring rule */
+static gboolean
+color_filters_button_cb(GtkWidget *list, GdkEventButton *event,
+ gpointer data _U_)
+{
+ if (event->type == GDK_2BUTTON_PRESS) {
+ edit_color_filter_dialog(list, FALSE);
+ }
+
+ return FALSE;
+}
+
+/* action_disable==TRUE ==> User pressed the "Disable" button:
+ * Disable the selected filters in the list.
+ * action_disable==FALSE ==> User pressed the "Enable" button:
+ * Enable the selected filters in the list.
+ */
+static void
+color_disable_cb(GtkWidget *widget, gboolean action_disable)
+{
+ gint filter_number;
+ GtkWidget *button;
+ GtkWidget * color_filters;
+ color_filter_t *colorf;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreeSelection *sel;
+
+ color_filters = (GtkWidget *)g_object_get_data(G_OBJECT(widget), COLOR_FILTERS_CL);
+
+ for (filter_number = 0; filter_number < color_dlg_num_of_filters; filter_number++)
+ {
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(color_filters));
+ gtk_tree_model_iter_nth_child(model, &iter, NULL, filter_number);
+ gtk_tree_model_get(model, &iter, 5, &colorf, -1);
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(color_filters));
+ if (gtk_tree_selection_iter_is_selected(sel, &iter)) {
+ colorf->disabled = action_disable;
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter,
+ 4, action_disable, -1);
+ }
+ }
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(color_filters), COLOR_ENABLE_LB);
+ gtk_widget_set_sensitive(button, action_disable);
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(color_filters), COLOR_DISABLE_LB);
+ gtk_widget_set_sensitive(button, !action_disable);
+}
+
+/* Delete a single color filter from the list and elsewhere. */
+void
+color_delete_single(gint row, GtkWidget *color_filters)
+{
+ color_filter_t *colorf;
+
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(color_filters));
+ gtk_tree_model_iter_nth_child(model, &iter, NULL, row);
+ gtk_tree_model_get(model, &iter, 5, &colorf, -1);
+
+ /* Remove this color filter from the CList displaying the
+ color filters. */
+ gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
+ color_dlg_num_of_filters--;
+
+ /* Destroy any "Edit color filter" dialog boxes editing it. */
+ if (colorf->edit_dialog != NULL)
+ window_destroy(colorf->edit_dialog);
+
+ /* Delete the color filter from the list of color filters. */
+ color_filter_edit_list = g_slist_remove(color_filter_edit_list, colorf);
+ color_filter_delete(colorf);
+
+ /* If we grab the focus after updating the selection, the first
+ * row is always selected, so we do it before */
+ gtk_widget_grab_focus(color_filters);
+}
+
+/* User pressed the "Delete" button: Delete the selected filters from the list.*/
+static void
+color_delete_cb(GtkWidget *widget, gpointer user_data _U_)
+{
+ GtkWidget *color_filters;
+ gint row, num_filters;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeSelection *sel;
+
+ color_filters = (GtkWidget *)g_object_get_data(G_OBJECT(widget), COLOR_FILTERS_CL);
+
+ /* get the number of filters in the list */
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(color_filters));
+ num_filters = gtk_tree_model_iter_n_children(model, NULL);
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(color_filters));
+
+ /* iterate through the list and delete the selected ones */
+ for (row = num_filters - 1; row >= 0; row--)
+ {
+ gtk_tree_model_iter_nth_child(model, &iter, NULL, row);
+ if (gtk_tree_selection_iter_is_selected(sel, &iter))
+ color_delete_single (row, color_filters);
+ }
+}
+
+/* User pressed "Import": Pop up an "Import color filter" dialog box. */
+static void
+color_import_cb(GtkButton *button, gpointer data _U_)
+{
+ GtkWidget *color_filters;
+ GtkTreeSelection *sel;
+
+ color_filters = (GtkWidget *)g_object_get_data(G_OBJECT(button), COLOR_FILTERS_CL);
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(color_filters));
+ gtk_tree_selection_unselect_all (sel);
+
+ file_color_import_cmd_cb(color_filters, &color_filter_edit_list);
+}
+
+/* User pressed "Export": Pop up an "Export color filter" dialog box. */
+static void
+color_export_cb(GtkButton *button, gpointer data _U_)
+{
+ GtkWidget *color_filters;
+
+ color_filters = (GtkWidget *)g_object_get_data(G_OBJECT(button), COLOR_FILTERS_CL);
+
+ file_color_export_cmd_cb(color_filters, color_filter_edit_list);
+}
+
+/* User confirmed the clear operation: Remove all user defined color filters and
+ revert to the global file. */
+static void
+color_clear_cmd(GtkWidget *widget)
+{
+ GtkWidget * color_filters;
+
+ color_filters = (GtkWidget *)g_object_get_data(G_OBJECT(widget), COLOR_FILTERS_CL);
+
+ while (color_dlg_num_of_filters > 0)
+ {
+ color_delete_single (color_dlg_num_of_filters-1, color_filters);
+ }
+
+ /* try to read the global filters */
+ color_filters_read_globals(color_filters);
+}
+
+/* Clear button: user responded to question */
+static void color_clear_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
+{
+ switch(btn) {
+ case(ESD_BTN_CLEAR):
+ color_clear_cmd(data);
+ break;
+ case(ESD_BTN_CANCEL):
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+/* User pressed "clear" button: ask user before really doing it */
+void
+color_clear_cb(GtkWidget *widget, gpointer data _U_) {
+ gpointer dialog;
+
+ /* ask user, if he/she is really sure */
+ dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTN_CLEAR | ESD_BTN_CANCEL,
+ "%sRemove all your personal color settings?%s\n\n"
+ "This will revert the color settings to global defaults.\n\n"
+ "Are you really sure?",
+ simple_dialog_primary_start(), simple_dialog_primary_end());
+
+ simple_dialog_set_cb(dialog, color_clear_answered_cb, widget);
+}
+
+
+
+/* User pressed "Ok" button: Exit dialog and apply new list of
+ color filters to the capture. */
+static void
+color_ok_cb(GtkButton *button _U_, gpointer user_data _U_)
+{
+ /* Apply the new coloring rules... */
+ color_apply_cb(button,user_data);
+
+ /* ... and destroy the dialog box. */
+ window_destroy(colorize_win);
+}
+
+/* User pressed "Apply" button: apply the new list of color filters
+ to the capture. */
+static void
+color_apply_cb(GtkButton *button _U_, gpointer user_data _U_)
+{
+ /* if we don't have a Save button, just save the settings now */
+ if (!prefs.gui_use_pref_save) {
+ if (!color_filters_write(color_filter_edit_list))
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Could not open filter file: %s", g_strerror(errno));
+ }
+
+ /* Apply the coloring rules, both the temporary ones in
+ * color_filter_tmp_list as the permanent ones in color_filter_edit_list
+ * */
+ color_filters_apply(color_filter_tmp_list, color_filter_edit_list);
+
+ /* colorize list */
+ new_packet_list_colorize_packets();
+}
+
+/* User pressed the "Save" button: save the color filters to the
+ color filter file. */
+static void
+color_save_cb(GtkButton *button _U_, gpointer user_data _U_)
+{
+
+ if (!color_filters_write(color_filter_edit_list))
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Could not open filter file: %s", g_strerror(errno));
+}
+
+/* User pressed "Cancel" button (or "ESC" or the 'X'):
+ Exit dialog without colorizing packets with the new list. */
+static void
+color_cancel_cb(GtkWidget *widget _U_, gpointer user_data _U_)
+{
+ /* Destroy the dialog box. */
+ window_destroy(colorize_win);
+}
diff --git a/ui/gtk/color_dlg.h b/ui/gtk/color_dlg.h
new file mode 100644
index 0000000000..4d98157ff0
--- /dev/null
+++ b/ui/gtk/color_dlg.h
@@ -0,0 +1,55 @@
+/* color_dlg.h
+ * Definitions for dialog boxes for color filters
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __COLOR_DLG_H__
+#define __COLOR_DLG_H__
+
+/** @file
+ * "Colorize Display" dialog box.
+ * @ingroup dialog_group
+ */
+
+extern gint color_dlg_num_of_filters; /* number of filters being displayed */
+extern gint color_dlg_row_selected; /* row in color_filters that is selected */
+
+/** User requested the "Colorize Display" dialog box by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+void color_display_cb(GtkWidget *widget, gpointer data);
+
+/** Open the colorize dialogue and presets the filter string.
+ *
+ * @param filter the preset filter string
+ */
+void color_display_with_filter(char *filter);
+
+/** Count the number of selected color filters.
+ *
+ * @return the number of selected color filters
+ */
+int color_selected_count(void);
+
+#endif /* color_dlg.h */
diff --git a/ui/gtk/color_edit_dlg.c b/ui/gtk/color_edit_dlg.c
new file mode 100644
index 0000000000..77cb2fa23e
--- /dev/null
+++ b/ui/gtk/color_edit_dlg.c
@@ -0,0 +1,636 @@
+/* color_edit_dlg.c
+ * Definitions for single color filter edit dialog boxes
+ *
+ * $Id$
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+
+#include "../color.h"
+#include "../color_filters.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/color_dlg.h"
+#include "ui/gtk/color_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/dfilter_expr_dlg.h"
+#include "ui/gtk/color_edit_dlg.h"
+#include "ui/gtk/filter_autocomplete.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+#define BUTTON_SIZE_X -1
+#define BUTTON_SIZE_Y -1
+
+
+static void edit_color_filter_destroy_cb(GObject *object, gpointer user_data);
+static void edit_color_filter_fg_cb(GtkButton *button, gpointer user_data);
+static void edit_color_filter_bg_cb(GtkButton *button, gpointer user_data);
+/*
+ static void edit_disabled_cb_cb(GtkButton *button, gpointer user_data);
+*/
+static void edit_color_filter_ok_cb(GtkButton *button, gpointer user_data);
+static void edit_new_color_filter_cancel_cb(GtkButton *button, gpointer user_data);
+
+static GtkWidget* color_sel_win_new(color_filter_t *colorf, gboolean);
+static void color_sel_ok_cb(GtkButton *button, gpointer user_data);
+static void color_sel_cancel_cb(GObject *object, gpointer user_data);
+
+
+#define COLOR_FILTERS_CL "color_filters_cl"
+#define COLOR_FILTER "color_filter"
+#define COLOR_FILTER_NAME_TE "color_filter_name_te"
+#define COLOR_FILTER_TEXT_TE "color_filter_text_te"
+#define COLOR_SELECTION_FG "color_selection_fg"
+#define COLOR_SELECTION_BG "color_selection_bg"
+#define COLOR_SELECTION_PARENT "color_selection_parent"
+
+/* XXX - we don't forbid having more than one "Edit color filter" dialog
+ open, so these shouldn't be global. */
+static GtkWidget *filt_name_entry;
+static GtkWidget *filt_text_entry;
+static GtkWidget *disabled_cb;
+
+
+static void
+filter_expr_cb(GtkWidget *w _U_, gpointer filter_te)
+{
+
+ dfilter_expr_dlg_new(GTK_WIDGET(filter_te));
+}
+
+
+/* Create an "Edit Color Filter" dialog for a given color filter, and
+ associate it with that color filter. */
+void
+edit_color_filter_dialog(GtkWidget *color_filters,
+ gboolean is_new_filter)
+{
+ color_filter_t *colorf;
+ GtkWidget *edit_dialog;
+ GtkWidget *dialog_vbox;
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkRGBA bg_rgba_color, fg_rgba_color;
+#else
+ GdkColor bg_color, fg_color;
+#endif
+ GtkWidget *filter_fr;
+ GtkWidget *filter_fr_vbox;
+ GtkWidget *filter_name_hbox;
+ GtkWidget *color_filter_name;
+ GtkWidget *filter_string_hbox;
+ GtkWidget *add_expression_bt;
+ GtkWidget *color_filter_text;
+
+ GtkWidget *settings_hbox;
+
+ GtkWidget *colorize_fr;
+ GtkWidget *colorize_hbox;
+ GtkWidget *colorize_filter_fg;
+ GtkWidget *colorize_filter_bg;
+
+ GtkWidget *status_fr;
+ GtkWidget *status_vbox;
+
+ GtkWidget *bbox;
+ GtkWidget *edit_color_filter_ok;
+ GtkWidget *edit_color_filter_cancel;
+
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(color_filters));
+
+ gtk_tree_model_iter_nth_child(model, &iter, NULL, color_dlg_row_selected);
+ gtk_tree_model_get(model, &iter, 5, &colorf, -1);
+
+ if (colorf->edit_dialog != NULL) {
+ /* There's already an edit box open for this filter; reactivate it. */
+ reactivate_window(colorf->edit_dialog);
+ return;
+ }
+
+ /* dialog window */
+ edit_dialog = dlg_conf_window_new ("Wireshark: Edit Color Filter");
+ gtk_window_set_default_size(GTK_WINDOW(edit_dialog), 500, -1);
+ g_object_set_data(G_OBJECT(edit_dialog), "edit_dialog", edit_dialog);
+ colorf->edit_dialog = edit_dialog;
+
+ dialog_vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog_vbox), 5);
+ gtk_container_add (GTK_CONTAINER (edit_dialog), dialog_vbox);
+
+ /* Filter frame */
+ filter_fr = gtk_frame_new("Filter");
+ gtk_box_pack_start (GTK_BOX (dialog_vbox), filter_fr, FALSE, FALSE, 0);
+
+ filter_fr_vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (filter_fr_vbox), 5);
+ gtk_container_add(GTK_CONTAINER(filter_fr), filter_fr_vbox);
+
+ /* filter name hbox */
+ filter_name_hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (filter_fr_vbox), filter_name_hbox, TRUE, FALSE, 3);
+
+ color_filter_name = gtk_label_new (("Name: "));
+ gtk_box_pack_start (GTK_BOX (filter_name_hbox), color_filter_name, FALSE, FALSE, 0);
+
+ filt_name_entry = gtk_entry_new ();
+ gtk_entry_set_text(GTK_ENTRY(filt_name_entry), colorf->filter_name);
+
+#if GTK_CHECK_VERSION(3,0,0)
+ color_t_to_gdkRGBAcolor(&bg_rgba_color, &colorf->bg_color);
+ color_t_to_gdkRGBAcolor(&fg_rgba_color, &colorf->fg_color);
+ gtk_widget_override_background_color(filt_name_entry, GTK_STATE_NORMAL, &bg_rgba_color);
+ gtk_widget_override_color(filt_name_entry, GTK_STATE_NORMAL, &fg_rgba_color);
+#else
+ color_t_to_gdkcolor(&bg_color, &colorf->bg_color);
+ color_t_to_gdkcolor(&fg_color, &colorf->fg_color);
+
+ gtk_widget_modify_base(filt_name_entry, GTK_STATE_NORMAL, &bg_color);
+ gtk_widget_modify_text(filt_name_entry, GTK_STATE_NORMAL, &fg_color);
+#endif
+ gtk_box_pack_start (GTK_BOX (filter_name_hbox), filt_name_entry, TRUE, TRUE, 0);
+ gtk_widget_set_tooltip_text(filt_name_entry, "This is the editable name of the filter. (No @ characters allowed.)");
+
+ /* filter string hbox */
+ filter_string_hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (filter_fr_vbox), filter_string_hbox, TRUE, FALSE, 3);
+
+ color_filter_text = gtk_label_new (("String: "));
+ gtk_box_pack_start (GTK_BOX (filter_string_hbox), color_filter_text, FALSE, FALSE, 0);
+
+ filt_text_entry = gtk_entry_new ();
+ g_signal_connect(filt_text_entry, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
+ g_object_set_data(G_OBJECT(filter_string_hbox), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+ g_signal_connect(filt_text_entry, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
+ g_signal_connect(edit_dialog, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
+ gtk_entry_set_text(GTK_ENTRY(filt_text_entry), colorf->filter_text);
+
+ gtk_box_pack_start (GTK_BOX (filter_string_hbox), filt_text_entry, TRUE, TRUE, 0);
+ gtk_widget_set_tooltip_text(filt_text_entry, "This is the editable text of the filter");
+
+ /* Create the "Add Expression..." button, to pop up a dialog
+ for constructing filter comparison expressions. */
+ add_expression_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_ADD_EXPRESSION);
+ g_signal_connect(add_expression_bt, "clicked", G_CALLBACK(filter_expr_cb), filt_text_entry);
+ gtk_box_pack_start (GTK_BOX(filter_string_hbox), add_expression_bt, FALSE, FALSE, 3);
+ gtk_widget_set_tooltip_text(add_expression_bt, "Add an expression to the filter string");
+
+ /* Show the (in)validity of the default filter string */
+ filter_te_syntax_check_cb(filt_text_entry, NULL);
+
+ /* settings-hbox for "choose color frame" and "status frame" */
+ settings_hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (dialog_vbox), settings_hbox, FALSE, FALSE, 0);
+
+ /* choose color frame */
+ colorize_fr = gtk_frame_new("Display Colors");
+ gtk_box_pack_start (GTK_BOX (settings_hbox), colorize_fr, TRUE, TRUE, 0);
+
+ colorize_hbox = gtk_hbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (colorize_hbox), 5);
+ gtk_container_add(GTK_CONTAINER(colorize_fr), colorize_hbox);
+
+ colorize_filter_fg = gtk_button_new_with_label (("Foreground Color..."));
+ gtk_box_pack_start (GTK_BOX (colorize_hbox), colorize_filter_fg, TRUE, FALSE, 0);
+ gtk_widget_set_tooltip_text(colorize_filter_fg, "Select foreground color for data display");
+
+ colorize_filter_bg = gtk_button_new_with_label (("Background Color..."));
+ gtk_box_pack_start (GTK_BOX (colorize_hbox), colorize_filter_bg, TRUE, FALSE, 0);
+ gtk_widget_set_tooltip_text(colorize_filter_bg, "Select background color for data display");
+
+ /* status frame */
+ status_fr = gtk_frame_new("Status");
+ gtk_box_pack_start (GTK_BOX (settings_hbox), status_fr, TRUE, TRUE, 0);
+
+ status_vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (status_vbox), 5);
+ gtk_container_add(GTK_CONTAINER(status_fr), status_vbox);
+
+ disabled_cb = gtk_check_button_new_with_label("Disabled");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(disabled_cb), colorf->disabled);
+ gtk_box_pack_start (GTK_BOX (status_vbox), disabled_cb, TRUE, FALSE, 0);
+ gtk_widget_set_tooltip_text(disabled_cb, "Color rule won't be checked if this box is selected");
+
+ /* button box */
+ bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL);
+ gtk_box_pack_start(GTK_BOX(dialog_vbox), bbox, FALSE, FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (bbox), 0);
+
+ edit_color_filter_ok = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ gtk_widget_set_tooltip_text(edit_color_filter_ok, "Accept filter color change");
+
+ edit_color_filter_cancel = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ gtk_widget_set_tooltip_text(edit_color_filter_cancel, "Reject filter color change");
+
+ gtk_widget_grab_default(edit_color_filter_ok);
+
+ /* signals and such */
+ g_object_set_data(G_OBJECT(edit_dialog), COLOR_FILTER, colorf);
+ g_signal_connect(edit_dialog, "destroy", G_CALLBACK(edit_color_filter_destroy_cb), NULL);
+ g_object_set_data(G_OBJECT(colorize_filter_fg), COLOR_FILTER, colorf);
+ g_signal_connect(colorize_filter_fg, "clicked", G_CALLBACK(edit_color_filter_fg_cb), NULL);
+ g_object_set_data(G_OBJECT(colorize_filter_bg), COLOR_FILTER, colorf);
+ g_signal_connect(colorize_filter_bg, "clicked", G_CALLBACK(edit_color_filter_bg_cb), NULL);
+ g_object_set_data(G_OBJECT(disabled_cb), COLOR_FILTER, colorf);
+/* g_signal_connect(disabled_cb, "clicked", G_CALLBACK(edit_disabled_cb_cb), NULL);*/
+ g_object_set_data(G_OBJECT(edit_color_filter_ok), COLOR_FILTERS_CL, color_filters);
+ g_object_set_data(G_OBJECT(edit_color_filter_ok), COLOR_FILTER, colorf);
+ g_signal_connect(edit_color_filter_ok, "clicked", G_CALLBACK(edit_color_filter_ok_cb), edit_dialog);
+
+ /* set callback to delete new filters if cancel chosen */
+ if (is_new_filter)
+ {
+ g_object_set_data(G_OBJECT(edit_color_filter_cancel), COLOR_FILTERS_CL, color_filters);
+ g_signal_connect(edit_color_filter_cancel, "clicked",
+ G_CALLBACK(edit_new_color_filter_cancel_cb), edit_dialog);
+ }
+ /* escape will select cancel */
+ window_set_cancel_button(edit_dialog, edit_color_filter_cancel, window_cancel_button_cb);
+
+ g_signal_connect(edit_dialog, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+
+ gtk_widget_show_all(edit_dialog);
+ window_present(edit_dialog);
+}
+
+/* Called when the dialog box is being destroyed; destroy any color
+ selection dialogs opened from this dialog, and null out the pointer
+ to this dialog. */
+static void
+edit_color_filter_destroy_cb(GObject *object, gpointer user_data _U_)
+{
+ color_filter_t *colorf;
+ GtkWidget *color_sel;
+
+ colorf = (color_filter_t *)g_object_get_data(G_OBJECT(object), COLOR_FILTER);
+ colorf->edit_dialog = NULL;
+
+ /* Destroy any color selection dialogs this dialog had open. */
+ color_sel = (GtkWidget *)g_object_get_data(G_OBJECT(object), COLOR_SELECTION_FG);
+ if (color_sel != NULL)
+ window_destroy(color_sel);
+ color_sel = (GtkWidget *)g_object_get_data(G_OBJECT(object), COLOR_SELECTION_BG);
+ if (color_sel != NULL)
+ window_destroy(color_sel);
+}
+
+/* Pop up a color selection box to choose the foreground color. */
+static void
+edit_color_filter_fg_cb(GtkButton *button, gpointer user_data _U_)
+{
+ color_filter_t *colorf;
+ GtkWidget *color_selection_fg;
+
+ colorf = (color_filter_t *)g_object_get_data(G_OBJECT(button), COLOR_FILTER);
+ /* Do we already have one open for this dialog? */
+ color_selection_fg = g_object_get_data(G_OBJECT(colorf->edit_dialog), COLOR_SELECTION_FG);
+ if (color_selection_fg != NULL) {
+ /* Yes. Just reactivate it. */
+ reactivate_window(color_selection_fg);
+ } else {
+ /* No. Create a new color selection box, and associate it with
+ this dialog. */
+ color_selection_fg = color_sel_win_new(colorf, FALSE);
+ g_object_set_data(G_OBJECT(colorf->edit_dialog), COLOR_SELECTION_FG, color_selection_fg);
+ g_object_set_data(G_OBJECT(color_selection_fg), COLOR_SELECTION_PARENT, colorf->edit_dialog);
+ }
+}
+
+/* Pop up a color selection box to choose the background color. */
+static void
+edit_color_filter_bg_cb (GtkButton *button,
+ gpointer user_data _U_)
+{
+ color_filter_t *colorf;
+ GtkWidget *color_selection_bg;
+
+ colorf = (color_filter_t *)g_object_get_data(G_OBJECT(button), COLOR_FILTER);
+ /* Do we already have one open for this dialog? */
+ color_selection_bg = g_object_get_data(G_OBJECT(colorf->edit_dialog), COLOR_SELECTION_BG);
+ if (color_selection_bg != NULL) {
+ /* Yes. Just reactivate it. */
+ reactivate_window(color_selection_bg);
+ } else {
+ /* No. Create a new color selection box, and associate it with
+ this dialog. */
+ color_selection_bg = color_sel_win_new(colorf, TRUE);
+ g_object_set_data(G_OBJECT(colorf->edit_dialog), COLOR_SELECTION_BG, color_selection_bg);
+ g_object_set_data(G_OBJECT(color_selection_bg), COLOR_SELECTION_PARENT, colorf->edit_dialog);
+ }
+}
+
+/* Toggle the disabled flag */
+#if 0
+static void
+edit_disabled_cb_cb (GtkButton *button,
+ gpointer user_data _U_)
+{
+ color_filter_t *colorf;
+
+ colorf = (color_filter_t *)g_object_get_data(G_OBJECT(button), COLOR_FILTER);
+ colorf->disabled = GTK_TOGGLE_BUTTON (button)->active;
+
+ printf("Colorfilter %s is now %s\n",colorf->filter_name,colorf->disabled?"disabled":"enabled");
+}
+#endif
+
+/* accept color (and potential content) change */
+static void
+edit_color_filter_ok_cb (GtkButton *button,
+ gpointer user_data)
+{
+ GtkWidget *dialog;
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context;
+ GdkRGBA *new_rgba_bg_color;
+ GdkRGBA *new_rgba_fg_color;
+#else
+ GtkStyle *style;
+ GdkColor new_fg_color;
+ GdkColor new_bg_color;
+#endif
+ gchar *filter_name;
+ gchar *filter_text;
+ gboolean filter_disabled;
+ color_filter_t *colorf;
+ dfilter_t *compiled_filter;
+ GtkWidget *color_filters;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gchar fg_str[14], bg_str[14];
+
+ dialog = (GtkWidget *)user_data;
+#if GTK_CHECK_VERSION(3,0,0)
+ context = gtk_widget_get_style_context (filt_name_entry);
+ gtk_style_context_get (context, GTK_STATE_NORMAL,
+ "background-color", &new_rgba_bg_color,
+ NULL);
+ gtk_style_context_get (context, GTK_STATE_NORMAL,
+ "forground-color", &new_rgba_fg_color,
+ NULL);
+/* gdk_rgba_free (rgba_bg_color); */
+
+#else
+
+ style = gtk_widget_get_style(filt_name_entry);
+ new_bg_color = style->base[GTK_STATE_NORMAL];
+ new_fg_color = style->text[GTK_STATE_NORMAL];
+#endif
+
+ filter_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(filt_name_entry)));
+ filter_text = g_strdup(gtk_entry_get_text(GTK_ENTRY(filt_text_entry)));
+ filter_disabled = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(disabled_cb));
+
+ if(strchr(filter_name,'@') || strchr(filter_text,'@')){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Filter names and strings must not"
+ " use the '@' character. Filter unchanged.");
+ g_free(filter_name);
+ g_free(filter_text);
+ return;
+ }
+
+ if(!dfilter_compile(filter_text, &compiled_filter)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Filter \"%s\" didn't compile correctly.\n"
+ " Please try again. Filter unchanged.\n%s\n", filter_name,
+ dfilter_error_msg);
+ } else {
+ color_filters = (GtkWidget *)g_object_get_data(G_OBJECT(button), COLOR_FILTERS_CL);
+ colorf = (color_filter_t *)g_object_get_data(G_OBJECT(button), COLOR_FILTER);
+
+ g_free(colorf->filter_name);
+ colorf->filter_name = filter_name;
+
+ g_free(colorf->filter_text);
+ colorf->filter_text = filter_text;
+
+ colorf->disabled = filter_disabled;
+#if GTK_CHECK_VERSION(3,0,0)
+ gdkRGBAcolor_to_color_t(&colorf->fg_color, new_rgba_fg_color);
+ gdkRGBAcolor_to_color_t(&colorf->bg_color, new_rgba_bg_color);
+#else
+ gdkcolor_to_color_t(&colorf->fg_color, &new_fg_color);
+ gdkcolor_to_color_t(&colorf->bg_color, &new_bg_color);
+#endif
+ g_snprintf(fg_str, sizeof(fg_str), "#%04X%04X%04X",
+ colorf->fg_color.red, colorf->fg_color.green, colorf->fg_color.blue);
+ g_snprintf(bg_str, sizeof(bg_str), "#%04X%04X%04X",
+ colorf->bg_color.red, colorf->bg_color.green, colorf->bg_color.blue);
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(color_filters));
+ gtk_tree_model_iter_nth_child(model, &iter, NULL, color_dlg_row_selected);
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, filter_name,
+ 1, filter_text, 2, fg_str, 3, bg_str,
+ 4, filter_disabled, -1);
+ if(colorf->c_colorfilter != NULL)
+ dfilter_free(colorf->c_colorfilter);
+ colorf->c_colorfilter = compiled_filter;
+
+ /* Destroy the dialog box. */
+ window_destroy(dialog);
+ }
+#if GTK_CHECK_VERSION(3,0,0)
+ gdk_rgba_free (new_rgba_fg_color);
+ gdk_rgba_free (new_rgba_bg_color);
+#endif
+}
+
+/* reject new color filter addition */
+static void
+edit_new_color_filter_cancel_cb(GtkButton *button, gpointer user_data _U_)
+{
+ /* Delete the entry. As a side effect this destroys the edit_dialog window. */
+ color_delete_single(color_dlg_num_of_filters-1, (GtkWidget*)g_object_get_data(G_OBJECT(button), COLOR_FILTERS_CL));
+}
+
+static GtkWidget*
+color_sel_win_new(color_filter_t *colorf, gboolean is_bg)
+{
+ gchar *title;
+ GtkWidget *color_sel_win;
+ color_t *color;
+ GdkColor gcolor;
+ GtkWidget *color_sel_ok;
+ GtkWidget *color_sel_cancel;
+ GtkWidget *color_sel_help;
+
+ if (is_bg) {
+ color = &colorf->bg_color;
+ title = g_strdup_printf("Wireshark: Choose background color for \"%s\"",
+ colorf->filter_name);
+ } else {
+ color = &colorf->fg_color;
+ title = g_strdup_printf("Wireshark: Choose foreground color for \"%s\"",
+ colorf->filter_name);
+ }
+ color_sel_win = gtk_color_selection_dialog_new(title);
+ g_free(title);
+ g_object_set_data(G_OBJECT(color_sel_win), "color_sel_win", color_sel_win);
+ gtk_container_set_border_width (GTK_CONTAINER (color_sel_win), 10);
+
+ if (color != NULL) {
+ color_t_to_gdkcolor(&gcolor, color);
+ gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(color_sel_win))), &gcolor);
+ }
+
+ g_object_get(color_sel_win, "ok-button", &color_sel_ok, NULL);
+ g_object_set_data(G_OBJECT(color_sel_win), "color_sel_ok", color_sel_ok);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(color_sel_ok, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS (color_sel_ok, GTK_CAN_DEFAULT);
+#endif
+
+ g_object_get(color_sel_win, "cancel-button", &color_sel_cancel, NULL);
+ g_object_set_data(G_OBJECT(color_sel_win), "color_sel_cancel", color_sel_cancel);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(color_sel_cancel, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS (color_sel_cancel, GTK_CAN_DEFAULT);
+#endif
+ window_set_cancel_button(color_sel_win, color_sel_cancel, NULL); /* ensure esc does req'd local cxl action. */
+ /* esc as handled by the */
+ /* gtk_color_selection_dialog widget */
+ /* doesn't result in this happening. */
+
+ g_object_get(color_sel_win, "help-button", &color_sel_help, NULL);
+ g_object_set_data(G_OBJECT(color_sel_win), "color_sel_help", color_sel_help);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(color_sel_help, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS (color_sel_help, GTK_CAN_DEFAULT);
+#endif
+
+ g_signal_connect(color_sel_ok, "clicked", G_CALLBACK(color_sel_ok_cb), color_sel_win);
+ g_signal_connect(color_sel_cancel, "clicked", G_CALLBACK(color_sel_cancel_cb), color_sel_win);
+
+ gtk_widget_show_all(color_sel_win);
+ return color_sel_win;
+}
+
+static void
+color_sel_win_destroy(GtkWidget *sel_win)
+{
+ GtkWidget *parent;
+ GtkWidget *color_selection_fg, *color_selection_bg;
+
+ /* Find the "Edit color filter" dialog box with which this is associated. */
+ parent = (GtkWidget *)g_object_get_data(G_OBJECT(sel_win), COLOR_SELECTION_PARENT);
+
+ /* Find that dialog box's foreground and background color selection
+ boxes, if any. */
+ color_selection_fg = g_object_get_data(G_OBJECT(parent), COLOR_SELECTION_FG);
+ color_selection_bg = g_object_get_data(G_OBJECT(parent), COLOR_SELECTION_BG);
+
+ if (sel_win == color_selection_fg) {
+ /* This was its foreground color selection box; it isn't, anymore. */
+ g_object_set_data(G_OBJECT(parent), COLOR_SELECTION_FG, NULL);
+ }
+ if (sel_win == color_selection_bg) {
+ /* This was its background color selection box; it isn't, anymore. */
+ g_object_set_data(G_OBJECT(parent), COLOR_SELECTION_BG, NULL);
+ }
+
+ /* Now destroy it. */
+ window_destroy(sel_win);
+}
+
+/* Retrieve selected color */
+static void
+color_sel_ok_cb (GtkButton *button _U_,
+ gpointer user_data)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkRGBA new_rgba_color; /* Color from color selection dialog */
+#else
+ GdkColor new_color; /* Color from color selection dialog */
+#endif
+ GtkWidget *color_dialog;
+ GtkWidget *parent;
+ GtkWidget *color_selection_bg;
+ gboolean is_bg;
+
+ color_dialog = (GtkWidget *)user_data;
+
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_color_selection_get_current_rgba(GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(color_dialog))), &new_rgba_color);
+#else
+ gtk_color_selection_get_current_color(GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(color_dialog))), &new_color);
+#endif
+#if 0
+ if ( ! get_color(&new_color) ){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Could not allocate color. Try again.");
+ } else {
+#endif
+ /* Find the "Edit color filter" dialog box with which this is
+ associated. */
+ parent = (GtkWidget *)g_object_get_data(G_OBJECT(color_dialog), COLOR_SELECTION_PARENT);
+
+ /* Find that dialog box's foreground and background color selection
+ boxes, if any. */
+ color_selection_bg = g_object_get_data(G_OBJECT(parent), COLOR_SELECTION_BG);
+ is_bg = (color_dialog == color_selection_bg);
+
+ color_sel_win_destroy(color_dialog);
+#if GTK_CHECK_VERSION(3,0,0)
+ /* now apply the change to the fore/background */
+ if (is_bg)
+ gtk_widget_override_background_color(filt_name_entry, GTK_STATE_NORMAL, &new_rgba_color);
+ else
+ gtk_widget_override_color(filt_name_entry, GTK_STATE_NORMAL, &new_rgba_color);
+#else
+ /* now apply the change to the fore/background */
+ if (is_bg)
+ gtk_widget_modify_base(filt_name_entry, GTK_STATE_NORMAL, &new_color);
+ else
+ gtk_widget_modify_text(filt_name_entry, GTK_STATE_NORMAL, &new_color);
+#endif
+#if 0
+ }
+#endif
+}
+
+/* Don't choose the selected color as the foreground or background
+ color for the filter. */
+static void
+color_sel_cancel_cb (GObject *object _U_,
+ gpointer user_data)
+{
+ GtkWidget *color_dialog;
+ color_dialog = (GtkWidget *)user_data;
+ /* nothing to change here. Just get rid of the dialog box. */
+
+ color_sel_win_destroy(color_dialog);
+}
diff --git a/ui/gtk/color_edit_dlg.h b/ui/gtk/color_edit_dlg.h
new file mode 100644
index 0000000000..e54fc3eff5
--- /dev/null
+++ b/ui/gtk/color_edit_dlg.h
@@ -0,0 +1,42 @@
+/* color_edit_dlg.h
+ * Definitions for dialog boxes for color filters
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __COLOR_EDIT_DLG_H__
+#define __COLOR_EDIT_DLG_H__
+
+/** @file
+ * "Colorize Edit Display" dialog box.
+ * @ingroup dialog_group
+ */
+
+/* new color filter edit dialog */
+extern void
+edit_color_filter_dialog(GtkWidget *color_filters,
+ gboolean is_new_filter);
+
+/* edit dialog wants to destroy itself */
+extern void
+color_delete_single(gint row, GtkWidget *color_filters);
+
+#endif /* color_edit_dlg.h */
diff --git a/ui/gtk/color_utils.c b/ui/gtk/color_utils.c
new file mode 100644
index 0000000000..7e2d9243ed
--- /dev/null
+++ b/ui/gtk/color_utils.c
@@ -0,0 +1,153 @@
+/* color_utils.c
+ * Toolkit-dependent implementations of routines to handle colors.
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "../color.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/color_utils.h"
+#include "ui/gtk/gtkglobals.h"
+#if 0
+static GdkColormap* sys_cmap;
+static GdkColormap* our_cmap = NULL;
+#endif
+GdkColor WHITE = { 0, 65535, 65535, 65535 };
+/*GdkColor LTGREY = { 0, 57343, 57343, 57343 };*/
+GdkColor BLACK = { 0, 0, 0, 0 };
+
+/*
+ * Initialize a color with R, G, and B values, including any toolkit-dependent
+ * work that needs to be done.
+ * Returns TRUE if it succeeds, FALSE if it fails.
+ */
+gboolean
+initialize_color(color_t *color, guint16 red, guint16 green, guint16 blue)
+{
+ GdkColor gdk_color;
+
+ gdk_color.pixel = 0;
+ gdk_color.red = red;
+ gdk_color.green = green;
+ gdk_color.blue = blue;
+#if 0
+ if (!get_color(&gdk_color))
+ return FALSE;
+#endif
+ gdkcolor_to_color_t(color, &gdk_color);
+ return TRUE;
+}
+
+/* Initialize the colors */
+void
+colors_init(void)
+{
+#if 0
+ gboolean got_white, got_black;
+
+ sys_cmap = gdk_colormap_get_system();
+
+ /* Allocate "constant" colors. */
+ got_white = get_color(&WHITE);
+ got_black = get_color(&BLACK);
+ /* Got milk? */
+ if (!got_white) {
+ if (!got_black)
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Could not allocate colors black or white.");
+ else
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Could not allocate color white.");
+ } else {
+ if (!got_black)
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Could not allocate color black.");
+ }
+#endif
+
+}
+#if 0
+/* allocate a color from the color map */
+gboolean
+get_color(GdkColor *new_color)
+{
+ GdkVisual *pv;
+
+ if (!our_cmap) {
+ if (!gdk_colormap_alloc_color (sys_cmap, new_color, FALSE,
+ TRUE)) {
+ pv = gdk_visual_get_best();
+ if (!(our_cmap = gdk_colormap_new(pv, TRUE))) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Could not create new colormap");
+ }
+ } else
+ return (TRUE);
+ }
+ return (gdk_colormap_alloc_color(our_cmap, new_color, FALSE, TRUE));
+}
+#endif
+void
+color_t_to_gdkcolor(GdkColor *target, const color_t *source)
+{
+ target->pixel = source->pixel;
+ target->red = source->red;
+ target->green = source->green;
+ target->blue = source->blue;
+}
+#if GTK_CHECK_VERSION(3,0,0)
+void
+color_t_to_gdkRGBAcolor(GdkRGBA *target, const color_t *source)
+{
+ target->alpha = 1;
+ target->red = source->red / 65535.0;
+ target->green = source->green / 65535.0;
+ target->blue = source->blue / 65535.0;
+}
+#endif
+void
+gdkcolor_to_color_t(color_t *target, const GdkColor *source)
+{
+ target->pixel = source->pixel;
+ target->red = source->red;
+ target->green = source->green;
+ target->blue = source->blue;
+}
+#if GTK_CHECK_VERSION(3,0,0)
+void
+gdkRGBAcolor_to_color_t(color_t *target, const GdkRGBA *source)
+{
+ target->pixel = 0;
+ target->red = source->red*65535;
+ target->green = source->green*65535;
+ target->blue = source->blue*65535;
+}
+#endif
+
diff --git a/ui/gtk/color_utils.h b/ui/gtk/color_utils.h
new file mode 100644
index 0000000000..9562a32a5d
--- /dev/null
+++ b/ui/gtk/color_utils.h
@@ -0,0 +1,70 @@
+/* colors.h
+ * Definitions for color structures and routines
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __COLORS_H__
+#define __COLORS_H__
+
+/** @file
+ * Definitions for color structures and routines
+ */
+
+/** The color white. */
+extern GdkColor WHITE;
+
+/** The color light-grey. */
+extern GdkColor LTGREY;
+
+/** The color black. */
+extern GdkColor BLACK;
+
+/** Initialize the colors. */
+void colors_init(void);
+
+/** Allocate a color from the color map.
+ *
+ * @param new_color the new color
+ * @return TRUE if the allocation succeeded
+ */
+#if 0
+gboolean get_color(GdkColor *new_color);
+#endif
+/** Convert color_t to GdkColor.
+ *
+ * @param target the GdkColor to be filled
+ * @param source the source color_t
+ */
+void color_t_to_gdkcolor(GdkColor *target, const color_t *source);
+#if GTK_CHECK_VERSION(3,0,0)
+void color_t_to_gdkRGBAcolor(GdkRGBA *target, const color_t *source);
+#endif
+/** Convert GdkColor to color_t.
+ *
+ * @param target the source color_t
+ * @param source the GdkColor to be filled
+ */
+void gdkcolor_to_color_t(color_t *target, const GdkColor *source);
+#if GTK_CHECK_VERSION(3,0,0)
+void gdkRGBAcolor_to_color_t(color_t *target, const GdkRGBA *source);
+#endif
+#endif /* __COLORS_H__ */
diff --git a/ui/gtk/compare_stat.c b/ui/gtk/compare_stat.c
new file mode 100644
index 0000000000..ea487c4964
--- /dev/null
+++ b/ui/gtk/compare_stat.c
@@ -0,0 +1,1072 @@
+/* compare_stat.c
+ * Compare two capture files
+ * Copyright 2008 Vincenzo Condoleo, Christophe Dirac, Reto Ruoss
+ * supported by HSR (Hochschule Rapperswil)
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 0F111-1307, USA.
+ */
+
+/* This module provides statistics about two merged capture files, to find packet loss,
+ * time delay, ip header checksum errors and order check.
+ * It's also detecting the matching regions of the different files.
+ * After the coloring is set Info column can be sorted to create zebra effect.
+ *
+ * The packets are compared by the ip id. MAC or TTL is used to distinct the different files.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/epan_dissect.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/to_str.h>
+#include <epan/tap.h>
+#include <epan/emem.h>
+#include <epan/packet.h>
+#include <epan/report_err.h>
+#include <epan/dissectors/packet-ip.h>
+#include <epan/nstime.h>
+#include <epan/in_cksum.h>
+
+#include "../stat_menu.h"
+#include "../simple_dialog.h"
+#include "../timestats.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/filter_autocomplete.h"
+
+#include "gui_utils.h"
+#include "dlg_utils.h"
+#include "register.h"
+#include "main.h"
+#include "filter_dlg.h"
+#include "service_response_time_table.h"
+#include "gtkglobals.h"
+#include "gui_utils.h"
+#include "globals.h"
+
+/* Color settings */
+#include "color.h"
+#include "color_filters.h"
+#include "color_dlg.h"
+#include "new_packet_list.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+/* From colorize convertion */
+#define COLOR_N 1
+
+/* For checksum */
+#define BYTES 8
+#define WRONG_CHKSUM 0
+
+#define MERGED_FILES 2
+
+#define TTL_SEARCH 5
+
+
+
+/* information which are needed for the display */
+typedef struct _for_gui {
+ guint count;
+ guint16 cksum;
+ nstime_t predecessor_time;
+ struct _frame_info *partner;
+} for_gui;
+
+/* each tracked packet */
+typedef struct _frame_info {
+ for_gui *fg;
+ column_info *cinfo;
+ guint32 num;
+ guint16 id;
+ guint8 ip_ttl;
+ address dl_dst;
+ nstime_t abs_ts, zebra_time, delta;
+} frame_info;
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _compstat_t {
+ GtkWidget *win, *treeview, *scrolled_win, *statis_label;
+ GtkTreeStore *simple_list;
+ GtkTreeIter iter, child;
+ emem_tree_t *packet_tree, *ip_id_tree, *nr_tree;
+ address eth_dst, eth_src;
+ nstime_t zebra_time, current_time;
+ timestat_t stats;
+ GArray *ip_ttl_list;
+ gboolean last_hit;
+ guint32 start_ongoing_hits, stop_ongoing_hits, start_packet_nr_first, start_packet_nr_second, stop_packet_nr_first, stop_packet_nr_second;
+ guint32 first_file_amount, second_file_amount;
+} compstat_t;
+
+/* column numbers */
+enum
+{
+ IP_ID=0,
+ PROBLEM,
+ COUNT,
+ DELTA,
+ COLUMNS
+};
+
+/* only one compare window should be open */
+static gboolean first_window=TRUE;
+
+/* allowed variace */
+static GtkWidget *spin_var_int=NULL;
+
+/* start/stop compare */
+static GtkWidget *spin_start_int, *spin_stop_int;
+
+/* to call directly _init */
+static gdouble compare_variance=0.0;
+static guint8 compare_start, compare_stop;
+static gboolean TTL_method=TRUE, ON_method=TRUE;
+static GtkWidget *radio_TTL, *radio_ON;
+
+static void
+comparestat_set_title(compstat_t *cs)
+{
+ char *title;
+
+ title=g_strdup_printf("Compare two capture files: %s", cf_get_display_name(&cfile));
+ gtk_window_set_title(GTK_WINDOW(cs->win), title);
+ g_free(title);
+}
+
+/* called when new capture starts, when it rescans the packetlist after some prefs have
+ * changed
+ */
+static void
+comparestat_reset(void *arg)
+{
+ compstat_t *cs=arg;
+
+ SET_ADDRESS(&cs->eth_src, AT_ETHER, 0, NULL);
+ SET_ADDRESS(&cs->eth_dst, AT_ETHER, 0, NULL);
+
+ gtk_tree_store_clear(cs->simple_list);
+ comparestat_set_title(cs);
+}
+
+/* This callback is invoked whenever the tap system has seen a packet
+ * we might be interested in.
+ * function returns :
+ * 0: no updates, no need to call (*draw) later
+ * !0: state has changed, call (*draw) sometime later
+ */
+static int
+comparestat_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *arg2)
+{
+ compstat_t *cs=arg;
+ const ws_ip *ci=arg2;
+ frame_info *fInfo, *fInfoTemp;
+ vec_t cksum_vec[3];
+ guint16 computed_cksum=0;
+
+ /* so this get filled, usually with the first frame */
+ if(cs->eth_dst.len==0) {
+ cs->eth_dst=pinfo->dl_dst;
+ cs->eth_src=pinfo->dl_src;
+ }
+
+ /* Set up the fields of the pseudo-header and create checksum */
+ cksum_vec[0].ptr=&ci->ip_v_hl;
+ cksum_vec[0].len=BYTES;
+ /* skip TTL */
+ cksum_vec[1].ptr=&ci->ip_p;
+ cksum_vec[1].len=1;
+ /* skip header checksum and ip's (because of NAT)*/
+ cksum_vec[2].ptr=ci->ip_dst.data;
+ cksum_vec[2].ptr=cksum_vec[2].ptr+ci->ip_dst.len;
+ /* dynamic computation */
+ cksum_vec[2].len=pinfo->iphdrlen-20;
+ computed_cksum=in_cksum(&cksum_vec[0], 3);
+
+ /* Set up the new order to create the zebra effect */
+ fInfoTemp=se_tree_lookup32(cs->packet_tree, pinfo->fd->num);
+ if((fInfoTemp!=NULL)){
+ col_set_time(pinfo->cinfo, COL_INFO, &fInfoTemp->zebra_time, "ZebraTime");
+ }
+
+ /* collect all packet infos */
+ fInfo=(frame_info*)se_alloc(sizeof(frame_info));
+ fInfo->fg=(for_gui*)se_alloc(sizeof(for_gui));
+ fInfo->fg->partner=NULL;
+ fInfo->fg->count=1;
+ fInfo->fg->cksum=computed_cksum;
+ fInfo->num=pinfo->fd->num;
+ fInfo->id=ci->ip_id;
+ fInfo->ip_ttl=ci->ip_ttl;
+ fInfo->dl_dst=pinfo->dl_dst;
+ fInfo->abs_ts=pinfo->fd->abs_ts;
+ /* clean memory */
+ nstime_set_zero(&fInfo->zebra_time);
+ nstime_set_zero(&fInfo->fg->predecessor_time);
+ se_tree_insert32(cs->packet_tree, pinfo->fd->num, fInfo);
+
+ if(cf_get_packet_count(&cfile)==abs(fInfo->num)){
+ nstime_set_unset(&cs->current_time);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* Find equal packets, same IP-Id, count them and make time statistics */
+static gboolean
+call_foreach_count_ip_id(gpointer value, gpointer arg)
+{
+ compstat_t *cs=(compstat_t*)arg;
+ frame_info *fInfo=(frame_info*)value, *fInfoTemp;
+ nstime_t delta;
+ guint i;
+
+ /* we only need one value out of pinfo we use a temp one */
+ packet_info *pinfo=(packet_info*)ep_alloc(sizeof(packet_info));
+ pinfo->fd=(frame_data*)ep_alloc(sizeof(frame_data));
+ pinfo->fd->num = fInfo->num;
+
+ fInfoTemp=se_tree_lookup32(cs->ip_id_tree, fInfo->id);
+ if(fInfoTemp==NULL){
+ /* Detect ongoing package loss */
+ if((cs->last_hit==FALSE)&&(cs->start_ongoing_hits>compare_start)&&(cs->stop_ongoing_hits<compare_stop)){
+ cs->stop_ongoing_hits++;
+ cs->stop_packet_nr_first=fInfo->num;
+ } else if(cs->stop_ongoing_hits<compare_stop){
+ cs->stop_ongoing_hits=0;
+ cs->stop_packet_nr_first=G_MAXINT32;
+ }
+ cs->last_hit=FALSE;
+
+ fInfo->fg->count=1;
+ se_tree_insert32(cs->ip_id_tree, fInfo->id, fInfo);
+ } else {
+ /* Detect ongoing package hits, special behavior if start is set to 0 */
+ if((cs->last_hit||(compare_start==0))&&(cs->start_ongoing_hits<compare_start||(compare_start==0))){
+ if((compare_start==0)&&(cs->start_ongoing_hits!=0)){
+ /* start from the first packet so allready set */
+ } else {
+ cs->start_ongoing_hits++;
+ /* Take the lower number */
+ cs->start_packet_nr_first=fInfoTemp->num;
+ cs->start_packet_nr_second=fInfo->num;
+ }
+ } else if(cs->start_ongoing_hits<compare_start){
+ cs->start_ongoing_hits=0;
+ cs->start_packet_nr_first=G_MAXINT32;
+ }
+ cs->last_hit=TRUE;
+
+ fInfo->fg->count=fInfoTemp->fg->count + 1;
+ if(fInfoTemp->fg->cksum!=fInfo->fg->cksum){
+ fInfo->fg->cksum=WRONG_CHKSUM;
+ fInfoTemp->fg->cksum=WRONG_CHKSUM;
+ }
+ /* Add partner */
+ fInfo->fg->partner=fInfoTemp;
+ /* Create time statistic */
+ if(fInfo->fg->count==MERGED_FILES){
+ nstime_delta(&delta, &fInfo->abs_ts, &fInfoTemp->abs_ts);
+ /* Set delta in both packets */
+ nstime_set_zero(&fInfoTemp->delta);
+ nstime_add(&fInfoTemp->delta, &delta);
+ nstime_set_zero(&fInfo->delta);
+ nstime_add(&fInfo->delta, &delta);
+ time_stat_update(&cs->stats, &delta, pinfo);
+ }
+ se_tree_insert32(cs->ip_id_tree, fInfo->id, fInfo);
+ }
+
+ /* collect TTL's */
+ if(TTL_method && (fInfo->num<TTL_SEARCH)){
+ for(i=0; i < cs->ip_ttl_list->len; i++){
+ if(g_array_index(cs->ip_ttl_list, guint8, i) == fInfo->ip_ttl){
+ return FALSE;
+ }
+ }
+ g_array_append_val(cs->ip_ttl_list, fInfo->ip_ttl);
+ }
+
+ return FALSE;
+}
+
+/*Create new numbering in the Info column, to create a zebra effect */
+static gboolean
+call_foreach_new_order(gpointer value, gpointer arg)
+{
+ compstat_t *cs=(compstat_t*)arg;
+ frame_info *fInfo=(frame_info*)value, *fInfoTemp;
+
+ /* overwrite Info column for new ordering */
+ fInfoTemp=se_tree_lookup32(cs->nr_tree, fInfo->id);
+ if(fInfoTemp==NULL){
+ if(TTL_method==FALSE){
+ if((ADDRESSES_EQUAL(&cs->eth_dst, &fInfo->dl_dst)) || (ADDRESSES_EQUAL(&cs->eth_src, &fInfo->dl_dst))){
+ se_tree_insert32(cs->nr_tree, fInfo->id, fInfo);
+ fInfo->zebra_time=cs->zebra_time;
+ cs->zebra_time.nsecs=cs->zebra_time.nsecs + MERGED_FILES;
+ } else {
+ cs->zebra_time.nsecs++;
+ se_tree_insert32(cs->nr_tree, fInfo->id, fInfo);
+ fInfo->zebra_time=cs->zebra_time;
+ cs->zebra_time.nsecs++;
+ }
+ } else {
+ if((g_array_index(cs->ip_ttl_list, guint8, 0)==fInfo->ip_ttl) || (g_array_index(cs->ip_ttl_list, guint8, 1)==fInfo->ip_ttl)){
+ se_tree_insert32(cs->nr_tree, fInfo->id, fInfo);
+ fInfo->zebra_time=cs->zebra_time;
+ cs->zebra_time.nsecs=cs->zebra_time.nsecs + MERGED_FILES;
+ } else {
+ cs->zebra_time.nsecs++;
+ se_tree_insert32(cs->nr_tree, fInfo->id, fInfo);
+ fInfo->zebra_time=cs->zebra_time;
+ cs->zebra_time.nsecs++;
+ }
+
+ }
+ } else {
+ if(TTL_method==FALSE){
+ if(((ADDRESSES_EQUAL(&cs->eth_dst, &fInfo->dl_dst)) || (ADDRESSES_EQUAL(&cs->eth_src, &fInfo->dl_dst)))&&(!fmod(fInfoTemp->zebra_time.nsecs,MERGED_FILES))){
+ fInfo->zebra_time.nsecs=fInfoTemp->zebra_time.nsecs;
+ } else {
+ fInfo->zebra_time.nsecs=fInfoTemp->zebra_time.nsecs+1;
+ }
+ } else {
+ if(((g_array_index(cs->ip_ttl_list, guint8, 0)==fInfo->ip_ttl) || (g_array_index(cs->ip_ttl_list, guint8, 1)==fInfo->ip_ttl))&&(!fmod(fInfoTemp->zebra_time.nsecs,MERGED_FILES))){
+ fInfo->zebra_time.nsecs=fInfoTemp->zebra_time.nsecs;
+ } else {
+ fInfo->zebra_time.nsecs=fInfoTemp->zebra_time.nsecs+1;
+ }
+ }
+ }
+
+ /* count packets of file */
+ if(fmod(fInfo->zebra_time.nsecs, MERGED_FILES)){
+ cs->first_file_amount++;
+ } else {
+ cs->second_file_amount++;
+ }
+
+ /* ordering */
+ if(!nstime_is_unset(&cs->current_time)){
+ fInfo->fg->predecessor_time.nsecs=cs->current_time.nsecs;
+ }
+
+ cs->current_time.nsecs=fInfo->zebra_time.nsecs;
+
+ return FALSE;
+}
+
+/* calculate scopes if not set yet */
+static gboolean
+call_foreach_merge_settings(gpointer value, gpointer arg)
+{
+ compstat_t *cs=(compstat_t*)arg;
+ frame_info *fInfo=(frame_info*)value, *fInfoTemp=NULL;
+ guint32 tot_packet_amount=cs->first_file_amount+cs->second_file_amount, swap;
+
+ if((fInfo->num==tot_packet_amount)&&(cs->stop_packet_nr_first!=G_MAXINT32)){
+ /* calculate missing stop number */
+ swap=cs->stop_packet_nr_first;
+ cs->stop_packet_nr_first=tot_packet_amount-cs->second_file_amount;;
+ cs->stop_packet_nr_second=swap;
+ }
+
+ if((fInfo->num==tot_packet_amount)&&(cs->stop_packet_nr_first==G_MAXINT32)&&(cs->start_packet_nr_first!=G_MAXINT32)){
+ fInfoTemp=se_tree_lookup32(cs->packet_tree, cs->start_packet_nr_first);
+ if(fInfoTemp==NULL){
+ fprintf(stderr,"ERROR: Incorrect start number\n");
+ }
+ if(fInfoTemp && fmod(fInfoTemp->zebra_time.nsecs, 2)){
+ /*first file*/
+ cs->stop_packet_nr_first=cs->start_packet_nr_first+abs(cs->second_file_amount-(cs->start_packet_nr_second-cs->first_file_amount));
+ if(cs->stop_packet_nr_first>(tot_packet_amount-cs->second_file_amount)){
+ cs->stop_packet_nr_first=tot_packet_amount-cs->second_file_amount;
+ }
+ /*this only happens if we have too many MAC's or TTL*/
+ if(cs->stop_packet_nr_first>cs->start_packet_nr_second){
+ cs->stop_packet_nr_first=cs->start_packet_nr_second-1;
+ }
+ fInfoTemp=se_tree_lookup32(cs->packet_tree, cs->stop_packet_nr_first);
+ while((fInfoTemp!=NULL)?fmod(!fInfoTemp->zebra_time.nsecs, 2):TRUE){
+ cs->stop_packet_nr_first--;
+ fInfoTemp=se_tree_lookup32(cs->packet_tree, cs->stop_packet_nr_first);
+ }
+ } else {
+ /*this only happens if we have too many MAC's or TTL*/
+ cs->stop_packet_nr_first=cs->first_file_amount+cs->start_packet_nr_first;
+ if(cs->stop_packet_nr_first>tot_packet_amount-cs->first_file_amount){
+ cs->stop_packet_nr_first=tot_packet_amount-cs->first_file_amount;
+ }
+ fInfoTemp=se_tree_lookup32(cs->packet_tree, cs->stop_packet_nr_first);
+ while((fInfoTemp!=NULL)?fmod(fInfoTemp->zebra_time.nsecs, 2):TRUE){
+ cs->stop_packet_nr_first--;
+ fInfoTemp=se_tree_lookup32(cs->packet_tree, cs->stop_packet_nr_first);
+ }
+ }
+ /* set second stop location */
+ cs->stop_packet_nr_second=cs->start_packet_nr_second+abs(cs->stop_packet_nr_first-cs->start_packet_nr_first);
+ if(cs->stop_packet_nr_second>tot_packet_amount){
+ cs->stop_packet_nr_second=tot_packet_amount;
+ }
+ }
+
+ /* no start found */
+ if(fInfo->num==tot_packet_amount&&compare_start!=0&&compare_stop!=0){
+ if(cs->start_packet_nr_first==G_MAXINT32){
+ report_failure("Start point couldn't be set. Please choose a lower start number.");
+ }
+ }
+
+ return FALSE;
+}
+
+
+/* build gtk-tree of lost, delayed, checksum error and wrong order Packets*/
+static gboolean
+call_foreach_print_ip_tree(gpointer value, gpointer user_data)
+{
+ frame_info *fInfo=(frame_info*)value;
+ compstat_t *cs=(compstat_t*)user_data;
+ gdouble delta, average;
+ gboolean show_it=FALSE;
+
+ delta=fabs(get_average(&fInfo->delta,1));
+ average=fabs(get_average(&cs->stats.tot, cs->stats.num));
+
+ /* special case if both are set to zero ignore start and stop numbering */
+ if(compare_start!=0&&compare_stop!=0){
+ /* check out if packet is in searched scope */
+ if((cs->start_packet_nr_first<fInfo->num)&&(cs->stop_packet_nr_first>fInfo->num)){
+ show_it=TRUE;
+ } else {
+ /* so we won't miss the other file */
+ if((fInfo->num>cs->start_packet_nr_second)&&(fInfo->num<cs->stop_packet_nr_second)){
+ show_it=TRUE;
+ }
+ }
+ } else {
+ show_it=TRUE;
+ }
+
+ /* Create the gtk tree */
+ if(show_it){
+ if((fInfo->fg->count<MERGED_FILES)){
+ gtk_tree_store_append(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->iter, NULL);
+ gtk_tree_store_set(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->iter, IP_ID, fInfo->id, PROBLEM, "Lost packet", COUNT, fInfo->fg->count, DELTA, 0.0, -1);
+ }
+
+ if(fInfo->fg->count > MERGED_FILES){
+ gtk_tree_store_append(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->iter, NULL);
+ gtk_tree_store_set(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->iter, IP_ID, fInfo->id, PROBLEM, "More than two packets", COUNT, fInfo->fg->count, DELTA, 0.0, -1);
+ if(fInfo->fg->cksum == WRONG_CHKSUM) {
+ gtk_tree_store_append(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->child, &cs->iter);
+ gtk_tree_store_set(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->child, IP_ID, fInfo->id, PROBLEM, "IP header checksum incorrect", COUNT, fInfo->fg->count, DELTA, 0.0, -1);
+ }
+ }
+ if(fInfo->fg->count == MERGED_FILES){
+ if(fInfo->fg->cksum == WRONG_CHKSUM) {
+ gtk_tree_store_append(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->iter, NULL);
+ gtk_tree_store_set(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->iter, IP_ID, fInfo->id, PROBLEM, "IP header checksum incorrect", COUNT, fInfo->fg->count, DELTA, delta, -1);
+ if(((delta < (average-cs->stats.variance)) || (delta > (average+cs->stats.variance))) && (delta > 0.0) && (cs->stats.variance!=0)){
+ gtk_tree_store_append(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->child, &cs->iter);
+ gtk_tree_store_set(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->child, IP_ID, fInfo->id, PROBLEM, "Late arrival", COUNT, fInfo->fg->count, DELTA, delta, -1);
+ }
+ if((nstime_cmp(&fInfo->fg->predecessor_time, &fInfo->zebra_time)>0||nstime_cmp(&fInfo->fg->partner->fg->predecessor_time, &fInfo->fg->partner->zebra_time)>0) && (fInfo->zebra_time.nsecs!=MERGED_FILES) && ON_method){
+ gtk_tree_store_append(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->child, &cs->iter);
+ gtk_tree_store_set(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->child, IP_ID, fInfo->id, PROBLEM, "Out of order", COUNT, fInfo->fg->count, DELTA, delta, -1);
+ }
+ } else if(((delta < (average-cs->stats.variance)) || (delta > (average+cs->stats.variance))) && (delta > 0.0) && (cs->stats.variance!=0)) {
+ gtk_tree_store_append(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->iter, NULL);
+ gtk_tree_store_set(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->iter, IP_ID, fInfo->id, PROBLEM, "Late arrival", COUNT, fInfo->fg->count, DELTA, delta, -1);
+ if((nstime_cmp(&fInfo->fg->predecessor_time, &fInfo->zebra_time)>0||nstime_cmp(&fInfo->fg->partner->fg->predecessor_time, &fInfo->fg->partner->zebra_time)>0) && fInfo->zebra_time.nsecs != MERGED_FILES && ON_method){
+ gtk_tree_store_append(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->child, &cs->iter);
+ gtk_tree_store_set(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->child, IP_ID, fInfo->id, PROBLEM, "Out of order", COUNT, fInfo->fg->count, DELTA, delta, -1);
+ }
+ } else if((nstime_cmp(&fInfo->fg->predecessor_time, &fInfo->zebra_time)>0||nstime_cmp(&fInfo->fg->partner->fg->predecessor_time, &fInfo->fg->partner->zebra_time)>0) && fInfo->zebra_time.nsecs != MERGED_FILES && ON_method){
+ gtk_tree_store_append(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->iter, NULL);
+ gtk_tree_store_set(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->iter, IP_ID, fInfo->id, PROBLEM, "Out of order", COUNT, fInfo->fg->count, DELTA, delta, -1);
+ }
+ }
+ }
+ return FALSE;
+}
+
+/* since the gtk2 implementation of tap is multithreaded we must protect
+ * remove_tap_listener() from modifying the list while draw_tap_listener()
+ * is running. the other protected block is in main.c
+ *
+ * there should not be any other critical regions in gtk2
+ */
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ compstat_t *cs=(compstat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(cs);
+ unprotect_thread_critical_region();
+
+ first_window=TRUE;
+ gtk_tree_store_clear(cs->simple_list);
+ g_free(cs);
+}
+
+/* this may be called any time, perhaps once every 3 seconds or so.
+ */
+static void
+comparestat_draw(void *arg)
+{
+ compstat_t *cs = arg;
+ GString *filter_str = g_string_new("");
+ const gchar *statis_string;
+ frame_info *fInfo;
+ guint32 first_file_amount, second_file_amount;
+
+ /* inital steps, clear all data before start*/
+ cs->zebra_time.secs=0;
+ cs->zebra_time.nsecs=1;
+ nstime_set_unset(&cs->current_time);
+ cs->ip_ttl_list=g_array_new(FALSE, FALSE, sizeof(guint8));
+ cs->last_hit=FALSE;
+ cs->start_ongoing_hits=0;
+ cs->stop_ongoing_hits=0;
+ cs->start_packet_nr_first=G_MAXINT32;
+ cs->start_packet_nr_second=G_MAXINT32;
+ cs->stop_packet_nr_first=G_MAXINT32;
+ cs->stop_packet_nr_second=G_MAXINT32;
+ cs->first_file_amount=0;
+ cs->second_file_amount=0;
+
+ time_stat_init(&cs->stats);
+
+ /* no need to do anything no file is open*/
+ if(cf_get_packet_count(&cfile)==0){
+ /* add statistic string */
+ statis_string=g_strdup_printf("No file open");
+ gtk_label_set_text((GtkLabel *) cs->statis_label, statis_string);
+ return;
+ }
+
+ /* not using g_free, because struct is managed by se binarytrees */
+ cs->ip_id_tree=se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "ip_id_tree");
+ emem_tree_foreach(cs->packet_tree, call_foreach_count_ip_id, cs);
+
+ /* set up TTL choice if only one number found */
+ if(TTL_method&&cs->ip_ttl_list->len==1){
+ g_array_append_val(cs->ip_ttl_list, g_array_index(cs->ip_ttl_list, guint8, 1));
+ }
+
+ emem_tree_foreach(cs->packet_tree, call_foreach_new_order,cs);
+ emem_tree_foreach(cs->packet_tree, call_foreach_merge_settings, cs);
+
+ /* remembering file amounts */
+ first_file_amount=cs->first_file_amount;
+ second_file_amount=cs->second_file_amount;
+ /* reset after numbering */
+ cs->nr_tree=se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "nr_tree");
+ /* microsecond precision for Info column*/
+ timestamp_set_precision(TS_PREC_AUTO_NSEC);
+ /* reset ordering */
+ nstime_set_unset(&cs->current_time);
+
+ /* set color filter, in Routing environment */
+ if(TTL_method&&cs->ip_ttl_list->len!=0){
+ g_string_printf(filter_str, "%s %i %s %i", "ip.ttl ==", g_array_index(cs->ip_ttl_list, guint8, 0), "|| ip.ttl ==", g_array_index(cs->ip_ttl_list, guint8, 1));
+ } else if(cs->eth_dst.len!=0&&cs->eth_src.len!=0){
+ g_string_printf(filter_str, "%s %s %s %s", "eth.dst==", ep_address_to_str(&cs->eth_dst), "|| eth.dst==", ep_address_to_str(&cs->eth_src));
+ }
+ color_filters_set_tmp(COLOR_N, filter_str->str, FALSE);
+ new_packet_list_colorize_packets();
+ /* Variance */
+ cs->stats.variance=compare_variance;
+
+ /* add statistic string */
+ statis_string=g_strdup_printf("Compare Statistics: \nNumber of packets total:%i 1st file:%i, 2nd file:%i\nScopes:\t start:%i stop:%i\nand:\t start:%i stop:%i\nEqual packets: %i \nAllowed variation: %f \nAverage time difference: %f", cf_get_packet_count(&cfile), first_file_amount, second_file_amount, cs->start_packet_nr_first, cs->stop_packet_nr_first, cs->start_packet_nr_second, cs->stop_packet_nr_second, cs->stats.num, cs->stats.variance, fabs(get_average(&cs->stats.tot, cs->stats.num)));
+ gtk_label_set_text((GtkLabel *) cs->statis_label, statis_string);
+
+ /* add start and stop of scanning */
+ if(cs->start_packet_nr_first!=G_MAXINT32&&compare_start!=0&&compare_stop!=0){
+ fInfo=se_tree_lookup32(cs->packet_tree, cs->start_packet_nr_first);
+ if(fInfo){
+ gtk_tree_store_append(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->iter, NULL);
+ gtk_tree_store_set(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->iter, IP_ID, fInfo->id, PROBLEM, "Start scanning", COUNT, 0, DELTA, 0.0, -1);
+ }
+ }
+ if(cs->stop_packet_nr_first!=G_MAXINT32&&compare_start!=0&&compare_stop!=0){
+ fInfo=se_tree_lookup32(cs->packet_tree, cs->stop_packet_nr_first);
+ if(fInfo){
+ gtk_tree_store_append(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->iter, NULL);
+ gtk_tree_store_set(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cs->treeview))), &cs->iter, IP_ID, fInfo->id, PROBLEM, "Stop scanning", COUNT, 0, DELTA, 0.0, -1);
+ }
+ }
+
+ emem_tree_foreach(cs->ip_id_tree, call_foreach_print_ip_tree, cs);
+ g_string_free(filter_str, TRUE);
+ g_array_free(cs->ip_ttl_list, TRUE);
+}
+
+/* called when a tree row is (un)selected in the popup window */
+static void
+new_tree_view_selection_changed(GtkTreeSelection *sel, gpointer user_data)
+{
+ gchar *problem;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ frame_info *fInfo;
+ /* Because it could be zero */
+ gint id=-1;
+
+ compstat_t *cs=(compstat_t*)user_data;
+
+ /* if something is selected */
+ if(gtk_tree_selection_get_selected(sel, &model, &iter)){
+ gtk_tree_model_get(model, &iter, 0, &id, 1, &problem, -1);
+ if (id<0) return;
+ /* The id is not enough to find the start or the end packet */
+ if(strcmp("Start scanning",problem)==0){
+ cf_goto_frame(&cfile, cs->start_packet_nr_first);
+ return;
+ }
+ if(strcmp("Stop scanning",problem)==0){
+ cf_goto_frame(&cfile, cs->stop_packet_nr_first);
+ return;
+ }
+ fInfo=se_tree_lookup32(cs->ip_id_tree, id);
+ if(fInfo != NULL){
+ cf_goto_frame(&cfile, fInfo->num);
+ }
+ }
+
+}
+
+/* add three columns to the GtkTreeView. All three of the columns will be
+ * displayed as text*/
+static void
+setup_tree_view(GtkWidget *treeview)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ /* Create a new GtkCellRendererText, add it to the tree view column and
+ * append the column to the tree view. */
+ renderer=gtk_cell_renderer_text_new ();
+ column=gtk_tree_view_column_new_with_attributes("IP ID", renderer, "text", IP_ID, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW (treeview), column);
+ renderer=gtk_cell_renderer_text_new ();
+ column=gtk_tree_view_column_new_with_attributes("Problem", renderer, "text", PROBLEM, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW (treeview), column);
+ renderer=gtk_cell_renderer_text_new ();
+ column=gtk_tree_view_column_new_with_attributes("Count", renderer, "text", COUNT, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW (treeview), column);
+ renderer=gtk_cell_renderer_text_new ();
+ column=gtk_tree_view_column_new_with_attributes("Delta", renderer, "text", DELTA, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW (treeview), column);
+}
+
+/* when called, this function will create a new instance of gtk2-comparestat.
+ */
+static void
+gtk_comparestat_init(const char *optarg, void* userdata _U_)
+{
+ compstat_t *cs;
+ char *title_string;
+ char *filter_string;
+ GtkWidget *stat_label;
+ GtkWidget *filter_label;
+ GtkWidget *bbox;
+ GtkWidget *close_bt;
+ GtkWidget *help_bt;
+ GtkWidget *vbox;
+ gdouble variance;
+ gint start, stop,ttl, order, pos=0;
+ const char *filter=NULL;
+ GString *error_string;
+
+ if(sscanf(optarg,"compare,%d,%d,%d,%d,%lf%n",&start, &stop, &ttl, &order, &variance, &pos)==5){
+ if(pos){
+ if(*(optarg+pos)==',')
+ filter=optarg+pos+1;
+ else
+ filter=optarg+pos;
+ } else {
+ filter=NULL;
+ }
+ } else {
+ fprintf(stderr, "wireshark: invalid \"-z compare,<start>,<stop>,<ttl[0|1]>,<order[0|1]>,<variance>[,<filter>]\" argument\n");
+ exit(1);
+ }
+
+ compare_variance=variance;
+ compare_start=start;
+ compare_stop=stop;
+ TTL_method=ttl;
+ ON_method=order;
+
+ cs=g_malloc(sizeof(compstat_t));
+ nstime_set_unset(&cs->current_time);
+ cs->ip_ttl_list=g_array_new(FALSE, FALSE, sizeof(guint8));
+ cs->last_hit=FALSE;
+ cs->start_ongoing_hits=0;
+ cs->stop_ongoing_hits=0;
+ cs->start_packet_nr_first=G_MAXINT32;
+ cs->start_packet_nr_second=G_MAXINT32;
+ cs->stop_packet_nr_first=G_MAXINT32;
+ cs->stop_packet_nr_second=G_MAXINT32;
+ cs->first_file_amount=0;
+ cs->second_file_amount=0;
+
+ cs->zebra_time.secs=0;
+ cs->zebra_time.nsecs=1;
+ cs->nr_tree=se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "nr_tree");
+ /* microsecond precision */
+ timestamp_set_precision(TS_PREC_AUTO_NSEC);
+
+ /* transient_for top_level */
+ cs->win=dlg_window_new("compare-stat");
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(cs->win), TRUE);
+ gtk_window_set_default_size(GTK_WINDOW(cs->win), 550, 400);
+ comparestat_set_title(cs);
+
+ vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(cs->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ title_string = g_strdup_printf("Compare two capture files: %s", cf_get_display_name(&cfile));
+ stat_label=gtk_label_new(title_string);
+ g_free(title_string);
+ gtk_box_pack_start(GTK_BOX(vbox), stat_label, FALSE, FALSE, 0);
+
+ filter_string = g_strdup_printf("Filter: %s", filter ? filter : "");
+ filter_label=gtk_label_new(filter_string);
+ g_free(filter_string);
+ gtk_label_set_line_wrap(GTK_LABEL(filter_label), TRUE);
+ gtk_box_pack_start(GTK_BOX(vbox), filter_label, FALSE, FALSE, 0);
+
+ /* add statistik info to Window */
+ cs->statis_label=gtk_label_new("Statistics:");
+ gtk_label_set_line_wrap(GTK_LABEL(cs->statis_label), TRUE);
+ gtk_box_pack_start(GTK_BOX(vbox), cs->statis_label, FALSE, FALSE, 0);
+
+ /* we must display TOP LEVEL Widget before calling simple_list_new */
+ gtk_widget_show_all(cs->win);
+
+ cs->treeview=gtk_tree_view_new();
+ setup_tree_view(cs->treeview);
+
+ /* create a newtree model with four columns */
+ cs->simple_list=gtk_tree_store_new(COLUMNS, G_TYPE_INT, G_TYPE_STRING, G_TYPE_INT, G_TYPE_DOUBLE);
+
+ /* add the tree model to the tree view and unreference it so that the model will
+ * be destroyed along with the tree view. */
+ gtk_tree_view_set_model(GTK_TREE_VIEW (cs->treeview), GTK_TREE_MODEL (cs->simple_list));
+ g_object_unref(cs->simple_list);
+
+ /* call this method when row is chosen*/
+ gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(cs->treeview)),GTK_SELECTION_SINGLE);
+ g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(cs->treeview)), "changed", G_CALLBACK(new_tree_view_selection_changed), cs);
+
+ /* list with scrollbar's */
+ cs->scrolled_win=gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(cs->scrolled_win), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_container_add(GTK_CONTAINER(cs->scrolled_win), cs->treeview);
+ gtk_box_pack_start(GTK_BOX(vbox), cs->scrolled_win, TRUE, TRUE, 0);
+
+ /* create a Hash to count the packets with the same ip.id */
+ cs->packet_tree=se_tree_create(EMEM_TREE_TYPE_RED_BLACK, "Packet_info_tree");
+
+ error_string=register_tap_listener("ip", cs, filter, 0, comparestat_reset, comparestat_packet, comparestat_draw);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ gtk_tree_store_clear(cs->simple_list);
+ g_free(cs);
+ return;
+ }
+
+ /* button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(cs->win, close_bt, window_cancel_button_cb);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_COMPARE_FILES_DIALOG);
+
+ g_signal_connect(cs->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(cs->win, "destroy", G_CALLBACK(win_destroy_cb), cs);
+
+ gtk_widget_show_all(cs->win);
+ window_present(cs->win);
+
+ cf_retap_packets(&cfile);
+}
+
+static GtkWidget *dlg=NULL;
+static GtkWidget *filter_entry;
+
+static void
+comparestat_start_button_clicked(GtkWidget *item _U_, gpointer data _U_)
+{
+ GString *str;
+ const char *filter;
+
+ compare_start=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_start_int));
+ compare_stop=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin_stop_int));
+ compare_variance=gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin_var_int));
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radio_TTL))){
+ TTL_method=TRUE;
+ } else {
+ TTL_method=FALSE;
+ }
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radio_ON))){
+ ON_method=TRUE;
+ } else {
+ ON_method=FALSE;
+ }
+
+ str = g_string_new("compare");
+ g_string_append_printf(str, ",%d,%d,%d,%d,%lf",compare_start, compare_stop, TTL_method, ON_method, compare_variance);
+ filter=gtk_entry_get_text(GTK_ENTRY(filter_entry));
+ if(filter[0]!=0){
+ g_string_append_printf(str, ",%s", filter);
+ }
+
+ if(first_window){
+ first_window = FALSE;
+ gtk_comparestat_init(str->str,NULL);
+ } else {
+ report_failure("cannot open more than one compare of the same type at once");
+ }
+
+ g_string_free(str, TRUE);
+}
+
+static void
+dlg_destroy_cb(GtkWidget *w _U_, gpointer user_data _U_)
+{
+ dlg=NULL;
+}
+
+/* create and show first view of this module
+ */
+void
+gtk_comparestat_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ GtkAdjustment *start_integer, *stop_integer, *var_integer;
+ GtkWidget *dlg_box;
+ GtkWidget *spin_start_label, *spin_stop_label, *spin_start_box, *spin_stop_box;
+ GtkWidget *spin_var_box, *spin_var_label;
+ GtkWidget *order_box, *radio_MAC, *order_label;
+ GtkWidget *differ_box, *radio_OFF, *differ_label;
+ GtkWidget *filter_box, *filter_bt;
+ GtkWidget *bbox, *start_button, *cancel_button;
+ const char *filter;
+ static construct_args_t args = {
+ "Compare statistics",
+ TRUE,
+ FALSE,
+ FALSE
+ };
+
+ /* if the window is already open, bring it to front */
+ if(dlg){
+ gdk_window_raise(gtk_widget_get_window(dlg));
+ return;
+ }
+
+ dlg=dlg_window_new("Wireshark: Compare two capture files");
+ gtk_window_set_default_size(GTK_WINDOW(dlg), 300, -1);
+
+ dlg_box=gtk_vbox_new(FALSE, 10);
+ gtk_container_set_border_width(GTK_CONTAINER(dlg_box), 10);
+ gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
+ gtk_widget_show(dlg_box);
+
+ /* spin Box */
+ spin_start_box=gtk_hbox_new(FALSE, 10);
+ spin_stop_box=gtk_hbox_new(FALSE, 10);
+
+ /* spin label */
+ gtk_container_set_border_width(GTK_CONTAINER(spin_start_box), 1);
+ spin_start_label=gtk_label_new("Start compare:");
+ gtk_box_pack_start(GTK_BOX(spin_start_box), spin_start_label, FALSE, FALSE, 0);
+ gtk_widget_show(spin_start_label);
+ gtk_container_set_border_width(GTK_CONTAINER(spin_stop_box), 1);
+ spin_stop_label=gtk_label_new("Stop compare: ");
+ gtk_box_pack_start(GTK_BOX(spin_stop_box), spin_stop_label, FALSE, FALSE, 0);
+ gtk_widget_show(spin_stop_label);
+
+ /* create adjustments. Spans between 0 and 100, starting at 0 and
+ * moves in increments of 1 */
+ start_integer=GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 100.0, 1.0, 5.0, 0.0));
+ stop_integer=GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 100.0, 1.0, 5.0, 0.0));
+
+ /* create spin button. Not displaying decimal */
+ spin_start_int=gtk_spin_button_new(start_integer, 1.0, 0);
+ spin_stop_int=gtk_spin_button_new(stop_integer, 1.0, 0);
+
+ /* pack it up */
+ gtk_box_pack_start(GTK_BOX(spin_start_box), spin_start_int, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(spin_stop_box), spin_stop_int, TRUE, TRUE, 0);
+
+ gtk_box_pack_start(GTK_BOX(dlg_box), spin_start_box, FALSE, FALSE, 0);
+ gtk_widget_show(spin_start_box);
+ gtk_box_pack_start(GTK_BOX(dlg_box), spin_stop_box, FALSE, FALSE, 0);
+ gtk_widget_show(spin_stop_box);
+
+ /* differ Box */
+ differ_box=gtk_hbox_new(FALSE, 10);
+
+ /* radio label */
+ gtk_container_set_border_width(GTK_CONTAINER(differ_box), 1);
+ differ_label=gtk_label_new("Endpoint distinction:");
+ gtk_box_pack_start(GTK_BOX(differ_box), differ_label, FALSE, FALSE, 0);
+ gtk_widget_show(differ_label);
+
+ /* create radio buttons */
+ radio_MAC=gtk_radio_button_new_with_label (NULL, "MAC");
+ radio_TTL=gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(radio_MAC), "TTL");
+ gtk_box_pack_start(GTK_BOX(differ_box), radio_MAC, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(differ_box), radio_TTL, TRUE, TRUE, 0);
+ gtk_widget_show(radio_MAC);
+ gtk_widget_show(radio_TTL);
+
+ gtk_box_pack_start(GTK_BOX(dlg_box), differ_box, FALSE, FALSE, 0);
+ gtk_widget_show(differ_box);
+
+ /* order Box */
+ order_box=gtk_hbox_new(FALSE, 10);
+
+ /* order label */
+ gtk_container_set_border_width(GTK_CONTAINER(order_box), 1);
+ order_label=gtk_label_new("Check order: ");
+ gtk_box_pack_start(GTK_BOX(order_box), order_label, FALSE, FALSE, 0);
+ gtk_widget_show(order_label);
+
+ /* create radio buttons */
+ radio_ON=gtk_radio_button_new_with_label (NULL, "On");
+ radio_OFF=gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(radio_ON), "Off");
+ gtk_box_pack_start(GTK_BOX(order_box), radio_ON, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(order_box), radio_OFF, TRUE, TRUE, 0);
+ gtk_widget_show(radio_ON);
+ gtk_widget_show(radio_OFF);
+
+ gtk_box_pack_start(GTK_BOX(dlg_box), order_box, FALSE, FALSE, 0);
+ gtk_widget_show(order_box);
+
+ /* spin box */
+ spin_var_box=gtk_hbox_new(FALSE, 10);
+
+ /* spin label */
+ gtk_container_set_border_width(GTK_CONTAINER(spin_var_box), 1);
+ spin_var_label=gtk_label_new("Time variance (sec +/-):");
+ gtk_box_pack_start(GTK_BOX(spin_var_box), spin_var_label, FALSE, FALSE, 0);
+ gtk_widget_show(spin_var_label);
+
+ /* create adjustments. Spans between 0 and 100, starting at 0 and
+ * moves in increments of 1 */
+ var_integer=GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 100.0, 1.0, 5.0, 0.0));
+
+ /* create spin button. Not displaying decimal */
+ spin_var_int=gtk_spin_button_new(var_integer, 0.0, 2);
+
+ /* pack it up */
+ gtk_box_pack_start(GTK_BOX(spin_var_box), spin_var_int, TRUE, TRUE, 0);
+ gtk_widget_show(spin_var_int);
+
+ gtk_box_pack_start(GTK_BOX(dlg_box), spin_var_box, FALSE, FALSE, 0);
+ gtk_widget_show(spin_var_box);
+
+ /* filter box */
+ filter_box=gtk_hbox_new(FALSE, 3);
+
+ /* filter label */
+ filter_bt=gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
+ g_signal_connect(filter_bt, "clicked", G_CALLBACK(display_filter_construct_cb), &args);
+ gtk_box_pack_start(GTK_BOX(filter_box), filter_bt, FALSE, TRUE, 0);
+ gtk_widget_show(filter_bt);
+
+ /* filter entry */
+ filter_entry=gtk_entry_new();
+ g_signal_connect(filter_entry, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
+ g_object_set_data(G_OBJECT(filter_box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+ g_signal_connect(filter_entry, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
+ g_signal_connect(dlg, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
+
+ /* filter prefs dialog */
+ g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_entry);
+ /* filter prefs dialog */
+
+ gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, TRUE, TRUE, 0);
+ filter=gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
+ if(filter){
+ gtk_entry_set_text(GTK_ENTRY(filter_entry), filter);
+ }
+ gtk_widget_show(filter_entry);
+
+ gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
+ gtk_widget_show(filter_box);
+
+ /* button box */
+ bbox = dlg_button_row_new(WIRESHARK_STOCK_CREATE_STAT, GTK_STOCK_CANCEL, NULL);
+ gtk_box_pack_start(GTK_BOX(dlg_box), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+
+ start_button = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_CREATE_STAT);
+ g_signal_connect_swapped(start_button, "clicked", G_CALLBACK(comparestat_start_button_clicked), NULL);
+
+ cancel_button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ window_set_cancel_button(dlg, cancel_button, window_cancel_button_cb);
+
+ /* give the initial focus to the "filter" entry box. */
+ gtk_widget_grab_focus(filter_entry);
+
+ gtk_widget_grab_default(start_button );
+
+ g_signal_connect(dlg, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(dlg, "destroy", G_CALLBACK(dlg_destroy_cb), NULL);
+
+ gtk_widget_show_all(dlg);
+ window_present(dlg);
+}
+
+
+void
+register_tap_listener_gtkcomparestat(void)
+{
+ register_stat_cmd_arg("compare", gtk_comparestat_init, NULL);
+}
+
diff --git a/ui/gtk/conversations_eth.c b/ui/gtk/conversations_eth.c
new file mode 100644
index 0000000000..336578d2f0
--- /dev/null
+++ b/ui/gtk/conversations_eth.c
@@ -0,0 +1,86 @@
+/* conversations_eth.c
+ * conversations_eth 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-eth.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/conversations_table.h"
+
+static int
+eth_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ const eth_hdr *ehdr=vip;
+
+ add_conversation_table_data((conversations_table *)pct, &ehdr->src, &ehdr->dst, 0, 0, 1, pinfo->fd->pkt_len, &pinfo->fd->rel_ts, SAT_ETHER, PT_NONE);
+
+ return 1;
+}
+
+
+
+static void
+eth_conversation_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"conv,eth,",9)){
+ filter=optarg+9;
+ } else {
+ filter=NULL;
+ }
+
+ init_conversation_table(TRUE, "Ethernet", "eth", filter, eth_conversation_packet);
+
+}
+
+void
+eth_endpoints_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ eth_conversation_init("conv,eth",NULL);
+}
+
+void
+register_tap_listener_eth_conversation(void)
+{
+ register_stat_cmd_arg("conv,eth", eth_conversation_init,NULL);
+ register_conversation_table(TRUE, "Ethernet", "eth", NULL /*filter*/, eth_conversation_packet);
+}
+
diff --git a/ui/gtk/conversations_fc.c b/ui/gtk/conversations_fc.c
new file mode 100644
index 0000000000..f1e0b3c58f
--- /dev/null
+++ b/ui/gtk/conversations_fc.c
@@ -0,0 +1,87 @@
+/* conversations_fc.c
+ * conversations_fc 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/conversation.h>
+#include <epan/dissectors/packet-scsi.h>
+#include <epan/dissectors/packet-fc.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/conversations_table.h"
+
+
+static int
+fc_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ const fc_hdr *fchdr=vip;
+
+ add_conversation_table_data((conversations_table *)pct, &fchdr->s_id, &fchdr->d_id, 0, 0, 1, pinfo->fd->pkt_len, &pinfo->fd->rel_ts, SAT_NONE, PT_NONE);
+
+ return 1;
+}
+
+
+
+static void
+fc_conversation_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"conv,fc,",8)){
+ filter=optarg+8;
+ } else {
+ filter=NULL;
+ }
+
+ init_conversation_table(TRUE, "Fibre Channel", "fc", filter, fc_conversation_packet);
+
+}
+
+void
+fc_endpoints_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ fc_conversation_init("conv,fc",NULL);
+}
+
+void
+register_tap_listener_fc_conversation(void)
+{
+ register_stat_cmd_arg("conv,fc", fc_conversation_init, NULL);
+ register_conversation_table(TRUE, "Fibre Channel", "fc", NULL /*filter*/, fc_conversation_packet);
+}
diff --git a/ui/gtk/conversations_fddi.c b/ui/gtk/conversations_fddi.c
new file mode 100644
index 0000000000..fcd721b6a1
--- /dev/null
+++ b/ui/gtk/conversations_fddi.c
@@ -0,0 +1,84 @@
+/* conversations_fddi.c
+ * conversations_fddi 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/packet.h"
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-fddi.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/conversations_table.h"
+
+static int
+fddi_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ const fddi_hdr *ehdr=vip;
+
+ add_conversation_table_data((conversations_table *)pct, &ehdr->src, &ehdr->dst, 0, 0, 1, pinfo->fd->pkt_len, &pinfo->fd->rel_ts, SAT_FDDI, PT_NONE);
+
+ return 1;
+}
+
+
+
+static void
+fddi_conversation_init(const char *optarg, void* userdata _U_ )
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"conv,fddi,",10)){
+ filter=optarg+10;
+ } else {
+ filter=NULL;
+ }
+
+ init_conversation_table(TRUE, "FDDI", "fddi", filter, fddi_conversation_packet);
+
+}
+
+void
+fddi_endpoints_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ fddi_conversation_init("conv,fddi",NULL);
+}
+
+void
+register_tap_listener_fddi_conversation(void)
+{
+ register_stat_cmd_arg("conv,fddi", fddi_conversation_init,NULL);
+ register_conversation_table(TRUE, "FDDI", "fddi", NULL /*filter*/, fddi_conversation_packet);
+}
diff --git a/ui/gtk/conversations_ip.c b/ui/gtk/conversations_ip.c
new file mode 100644
index 0000000000..d77fb5c6a8
--- /dev/null
+++ b/ui/gtk/conversations_ip.c
@@ -0,0 +1,82 @@
+/* conversations_ip.c
+ * conversations_ip 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-ip.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/conversations_table.h"
+
+static int
+ip_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ const ws_ip *iph=vip;
+
+ add_conversation_table_data((conversations_table *)pct, &iph->ip_src, &iph->ip_dst, 0, 0, 1, pinfo->fd->pkt_len, &pinfo->fd->rel_ts, SAT_NONE, PT_NONE);
+
+ return 1;
+}
+
+static void
+ip_conversation_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"conv,ip,",8)){
+ filter=optarg+8;
+ } else {
+ filter=NULL;
+ }
+
+ init_conversation_table(TRUE, "IPv4", "ip", filter, ip_conversation_packet);
+
+}
+
+void
+ip_endpoints_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ ip_conversation_init("conv,ip",NULL);
+}
+
+void
+register_tap_listener_ip_conversation(void)
+{
+ register_stat_cmd_arg("conv,ip", ip_conversation_init,NULL);
+ register_conversation_table(TRUE, "IPv4", "ip", NULL /*filter*/, ip_conversation_packet);
+}
diff --git a/ui/gtk/conversations_ipv6.c b/ui/gtk/conversations_ipv6.c
new file mode 100644
index 0000000000..83e9585e42
--- /dev/null
+++ b/ui/gtk/conversations_ipv6.c
@@ -0,0 +1,91 @@
+/* conversations_ipv6.c 2009 Clif Bratcher
+ * Modified from conversations_ip 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-ipv6.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/conversations_table.h"
+
+static int
+ipv6_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ const struct ip6_hdr *ip6h = vip;
+ address src;
+ address dst;
+
+ /* Addresses aren't implemented as 'address' type in struct ip6_hdr */
+ src.type = dst.type = AT_IPv6;
+ src.len = dst.len = sizeof(struct e_in6_addr);
+ src.data = &ip6h->ip6_src;
+ dst.data = &ip6h->ip6_dst;
+
+ add_conversation_table_data((conversations_table *)pct, &src, &dst, 0, 0, 1, pinfo->fd->pkt_len, &pinfo->fd->rel_ts, SAT_NONE, PT_NONE);
+
+ return 1;
+}
+
+
+static void
+ipv6_conversation_init(const char *optarg, void *userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg, "conv,ipv6,", 10)) {
+ filter = optarg + 10;
+ }
+ else {
+ filter = NULL;
+ }
+
+ init_conversation_table(TRUE, "IPv6", "ipv6", filter, ipv6_conversation_packet);
+}
+
+void
+ipv6_endpoints_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ ipv6_conversation_init("conv,ipv6", NULL);
+}
+
+void
+register_tap_listener_ipv6_conversation(void)
+{
+ register_stat_cmd_arg("conv,ipv6", ipv6_conversation_init, NULL);
+ register_conversation_table(TRUE, "IPv6", "ipv6", NULL /*filter*/, ipv6_conversation_packet);
+}
diff --git a/ui/gtk/conversations_ipx.c b/ui/gtk/conversations_ipx.c
new file mode 100644
index 0000000000..481e5a6d48
--- /dev/null
+++ b/ui/gtk/conversations_ipx.c
@@ -0,0 +1,85 @@
+/* conversations_ipx.c
+ * conversations_ipx 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-ipx.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/conversations_table.h"
+
+
+static int
+ipx_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ const ipxhdr_t *ipxh=vip;
+
+ add_conversation_table_data((conversations_table *)pct, &ipxh->ipx_src, &ipxh->ipx_dst, 0, 0, 1, pinfo->fd->pkt_len, &pinfo->fd->rel_ts, SAT_NONE, PT_NONE);
+
+ return 1;
+}
+
+
+
+static void
+ipx_conversation_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"conv,ipx,",9)){
+ filter=optarg+9;
+ } else {
+ filter=NULL;
+ }
+
+ init_conversation_table(TRUE, "IPX", "ipx", filter, ipx_conversation_packet);
+
+}
+
+void
+ipx_endpoints_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ ipx_conversation_init("conv,ipx",NULL);
+}
+
+void
+register_tap_listener_ipx_conversation(void)
+{
+ register_stat_cmd_arg("conv,ipx", ipx_conversation_init,NULL);
+ register_conversation_table(TRUE, "IPX", "ipx", NULL /*filter*/, ipx_conversation_packet);
+}
diff --git a/ui/gtk/conversations_jxta.c b/ui/gtk/conversations_jxta.c
new file mode 100644
index 0000000000..e56ac0e118
--- /dev/null
+++ b/ui/gtk/conversations_jxta.c
@@ -0,0 +1,93 @@
+/* conversations_jxta.c
+ * conversations_jxta copyright (c) 2005 Mike Duigou <bondolo@jxta.org>
+ * copied from conversations_sctp.c
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-jxta.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/conversations_table.h"
+
+static int
+jxta_conversation_packet(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip)
+{
+ const jxta_tap_header *jxtahdr = (const jxta_tap_header *) vip;
+
+ add_conversation_table_data((conversations_table *)pct,
+ &jxtahdr->src_address,
+ &jxtahdr->dest_address,
+ 0,
+ 0,
+ 1,
+ jxtahdr->size,
+ NULL,
+ SAT_JXTA,
+ PT_NONE);
+
+
+ return 1;
+}
+
+static void
+jxta_conversation_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"conv,jxta,",10)){
+ filter=optarg+10;
+ } else {
+ filter=NULL;
+ }
+
+ init_conversation_table(TRUE, "JXTA", "jxta", filter, jxta_conversation_packet);
+
+}
+
+void
+jxta_conversation_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ jxta_conversation_init("conv,jxta",NULL);
+}
+
+void
+register_tap_listener_jxta_conversation(void)
+{
+ register_stat_cmd_arg("conv,jxta", jxta_conversation_init,NULL);
+ register_conversation_table(TRUE, "JXTA", "jxta", NULL /*filter*/, jxta_conversation_packet);
+}
diff --git a/ui/gtk/conversations_ncp.c b/ui/gtk/conversations_ncp.c
new file mode 100644
index 0000000000..058da489ab
--- /dev/null
+++ b/ui/gtk/conversations_ncp.c
@@ -0,0 +1,85 @@
+/* conversations_ncp.c 2005 Greg Morris
+ * modified from conversations_eth.c 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-ncp-int.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/conversations_table.h"
+
+static int
+ncp_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ const struct ncp_common_header *ncph=vip;
+ guint32 connection;
+
+ connection = (ncph->conn_high * 256)+ncph->conn_low;
+ if (connection < 65535) {
+ add_conversation_table_data((conversations_table *)pct, &pinfo->src, &pinfo->dst, connection, connection, 1, pinfo->fd->pkt_len, &pinfo->fd->rel_ts, SAT_NONE, PT_NCP);
+ }
+
+ return 1;
+}
+
+static void
+ncp_conversation_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"conv,ncp,",9)){
+ filter=optarg+9;
+ } else {
+ filter=NULL;
+ }
+
+ init_conversation_table(FALSE, "NCP", "ncp_hdr", filter, ncp_conversation_packet);
+}
+
+void
+ncp_endpoints_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ ncp_conversation_init("conv,ncp",NULL);
+}
+
+void
+register_tap_listener_ncp_conversation(void)
+{
+ register_stat_cmd_arg("conv,ncp", ncp_conversation_init,NULL);
+ register_conversation_table(FALSE, "NCP", "ncp_hdr", NULL /*filter*/, ncp_conversation_packet);
+}
diff --git a/ui/gtk/conversations_rsvp.c b/ui/gtk/conversations_rsvp.c
new file mode 100644
index 0000000000..a5ddf2b3c8
--- /dev/null
+++ b/ui/gtk/conversations_rsvp.c
@@ -0,0 +1,86 @@
+/* conversations_rsvp.c
+ * conversations_rsvp.c August 2005, Manu Pathak <mapathak@cisco.com>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-rsvp.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/conversations_table.h"
+
+static int
+rsvp_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ const rsvp_conversation_info *rsvph = vip;
+
+ add_conversation_table_data((conversations_table *)pct,
+ &rsvph->source, &rsvph->destination, 0, 0, 1,
+ pinfo->fd->pkt_len, &pinfo->fd->rel_ts, SAT_NONE, PT_NONE);
+
+ return 1;
+}
+
+static void
+rsvp_conversation_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"conv,rsvp,",10)){
+ filter=optarg+10;
+ } else {
+ filter=NULL;
+ }
+
+ init_conversation_table(TRUE, "RSVP", "rsvp", filter,
+ rsvp_conversation_packet);
+
+}
+
+void
+rsvp_endpoints_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ rsvp_conversation_init("conv,rsvp",NULL);
+}
+
+void
+register_tap_listener_rsvp_conversation(void)
+{
+ register_stat_cmd_arg("conv,rsvp", rsvp_conversation_init,NULL);
+ register_conversation_table(TRUE, "RSVP", "rsvp", NULL /*filter*/,
+ rsvp_conversation_packet);
+}
diff --git a/ui/gtk/conversations_sctp.c b/ui/gtk/conversations_sctp.c
new file mode 100644
index 0000000000..e7c5379ea5
--- /dev/null
+++ b/ui/gtk/conversations_sctp.c
@@ -0,0 +1,94 @@
+/* conversations_sctp.c
+ * conversations_sctp 2005 Oleg Terletsky <oleg.terletsky@comverse.com>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-sctp.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/conversations_table.h"
+
+static int
+sctp_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ const struct _sctp_info *sctphdr=vip;
+
+ add_conversation_table_data((conversations_table *)pct,
+ &sctphdr->ip_src,
+ &sctphdr->ip_dst,
+ sctphdr->sport,
+ sctphdr->dport,
+ 1,
+ pinfo->fd->pkt_len,
+ &pinfo->fd->rel_ts,
+ SAT_NONE,
+ PT_SCTP);
+
+
+ return 1;
+}
+
+
+
+static void
+sctp_conversation_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"conv,sctp,",10)){
+ filter=optarg+10;
+ } else {
+ filter=NULL;
+ }
+
+ init_conversation_table(FALSE, "SCTP", "sctp", filter, sctp_conversation_packet);
+
+}
+
+void
+sctp_conversation_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ sctp_conversation_init("conv,sctp",NULL);
+}
+
+void
+register_tap_listener_sctp_conversation(void)
+{
+ register_stat_cmd_arg("conv,sctp", sctp_conversation_init,NULL);
+ register_conversation_table(FALSE, "SCTP", "sctp", NULL /*filter*/, sctp_conversation_packet);
+}
diff --git a/ui/gtk/conversations_table.c b/ui/gtk/conversations_table.c
new file mode 100644
index 0000000000..5573b8162a
--- /dev/null
+++ b/ui/gtk/conversations_table.c
@@ -0,0 +1,3016 @@
+/* conversations_table.c
+ * conversations_table 2003 Ronnie Sahlberg
+ * Helper routines common to all endpoint conversations tap.
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <locale.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/to_str.h>
+#include <epan/address.h>
+#include <epan/addr_resolv.h>
+#include <epan/tap.h>
+#include <epan/nstime.h>
+
+#include "../simple_dialog.h"
+#include "../globals.h"
+
+#include "ui/gtk/sat.h"
+#include "ui/gtk/conversations_table.h"
+#include "ui/gtk/filter_utils.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/follow_tcp.h"
+#include "ui/gtk/follow_udp.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/utf8_entities.h"
+
+#include "old-gtk-compat.h"
+
+#define COL_STR_LEN 16
+#define CONV_PTR_KEY "conversations-pointer"
+#define NB_PAGES_KEY "notebook-pages"
+#define FOLLOW_STREAM_BT_KEY "follow-stream-button"
+#define NO_BPS_STR "N/A"
+
+#define CMP_NUM(n1, n2) \
+ if ((n1) > (n2)) \
+ return 1; \
+ else if ((n1) < (n2)) \
+ return -1; \
+ else \
+ return 0;
+
+/* convert a port number into a string */
+static char *
+ct_port_to_str(int port_type_val, guint32 port)
+{
+ static int i=0;
+ static gchar *strp, str[4][12];
+ gchar *bp;
+
+ strp=str[i];
+
+ switch(port_type_val){
+ case PT_TCP:
+ case PT_UDP:
+ case PT_SCTP:
+ case PT_NCP:
+ i = (i+1)%4;
+ bp = &strp[11];
+
+ *bp = 0;
+ do {
+ *--bp = (port % 10) +'0';
+ } while ((port /= 10) != 0 && bp > strp);
+ return bp;
+ }
+ return NULL;
+}
+
+#define FN_SRC_ADDRESS 0
+#define FN_DST_ADDRESS 1
+#define FN_ANY_ADDRESS 2
+#define FN_SRC_PORT 3
+#define FN_DST_PORT 4
+#define FN_ANY_PORT 5
+/* given an address (to distinguish between ipv4 and ipv6 for tcp/udp),
+ a port_type and a name_type (FN_...)
+ return a string for the filter name.
+
+ Some addresses, like AT_ETHER may actually be any of multiple types
+ of protocols, either ethernet, tokenring, fddi, wlan etc so we must be
+ more specific there; that's why we need specific_addr_type.
+*/
+static const char *
+ct_get_filter_name(address *addr, int specific_addr_type_val, int port_type_val, int name_type_val)
+{
+ switch(name_type_val){
+ case FN_SRC_ADDRESS:
+ switch(addr->type){
+ case AT_ETHER:
+ switch(specific_addr_type_val){
+ case SAT_ETHER:
+ return "eth.src";
+ case SAT_WLAN:
+ return "wlan.sa";
+ case SAT_FDDI:
+ return "fddi.src";
+ case SAT_TOKENRING:
+ return "tr.src";
+ default:
+ break;
+ }
+ break;
+ case AT_IPv4:
+ return "ip.src";
+ case AT_IPv6:
+ return "ipv6.src";
+ case AT_IPX:
+ return "ipx.src";
+ case AT_FC:
+ return "fc.s_id";
+ case AT_URI:
+ switch(specific_addr_type_val){
+ case SAT_JXTA:
+ return "jxta.message.src";
+ default:
+ break;
+ }
+ break;
+ case AT_USB:
+ return "usb.sa";
+ default:
+ break;
+ }
+ break;
+ case FN_DST_ADDRESS:
+ switch(addr->type){
+ case AT_ETHER:
+ switch(specific_addr_type_val){
+ case SAT_ETHER:
+ return "eth.dst";
+ case SAT_WLAN:
+ return "wlan.da";
+ case SAT_FDDI:
+ return "fddi.dst";
+ case SAT_TOKENRING:
+ return "tr.dst";
+ default:
+ break;
+ }
+ break;
+ case AT_IPv4:
+ return "ip.dst";
+ case AT_IPv6:
+ return "ipv6.dst";
+ case AT_IPX:
+ return "ipx.dst";
+ case AT_FC:
+ return "fc.d_id";
+ case AT_URI:
+ switch(specific_addr_type_val){
+ case SAT_JXTA:
+ return "jxta.message.dst";
+ default:
+ break;
+ }
+ break;
+ case AT_USB:
+ return "usb.da";
+ default:
+ break;
+ }
+ break;
+ case FN_ANY_ADDRESS:
+ switch(addr->type){
+ case AT_ETHER:
+ switch(specific_addr_type_val){
+ case SAT_ETHER:
+ return "eth.addr";
+ case SAT_WLAN:
+ return "wlan.addr";
+ case SAT_FDDI:
+ return "fddi.addr";
+ case SAT_TOKENRING:
+ return "tr.addr";
+ default:
+ break;
+ }
+ break;
+ case AT_IPv4:
+ return "ip.addr";
+ case AT_IPv6:
+ return "ipv6.addr";
+ case AT_IPX:
+ return "ipx.addr";
+ case AT_FC:
+ return "fc.id";
+ case AT_URI:
+ switch(specific_addr_type_val){
+ case SAT_JXTA:
+ return "jxta.message.address";
+ default:
+ break;
+ }
+ break;
+ case AT_USB:
+ return "usb.addr";
+ default:
+ break;
+ }
+ break;
+ case FN_SRC_PORT:
+ switch(port_type_val){
+ case PT_TCP:
+ return "tcp.srcport";
+ case PT_UDP:
+ return "udp.srcport";
+ case PT_SCTP:
+ return "sctp.srcport";
+ case PT_NCP:
+ return "ncp.connection";
+ default:
+ break;
+ }
+ break;
+ case FN_DST_PORT:
+ switch(port_type_val){
+ case PT_TCP:
+ return "tcp.dstport";
+ case PT_UDP:
+ return "udp.dstport";
+ case PT_SCTP:
+ return "sctp.dstport";
+ case PT_NCP:
+ return "ncp.connection";
+ default:
+ break;
+ }
+ break;
+ case FN_ANY_PORT:
+ switch(port_type_val){
+ case PT_TCP:
+ return "tcp.port";
+ case PT_UDP:
+ return "udp.port";
+ case PT_SCTP:
+ return "sctp.port";
+ case PT_NCP:
+ return "ncp.connection";
+ default:
+ break;
+ }
+ break;
+ }
+
+ g_assert_not_reached();
+ return NULL;
+}
+
+static void
+reset_ct_table_data(conversations_table *ct)
+{
+ guint32 i;
+ char title[256];
+ GString *error_string;
+ const char *filter;
+ GtkListStore *store;
+
+ if (ct->use_dfilter) {
+ filter = gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
+ } else {
+ filter = ct->filter;
+ }
+
+ error_string = set_tap_dfilter (ct, filter);
+ if (error_string) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ return;
+ }
+
+ if(ct->page_lb) {
+ g_snprintf(title, sizeof(title), "Conversations: %s", cf_get_display_name(&cfile));
+ gtk_window_set_title(GTK_WINDOW(ct->win), title);
+ g_snprintf(title, sizeof(title), "%s", ct->name);
+ gtk_label_set_text(GTK_LABEL(ct->page_lb), title);
+ gtk_widget_set_sensitive(ct->page_lb, FALSE);
+
+ if (ct->use_dfilter) {
+ if (filter && strlen(filter)) {
+ g_snprintf(title, sizeof(title), "%s Conversations - Filter: %s", ct->name, filter);
+ } else {
+ g_snprintf(title, sizeof(title), "%s Conversations - No Filter", ct->name);
+ }
+ } else {
+ g_snprintf(title, sizeof(title), "%s Conversations", ct->name);
+ }
+ gtk_label_set_text(GTK_LABEL(ct->name_lb), title);
+ } else {
+ g_snprintf(title, sizeof(title), "%s Conversations: %s", ct->name, cf_get_display_name(&cfile));
+ gtk_window_set_title(GTK_WINDOW(ct->win), title);
+ }
+
+ /* remove all entries from the list */
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ct->table)));
+ gtk_list_store_clear(store);
+
+ /* delete all conversations */
+ for(i=0;i<ct->num_conversations;i++){
+ conv_t *conv = &g_array_index(ct->conversations, conv_t, i);
+ g_free((gpointer)conv->src_address.data);
+ g_free((gpointer)conv->dst_address.data);
+ }
+ if (ct->conversations)
+ g_array_free(ct->conversations, TRUE);
+
+ if (ct->hashtable != NULL)
+ g_hash_table_destroy(ct->hashtable);
+
+ ct->conversations=NULL;
+ ct->hashtable=NULL;
+ ct->num_conversations=0;
+}
+
+static void
+reset_ct_table_data_cb(void *arg)
+{
+ reset_ct_table_data(arg);
+}
+
+static void
+ct_win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ conversations_table *conversations=(conversations_table *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(conversations);
+ unprotect_thread_critical_region();
+
+ reset_ct_table_data(conversations);
+ g_free(conversations);
+}
+
+enum
+{
+ SRC_ADR_COLUMN,
+ SRC_PORT_COLUMN,
+ DST_ADR_COLUMN,
+ DST_PORT_COLUMN,
+ PACKETS_COLUMN,
+ BYTES_COLUMN,
+ PKT_AB_COLUMN,
+ BYTES_AB_COLUMN,
+ PKT_BA_COLUMN,
+ BYTES_BA_COLUMN,
+ START_COLUMN,
+ DURATION_COLUMN,
+ BPS_AB_COLUMN,
+ BPS_BA_COLUMN,
+ INDEX_COLUMN,
+ N_COLUMNS
+};
+
+static gint
+ct_sort_func(GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ guint32 idx1, idx2;
+ /* The col to get data from is in userdata */
+ gint data_column = GPOINTER_TO_INT(user_data);
+
+ conversations_table *ct = g_object_get_data(G_OBJECT(model), CONV_PTR_KEY);
+ conv_t *conv1 = NULL;
+ conv_t *conv2 = NULL;
+ double duration1, duration2;
+
+ gtk_tree_model_get(model, a, INDEX_COLUMN, &idx1, -1);
+ gtk_tree_model_get(model, b, INDEX_COLUMN, &idx2, -1);
+
+ if (!ct || idx1 >= ct->num_conversations || idx2 >= ct->num_conversations)
+ return 0;
+
+ conv1 = &g_array_index(ct->conversations, conv_t, idx1);
+ conv2 = &g_array_index(ct->conversations, conv_t, idx2);
+
+
+ switch(data_column){
+ case SRC_ADR_COLUMN: /* Source address */
+ return(CMP_ADDRESS(&conv1->src_address, &conv2->src_address));
+ case DST_ADR_COLUMN: /* Destination address */
+ return(CMP_ADDRESS(&conv1->dst_address, &conv2->dst_address));
+ case SRC_PORT_COLUMN: /* Source port */
+ CMP_NUM(conv1->src_port, conv2->src_port);
+ case DST_PORT_COLUMN: /* Destination port */
+ CMP_NUM(conv1->dst_port, conv2->dst_port);
+ case START_COLUMN: /* Start time */
+ return nstime_cmp(&conv1->start_time, &conv2->start_time);
+ }
+
+ duration1 = nstime_to_sec(&conv1->stop_time) - nstime_to_sec(&conv1->start_time);
+ duration2 = nstime_to_sec(&conv2->stop_time) - nstime_to_sec(&conv2->start_time);
+
+ switch(data_column){
+ case DURATION_COLUMN: /* Duration */
+ CMP_NUM(duration1, duration2);
+ case BPS_AB_COLUMN: /* bps A->B */
+ if (duration1 > 0 && conv1->tx_frames > 1 && duration2 > 0 && conv2->tx_frames > 1) {
+ CMP_NUM((gint64) conv1->tx_bytes / duration1, (gint64) conv2->tx_bytes / duration2);
+ } else {
+ CMP_NUM(conv1->tx_bytes, conv2->tx_bytes);
+ }
+ case BPS_BA_COLUMN: /* bps A<-B */
+ if (duration1 > 0 && conv1->rx_frames > 1 && duration2 > 0 && conv2->rx_frames > 1) {
+ CMP_NUM((gint64) conv1->rx_bytes / duration1, (gint64) conv2->rx_bytes / duration2);
+ } else {
+ CMP_NUM(conv1->rx_bytes, conv2->rx_bytes);
+ }
+ default:
+ g_assert_not_reached();
+ }
+
+ return 0;
+}
+
+/* Filter direction */
+#define DIR_A_TO_FROM_B 0
+#define DIR_A_TO_B 1
+#define DIR_A_FROM_B 2
+#define DIR_A_TO_FROM_ANY 3
+#define DIR_A_TO_ANY 4
+#define DIR_A_FROM_ANY 5
+#define DIR_ANY_TO_FROM_B 6
+#define DIR_ANY_FROM_B 7
+#define DIR_ANY_TO_B 8
+
+static void
+ct_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callback_action)
+{
+ int direction;
+ guint32 idx = 0;
+ conversations_table *ct = (conversations_table *)callback_data;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreeSelection *sel;
+ char *str = NULL;
+ char *sport, *dport;
+ conv_t *conv;
+
+ direction=FILTER_EXTRA(callback_action);
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(ct->table));
+ if (!gtk_tree_selection_get_selected(sel, &model, &iter))
+ return;
+
+ gtk_tree_model_get (model, &iter,
+ INDEX_COLUMN, &idx,
+ -1);
+
+ if(idx>= ct->num_conversations){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No conversation selected");
+ return;
+ }
+ conv = &g_array_index(ct->conversations, conv_t, idx);
+ sport=ct_port_to_str(conv->port_type, conv->src_port);
+ dport=ct_port_to_str(conv->port_type, conv->dst_port);
+
+ switch(direction){
+ case DIR_A_TO_FROM_B:
+ /* A <-> B */
+ str = g_strdup_printf("%s==%s%s%s%s%s && %s==%s%s%s%s%s",
+ ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_ANY_ADDRESS),
+ ep_address_to_str(&conv->src_address),
+ sport?" && ":"",
+ sport?ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_ANY_PORT):"",
+ sport?"==":"",
+ sport?sport:"",
+ ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_ANY_ADDRESS),
+ ep_address_to_str(&conv->dst_address),
+ dport?" && ":"",
+ dport?ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_ANY_PORT):"",
+ dport?"==":"",
+ dport?dport:""
+ );
+ break;
+ case DIR_A_TO_B:
+ /* A --> B */
+ str = g_strdup_printf("%s==%s%s%s%s%s && %s==%s%s%s%s%s",
+ ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_SRC_ADDRESS),
+ ep_address_to_str(&conv->src_address),
+ sport?" && ":"",
+ sport?ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_SRC_PORT):"",
+ sport?"==":"",
+ sport?sport:"",
+ ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_DST_ADDRESS),
+ ep_address_to_str(&conv->dst_address),
+ dport?" && ":"",
+ dport?ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_DST_PORT):"",
+ dport?"==":"",
+ dport?dport:""
+ );
+ break;
+ case DIR_A_FROM_B:
+ /* A <-- B */
+ str = g_strdup_printf("%s==%s%s%s%s%s && %s==%s%s%s%s%s",
+ ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_DST_ADDRESS),
+ ep_address_to_str(&conv->src_address),
+ sport?" && ":"",
+ sport?ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_DST_PORT):"",
+ sport?"==":"",
+ sport?sport:"",
+ ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_SRC_ADDRESS),
+ ep_address_to_str(&conv->dst_address),
+ dport?" && ":"",
+ dport?ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_SRC_PORT):"",
+ dport?"==":"",
+ dport?dport:""
+ );
+ break;
+ case DIR_A_TO_FROM_ANY:
+ /* A <-> ANY */
+ str = g_strdup_printf("%s==%s%s%s%s%s",
+ ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_ANY_ADDRESS),
+ ep_address_to_str(&conv->src_address),
+ sport?" && ":"",
+ sport?ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_ANY_PORT):"",
+ sport?"==":"",
+ sport?sport:""
+ );
+ break;
+ case DIR_A_TO_ANY:
+ /* A --> ANY */
+ str = g_strdup_printf("%s==%s%s%s%s%s",
+ ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_SRC_ADDRESS),
+ ep_address_to_str(&conv->src_address),
+ sport?" && ":"",
+ sport?ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_SRC_PORT):"",
+ sport?"==":"",
+ sport?sport:""
+ );
+ break;
+ case DIR_A_FROM_ANY:
+ /* A <-- ANY */
+ str = g_strdup_printf("%s==%s%s%s%s%s",
+ ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_DST_ADDRESS),
+ ep_address_to_str(&conv->src_address),
+ sport?" && ":"",
+ sport?ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_DST_PORT):"",
+ sport?"==":"",
+ sport?sport:""
+ );
+ break;
+ case DIR_ANY_TO_FROM_B:
+ /* ANY <-> B */
+ str = g_strdup_printf("%s==%s%s%s%s%s",
+ ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_ANY_ADDRESS),
+ ep_address_to_str(&conv->dst_address),
+ dport?" && ":"",
+ dport?ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_ANY_PORT):"",
+ dport?"==":"",
+ dport?dport:""
+ );
+ break;
+ case DIR_ANY_FROM_B:
+ /* ANY <-- B */
+ str = g_strdup_printf("%s==%s%s%s%s%s",
+ ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_SRC_ADDRESS),
+ ep_address_to_str(&conv->dst_address),
+ dport?" && ":"",
+ dport?ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_SRC_PORT):"",
+ dport?"==":"",
+ dport?dport:""
+ );
+ break;
+ case DIR_ANY_TO_B:
+ /* ANY --> B */
+ str = g_strdup_printf("%s==%s%s%s%s%s",
+ ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_DST_ADDRESS),
+ ep_address_to_str(&conv->dst_address),
+ dport?" && ":"",
+ dport?ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_DST_PORT):"",
+ dport?"==":"",
+ dport?dport:""
+ );
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ apply_selected_filter (callback_action, str);
+
+ g_free (str);
+}
+
+static gboolean
+ct_show_popup_menu_cb(void *widg _U_, GdkEvent *event, conversations_table *ct)
+{
+ GdkEventButton *bevent = (GdkEventButton *)event;
+
+ if(event->type==GDK_BUTTON_PRESS && bevent->button==3){
+ gtk_menu_popup(GTK_MENU(ct->menu), NULL, NULL, NULL, NULL,
+ bevent->button, bevent->time);
+ }
+
+ return FALSE;
+}
+
+
+/* As Selected */
+static void
+conv_apply_as_selected_AtofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_SELECTED, DIR_A_TO_FROM_B));
+}
+
+static void
+conv_apply_as_selected_AtoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_SELECTED, DIR_A_TO_B));
+}
+
+static void
+conv_apply_as_selected_AfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_SELECTED, DIR_A_FROM_B));
+}
+
+static void
+conv_apply_as_selected_AtofromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_SELECTED, DIR_A_TO_FROM_ANY));
+}
+
+static void
+conv_apply_as_selected_AtoAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_SELECTED, DIR_A_TO_ANY));
+}
+
+static void
+conv_apply_as_selected_AfromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_SELECTED, DIR_A_FROM_ANY));
+}
+
+static void
+conv_apply_as_selected_AnytofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_SELECTED, DIR_ANY_TO_FROM_B));
+}
+
+
+static void
+conv_apply_as_selected_AnyfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_SELECTED, DIR_ANY_FROM_B));
+}
+
+static void
+conv_apply_as_selected_AnytoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_SELECTED, DIR_ANY_TO_B));
+}
+
+/* As Not Selected */
+static void
+conv_apply_as_not_selected_AtofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_A_TO_FROM_B));
+}
+
+static void
+conv_apply_as_not_selected_AtoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_A_TO_B));
+}
+
+static void
+conv_apply_as_not_selected_AfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_A_FROM_B));
+}
+
+static void
+conv_apply_as_not_selected_AtofromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_A_TO_FROM_ANY));
+}
+
+static void
+conv_apply_as_not_selected_AtoAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_A_TO_ANY));
+}
+
+static void
+conv_apply_as_not_selected_AfromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_A_FROM_ANY));
+}
+
+static void
+conv_apply_as_not_selected_AnytofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_ANY_TO_FROM_B));
+}
+
+static void
+conv_apply_as_not_selected_AnyfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_ANY_FROM_B));
+}
+
+static void
+conv_apply_as_not_selected_AnytoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_NOT_SELECTED, DIR_ANY_TO_B));
+}
+
+/* And Selected */
+static void
+conv_apply_and_selected_AtofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_A_TO_FROM_B));
+}
+
+static void
+conv_apply_and_selected_AtoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_A_TO_B));
+}
+
+static void
+conv_apply_and_selected_AfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_A_FROM_B));
+}
+
+static void
+conv_apply_and_selected_AtofromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_A_TO_FROM_ANY));
+}
+
+static void
+conv_apply_and_selected_AtoAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_A_TO_ANY));
+}
+
+static void
+conv_apply_and_selected_AfromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_A_FROM_ANY));
+}
+
+static void
+conv_apply_and_selected_AnytofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_ANY_TO_FROM_B));
+}
+
+static void
+conv_apply_and_selected_AnyfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_ANY_FROM_B));
+}
+
+static void
+conv_apply_and_selected_AnytoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_AND_SELECTED, DIR_ANY_TO_B));
+}
+
+/* Or Selected */
+static void
+conv_apply_or_selected_AtofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_A_TO_FROM_B));
+}
+
+static void
+conv_apply_or_selected_AtoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_A_TO_B));
+}
+
+static void
+conv_apply_or_selected_AfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_A_FROM_B));
+}
+
+static void
+conv_apply_or_selected_AtofromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_A_TO_FROM_ANY));
+}
+
+static void
+conv_apply_or_selected_AtoAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_A_TO_ANY));
+}
+
+static void
+conv_apply_or_selected_AfromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_A_FROM_ANY));
+}
+
+static void
+conv_apply_or_selected_AnytofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_ANY_TO_FROM_B));
+}
+
+static void
+conv_apply_or_selected_AnyfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_ANY_FROM_B));
+}
+
+static void
+conv_apply_or_selected_AnytoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_OR_SELECTED, DIR_ANY_TO_B));
+}
+
+/* And Not Selected */
+static void
+conv_apply_and_not_selected_AtofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_FROM_B));
+}
+
+static void
+conv_apply_and_not_selected_AtoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_B));
+}
+
+static void
+conv_apply_and_not_selected_AfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_A_FROM_B));
+}
+
+static void
+conv_apply_and_not_selected_AtofromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_FROM_ANY));
+}
+
+static void
+conv_apply_and_not_selected_AtoAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_ANY));
+}
+
+static void
+conv_apply_and_not_selected_AfromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_A_FROM_ANY));
+}
+
+static void
+conv_apply_and_not_selected_AnytofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_ANY_TO_FROM_B));
+}
+
+static void
+conv_apply_and_not_selected_AnyfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_ANY_FROM_B));
+}
+
+static void
+conv_apply_and_not_selected_AnytoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_ANY_TO_B));
+}
+
+/* Or Not Selected */
+static void
+conv_apply_or_not_selected_AtofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_FROM_B));
+}
+
+static void
+conv_apply_or_not_selected_AtoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_B));
+}
+
+static void
+conv_apply_or_not_selected_AfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_A_FROM_B));
+}
+
+static void
+conv_apply_or_not_selected_AtofromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_FROM_ANY));
+}
+
+static void
+conv_apply_or_not_selected_AtoAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_ANY));
+}
+
+static void
+conv_apply_or_not_selected_AfromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_A_FROM_ANY));
+}
+
+static void
+conv_apply_or_not_selected_AnytofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_ANY_TO_FROM_B));
+}
+
+static void
+conv_apply_or_not_selected_AnyfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_ANY_FROM_B));
+}
+
+static void
+conv_apply_or_not_selected_AnytoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, DIR_ANY_TO_B));
+}
+
+
+/* Prepare As Selected*/
+static void
+conv_prepare_as_selected_AtofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_A_TO_FROM_B));
+}
+
+static void
+conv_prepare_as_selected_AtoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_A_TO_B));
+}
+
+static void
+conv_prepare_as_selected_AfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_A_FROM_B));
+}
+
+static void
+conv_prepare_as_selected_AtofromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_A_TO_FROM_ANY));
+}
+
+static void
+conv_prepare_as_selected_AtoAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_A_TO_ANY));
+}
+
+static void
+conv_prepare_as_selected_AfromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_A_FROM_ANY));
+}
+
+static void
+conv_prepare_as_selected_AnytofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_ANY_TO_FROM_B));
+}
+
+static void
+conv_prepare_as_selected_AnyfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_ANY_FROM_B));
+}
+
+static void
+conv_prepare_as_selected_AnytoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_SELECTED, DIR_ANY_TO_B));
+}
+
+/* Prepare As Not Selected */
+static void
+conv_prepare_as_not_selected_AtofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_A_TO_FROM_B));
+}
+
+static void
+conv_prepare_as_not_selected_AtoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_A_TO_B));
+}
+
+static void
+conv_prepare_as_not_selected_AfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_A_FROM_B));
+}
+
+static void
+conv_prepare_as_not_selected_AtofromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_A_TO_FROM_ANY));
+}
+
+static void
+conv_prepare_as_not_selected_AtoAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_A_TO_ANY));
+}
+
+static void
+conv_prepare_as_not_selected_AfromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_A_FROM_ANY));
+}
+
+static void
+conv_prepare_as_not_selected_AnytofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_ANY_TO_FROM_B));
+}
+
+static void
+conv_prepare_as_not_selected_AnyfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_ANY_FROM_B));
+}
+
+static void
+conv_prepare_as_not_selected_AnytoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, DIR_ANY_TO_B));
+}
+
+/* Prepare And Selected */
+static void
+conv_prepare_and_selected_AtofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_A_TO_FROM_B));
+}
+
+static void
+conv_prepare_and_selected_AtoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_A_TO_B));
+}
+
+static void
+conv_prepare_and_selected_AfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_A_FROM_B));
+}
+
+static void
+conv_prepare_and_selected_AtofromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_A_TO_FROM_ANY));
+}
+
+static void
+conv_prepare_and_selected_AtoAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_A_TO_ANY));
+}
+
+static void
+conv_prepare_and_selected_AfromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_A_FROM_ANY));
+}
+
+static void
+conv_prepare_and_selected_AnytofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_ANY_TO_FROM_B));
+}
+
+static void
+conv_prepare_and_selected_AnyfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_ANY_FROM_B));
+}
+
+static void
+conv_prepare_and_selected_AnytoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_AND_SELECTED, DIR_ANY_TO_B));
+}
+
+/* Prepare Or Selected */
+static void
+conv_prepare_or_selected_AtofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_A_TO_FROM_B));
+}
+
+static void
+conv_prepare_or_selected_AtoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_A_TO_B));
+}
+
+static void
+conv_prepare_or_selected_AfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_A_FROM_B));
+}
+
+static void
+conv_prepare_or_selected_AtofromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_A_TO_FROM_ANY));
+}
+
+static void
+conv_prepare_or_selected_AtoAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_A_TO_ANY));
+}
+
+static void
+conv_prepare_or_selected_AfromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_A_FROM_ANY));
+}
+
+static void
+conv_prepare_or_selected_AnytofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_ANY_TO_FROM_B));
+}
+
+static void
+conv_prepare_or_selected_AnyfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_ANY_FROM_B));
+}
+
+static void
+conv_prepare_or_selected_AnytoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_OR_SELECTED, DIR_ANY_TO_B));
+}
+
+/* Prepare And Not Selected */
+static void
+conv_prepare_and_not_selected_AtofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_FROM_B));
+}
+
+static void
+conv_prepare_and_not_selected_AtoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_B));
+}
+
+static void
+conv_prepare_and_not_selected_AfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_A_FROM_B));
+}
+
+static void
+conv_prepare_and_not_selected_AtofromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_FROM_ANY));
+}
+
+static void
+conv_prepare_and_not_selected_AtoAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_ANY));
+}
+
+static void
+conv_prepare_and_not_selected_AfromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_A_FROM_ANY));
+}
+
+static void
+conv_prepare_and_not_selected_AnytofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_ANY_TO_FROM_B));
+}
+
+static void
+conv_prepare_and_not_selected_AnyfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_ANY_FROM_B));
+}
+
+static void
+conv_prepare_and_not_selected_AnytoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_ANY_TO_B));
+}
+
+/* Prepare Or Not Selected */
+static void
+conv_prepare_or_not_selected_AtofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_FROM_B));
+}
+
+static void
+conv_prepare_or_not_selected_AtoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, DIR_A_TO_B));
+}
+
+static void
+conv_prepare_or_not_selected_AfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_A_FROM_B));
+}
+
+static void
+conv_prepare_or_not_selected_AtofromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_FROM_ANY));
+}
+
+static void
+conv_prepare_or_not_selected_AtoAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_A_TO_ANY));
+}
+
+static void
+conv_prepare_or_not_selected_AfromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_A_FROM_ANY));
+}
+
+static void
+conv_prepare_or_not_selected_AnytofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_ANY_TO_FROM_B));
+}
+
+static void
+conv_prepare_or_not_selected_AnyfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_ANY_FROM_B));
+}
+
+static void
+conv_prepare_or_not_selected_AnytoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, DIR_ANY_TO_B));
+}
+
+/* Find packet */
+static void
+conv_find_AtofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_A_TO_FROM_B));
+}
+
+static void
+conv_find_AtoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_A_TO_B));
+}
+
+static void
+conv_find_AfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_A_FROM_B));
+}
+
+static void
+conv_find_AtofromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_A_TO_FROM_ANY));
+}
+
+static void
+conv_find_AtoAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_A_TO_ANY));
+}
+
+static void
+conv_find_AfromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_A_FROM_ANY));
+}
+
+static void
+conv_find_AnytofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_ANY_TO_FROM_B));
+}
+
+static void
+conv_find_AnyfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_ANY_FROM_B));
+}
+
+static void
+conv_find_AnytoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_FRAME(ACTYPE_SELECTED, DIR_ANY_TO_B));
+}
+
+/* Find next */
+static void
+conv_find_next_AtofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_NEXT(ACTYPE_NOT_SELECTED, DIR_A_TO_FROM_B));
+}
+
+static void
+conv_find_next_AtoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_NEXT(ACTYPE_NOT_SELECTED, DIR_A_TO_B));
+}
+
+static void
+conv_find_next_AfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_NEXT(ACTYPE_NOT_SELECTED, DIR_A_FROM_B));
+}
+
+static void
+conv_find_next_AtofromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_NEXT(ACTYPE_NOT_SELECTED, DIR_A_TO_FROM_ANY));
+}
+
+static void
+conv_find_next_AtoAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_NEXT(ACTYPE_NOT_SELECTED, DIR_A_TO_ANY));
+}
+
+static void
+conv_find_next_AfromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_NEXT(ACTYPE_NOT_SELECTED, DIR_A_FROM_ANY));
+}
+
+static void
+conv_find_next_AnytofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_NEXT(ACTYPE_NOT_SELECTED, DIR_ANY_TO_FROM_B));
+}
+
+static void
+conv_find_next_AnyfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_NEXT(ACTYPE_NOT_SELECTED, DIR_ANY_FROM_B));
+}
+
+static void
+conv_find_next_AnytoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_NEXT(ACTYPE_NOT_SELECTED, DIR_ANY_TO_B));
+}
+
+/* Find Previous */
+static void
+conv_find_previous_AtofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_AND_SELECTED, DIR_A_TO_FROM_B));
+}
+
+static void
+conv_find_previous_AtoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_AND_SELECTED, DIR_A_TO_B));
+}
+
+static void
+conv_find_previous_AfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_AND_SELECTED, DIR_A_FROM_B));
+}
+
+static void
+conv_find_previous_AtofromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_AND_SELECTED, DIR_A_TO_FROM_ANY));
+}
+
+static void
+conv_find_previous_AtoAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_AND_SELECTED, DIR_A_TO_ANY));
+}
+
+static void
+conv_find_previous_AfromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_AND_SELECTED, DIR_A_FROM_ANY));
+}
+
+static void
+conv_find_previous_AnytofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_AND_SELECTED, DIR_ANY_TO_FROM_B));
+}
+
+static void
+conv_find_previous_AnyfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_AND_SELECTED, DIR_ANY_FROM_B));
+}
+
+static void
+conv_find_previous_AnytoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_AND_SELECTED, DIR_ANY_TO_B));
+}
+
+/* Colorize Conversation */
+
+static void
+conv_color_AtofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_A_TO_FROM_B));
+}
+
+static void
+conv_color_AtoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_A_TO_B));
+}
+
+static void
+conv_color_AfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_A_FROM_B));
+}
+
+static void
+conv_color_AtofromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_A_TO_FROM_ANY));
+}
+
+static void
+conv_color_AtoAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_A_TO_ANY));
+}
+
+static void
+conv_color_AfromAny_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_A_FROM_ANY));
+}
+
+static void
+conv_color_AnytofromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_ANY_TO_FROM_B));
+}
+
+static void
+conv_color_AnyfromB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_ANY_FROM_B));
+}
+
+static void
+conv_color_AnytoB_cb(GtkAction *action _U_, gpointer user_data)
+{
+ ct_select_filter_cb( NULL, user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, DIR_ANY_TO_B));
+}
+static const char *ui_desc_conv_filter_popup =
+"<ui>\n"
+" <popup name='ConversationFilterPopup' action='PopupAction'>\n"
+" <menu name= 'ApplyAsFilter' action='/Apply as Filter'>\n"
+" <menu name= 'ApplyAsFilterSelected' action='/Apply as Filter/Selected'>\n"
+" <menuitem action='/Apply as Filter/Selected/A_to_from_B'/>\n"
+" <menuitem action='/Apply as Filter/Selected/A_to_B'/>\n"
+" <menuitem action='/Apply as Filter/Selected/A_from_B'/>\n"
+" <menuitem action='/Apply as Filter/Selected/A_to_from_Any'/>\n"
+" <menuitem action='/Apply as Filter/Selected/A_to_Any'/>\n"
+" <menuitem action='/Apply as Filter/Selected/A_from_Any'/>\n"
+" <menuitem action='/Apply as Filter/Selected/Any_tofrom_B'/>\n"
+" <menuitem action='/Apply as Filter/Selected/Any_from_B'/>\n"
+" <menuitem action='/Apply as Filter/Selected/Any_to_B'/>\n"
+" </menu>\n"
+" <menu name= 'ApplyAsFilterNotSelected' action='/Apply as Filter/Not Selected'>\n"
+" <menuitem action='/Apply as Filter/Not Selected/A_to_from_B'/>\n"
+" <menuitem action='/Apply as Filter/Not Selected/A_to_B'/>\n"
+" <menuitem action='/Apply as Filter/Not Selected/A_from_B'/>\n"
+" <menuitem action='/Apply as Filter/Not Selected/A_to_from_Any'/>\n"
+" <menuitem action='/Apply as Filter/Not Selected/A_to_Any'/>\n"
+" <menuitem action='/Apply as Filter/Not Selected/A_from_Any'/>\n"
+" <menuitem action='/Apply as Filter/Not Selected/Any_tofrom_B'/>\n"
+" <menuitem action='/Apply as Filter/Not Selected/Any_from_B'/>\n"
+" <menuitem action='/Apply as Filter/Not Selected/Any_to_B'/>\n"
+" </menu>\n"
+" <menu name= 'ApplyAsFilterAndSelected' action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected'>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_to_from_B'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_to_B'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_from_B'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_to_from_Any'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_to_Any'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_from_Any'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/Any_tofrom_B'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/Any_from_B'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/Any_to_B'/>\n"
+" </menu>\n"
+" <menu name= 'ApplyAsFilterOrSelected' action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected'>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_to_from_B'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_to_B'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_from_B'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_to_from_Any'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_to_Any'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_from_Any'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/Any_tofrom_B'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/Any_from_B'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/Any_to_B'/>\n"
+" </menu>\n"
+" <menu name= 'ApplyAsFilterAndNotSelected' action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected'>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_to_from_B'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_to_B'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_from_B'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_to_from_Any'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_to_Any'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_from_Any'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/Any_tofrom_B'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/Any_from_B'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/Any_to_B'/>\n"
+" </menu>\n"
+" <menu name= 'ApplyAsFilterOrNotSelected' action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected'>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_to_from_B'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_to_B'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_from_B'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_to_from_Any'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_to_Any'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_from_Any'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/Any_tofrom_B'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/Any_from_B'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/Any_to_B'/>\n"
+" </menu>\n"
+" </menu>\n"
+" <menu name= 'PrepareAFilter' action='/Prepare a Filter'>\n"
+" <menu name= 'PrepareAFilterSelected' action='/Prepare a Filter/Selected'>\n"
+" <menuitem action='/Prepare a Filter/Selected/A_to_from_B'/>\n"
+" <menuitem action='/Prepare a Filter/Selected/A_to_B'/>\n"
+" <menuitem action='/Prepare a Filter/Selected/A_from_B'/>\n"
+" <menuitem action='/Prepare a Filter/Selected/A_to_from_Any'/>\n"
+" <menuitem action='/Prepare a Filter/Selected/A_to_Any'/>\n"
+" <menuitem action='/Prepare a Filter/Selected/A_from_Any'/>\n"
+" <menuitem action='/Prepare a Filter/Selected/Any_tofrom_B'/>\n"
+" <menuitem action='/Prepare a Filter/Selected/Any_from_B'/>\n"
+" <menuitem action='/Prepare a Filter/Selected/Any_to_B'/>\n"
+" </menu>\n"
+" <menu name= 'PrepareAFilterNotSelected' action='/Prepare a Filter/Not Selected'>\n"
+" <menuitem action='/Prepare a Filter/Not Selected/A_to_from_B'/>\n"
+" <menuitem action='/Prepare a Filter/Not Selected/A_to_B'/>\n"
+" <menuitem action='/Prepare a Filter/Not Selected/A_from_B'/>\n"
+" <menuitem action='/Prepare a Filter/Not Selected/A_to_from_Any'/>\n"
+" <menuitem action='/Prepare a Filter/Not Selected/A_to_Any'/>\n"
+" <menuitem action='/Prepare a Filter/Not Selected/A_from_Any'/>\n"
+" <menuitem action='/Prepare a Filter/Not Selected/Any_tofrom_B'/>\n"
+" <menuitem action='/Prepare a Filter/Not Selected/Any_from_B'/>\n"
+" <menuitem action='/Prepare a Filter/Not Selected/Any_to_B'/>\n"
+" </menu>\n"
+" <menu name= 'PrepareAFilterAndSelected' action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected'>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_to_from_B'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_to_B'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_from_B'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_to_from_Any'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_to_Any'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_from_Any'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/Any_tofrom_B'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/Any_from_B'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/Any_to_B'/>\n"
+" </menu>\n"
+" <menu name= 'PrepareAFilterOrSelected' action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected'>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_to_from_B'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_to_B'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_from_B'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_to_from_Any'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_to_Any'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_from_Any'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/Any_tofrom_B'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/Any_from_B'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/Any_to_B'/>\n"
+" </menu>\n"
+" <menu name= 'PrepareAFilterAndNotSelected' action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected'>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_to_from_B'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_to_B'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_from_B'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_to_from_Any'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_to_Any'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_from_Any'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/Any_tofrom_B'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/Any_from_B'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/Any_to_B'/>\n"
+" </menu>\n"
+" <menu name= 'PrepareAFilterOrNotSelected' action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected'>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_to_from_B'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_to_B'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_from_B'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_to_from_Any'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_to_Any'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_from_Any'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/Any_tofrom_B'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/Any_from_B'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/Any_to_B'/>\n"
+" </menu>\n"
+" </menu>\n"
+" <menu name= 'FindPacket' action='/Find Packet'>\n"
+" <menu name= 'FindPacketFindPacket' action='/Find Packet/Find Packet'>\n"
+" <menuitem action='/Find Packet/Find Packet/A_to_from_B'/>\n"
+" <menuitem action='/Find Packet/Find Packet/A_to_B'/>\n"
+" <menuitem action='/Find Packet/Find Packet/A_from_B'/>\n"
+" <menuitem action='/Find Packet/Find Packet/A_to_from_Any'/>\n"
+" <menuitem action='/Find Packet/Find Packet/A_to_Any'/>\n"
+" <menuitem action='/Find Packet/Find Packet/A_from_Any'/>\n"
+" <menuitem action='/Find Packet/Find Packet/Any_tofrom_B'/>\n"
+" <menuitem action='/Find Packet/Find Packet/Any_from_B'/>\n"
+" <menuitem action='/Find Packet/Find Packet/Any_to_B'/>\n"
+" </menu>\n"
+" <menu name= 'FindPacketFindNext' action='/Find Packet/Find Next'>\n"
+" <menuitem action='/Find Packet/Find Next/A_to_from_B'/>\n"
+" <menuitem action='/Find Packet/Find Next/A_to_B'/>\n"
+" <menuitem action='/Find Packet/Find Next/A_from_B'/>\n"
+" <menuitem action='/Find Packet/Find Next/A_to_from_Any'/>\n"
+" <menuitem action='/Find Packet/Find Next/A_to_Any'/>\n"
+" <menuitem action='/Find Packet/Find Next/A_from_Any'/>\n"
+" <menuitem action='/Find Packet/Find Next/Any_tofrom_B'/>\n"
+" <menuitem action='/Find Packet/Find Next/Any_from_B'/>\n"
+" <menuitem action='/Find Packet/Find Next/Any_to_B'/>\n"
+" </menu>\n"
+" <menu name= 'FindPacketFindPrevious' action='/Find Packet/Find Previous'>\n"
+" <menuitem action='/Find Packet/Find Previous/A_to_from_B'/>\n"
+" <menuitem action='/Find Packet/Find Previous/A_to_B'/>\n"
+" <menuitem action='/Find Packet/Find Previous/A_from_B'/>\n"
+" <menuitem action='/Find Packet/Find Previous/A_to_from_Any'/>\n"
+" <menuitem action='/Find Packet/Find Previous/A_to_Any'/>\n"
+" <menuitem action='/Find Packet/Find Previous/A_from_Any'/>\n"
+" <menuitem action='/Find Packet/Find Previous/Any_tofrom_B'/>\n"
+" <menuitem action='/Find Packet/Find Previous/Any_from_B'/>\n"
+" <menuitem action='/Find Packet/Find Previous/Any_to_B'/>\n"
+" </menu>\n"
+" </menu>\n"
+" <menu name= 'ColorizeConversation' action='/Colorize Conversation'>\n"
+" <menuitem action='/Colorize Conversation/A_to_from_B'/>\n"
+" <menuitem action='/Colorize Conversation/A_to_B'/>\n"
+" <menuitem action='/Colorize Conversation/A_from_B'/>\n"
+" <menuitem action='/Colorize Conversation/A_to_from_Any'/>\n"
+" <menuitem action='/Colorize Conversation/A_to_Any'/>\n"
+" <menuitem action='/Colorize Conversation/A_from_Any'/>\n"
+" <menuitem action='/Colorize Conversation/Any_tofrom_B'/>\n"
+" <menuitem action='/Colorize Conversation/Any_from_B'/>\n"
+" <menuitem action='/Colorize Conversation/Any_to_B'/>\n"
+" </menu>\n"
+" </popup>\n"
+"</ui>\n";
+
+/*
+ * GtkActionEntry
+ * typedef struct {
+ * const gchar *name;
+ * const gchar *stock_id;
+ * const gchar *label;
+ * const gchar *accelerator;
+ * const gchar *tooltip;
+ * GCallback callback;
+ * } GtkActionEntry;
+ * const gchar *name; The name of the action.
+ * const gchar *stock_id; The stock id for the action, or the name of an icon from the icon theme.
+ * const gchar *label; The label for the action. This field should typically be marked for translation,
+ * see gtk_action_group_set_translation_domain().
+ * If label is NULL, the label of the stock item with id stock_id is used.
+ * const gchar *accelerator; The accelerator for the action, in the format understood by gtk_accelerator_parse().
+ * const gchar *tooltip; The tooltip for the action. This field should typically be marked for translation,
+ * see gtk_action_group_set_translation_domain().
+ * GCallback callback; The function to call when the action is activated.
+ *
+ */
+static const GtkActionEntry conv_filter_menu_entries[] = {
+ /* Top level */
+ { "/Apply as Filter", NULL, "Apply as Filter", NULL, NULL, NULL },
+ { "/Prepare a Filter", NULL, "Prepare a Filter", NULL, NULL, NULL },
+ { "/Find Packet", NULL, "Find Packet", NULL, NULL, NULL },
+ { "/Colorize Conversation", NULL, "Colorize Conversation", NULL, NULL, NULL },
+
+ /* Apply as */
+ { "/Apply as Filter/Selected", NULL, "Selected" , NULL, NULL, NULL },
+ { "/Apply as Filter/Not Selected", NULL, "Not Selected", NULL, NULL, NULL },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, NULL, NULL },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, NULL, NULL },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, NULL, NULL },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, NULL, NULL },
+
+ /* Apply as Selected */
+ { "/Apply as Filter/Selected/A_to_from_B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_apply_as_selected_AtofromB_cb)},
+ { "/Apply as Filter/Selected/A_to_B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_apply_as_selected_AtoB_cb)},
+ { "/Apply as Filter/Selected/A_from_B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_apply_as_selected_AfromB_cb)},
+ { "/Apply as Filter/Selected/A_to_from_Any", NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any", G_CALLBACK(conv_apply_as_selected_AtofromAny_cb)},
+ { "/Apply as Filter/Selected/A_to_Any", NULL, "A " UTF8_RIGHTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_apply_as_selected_AtoAny_cb)},
+ { "/Apply as Filter/Selected/A_from_Any", NULL, "A " UTF8_LEFTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_apply_as_selected_AfromAny_cb)},
+ { "/Apply as Filter/Selected/Any_tofrom_B", NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B",NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_apply_as_selected_AnytofromB_cb)},
+ { "/Apply as Filter/Selected/Any_to_B", NULL, "Any " UTF8_RIGHTWARDS_ARROW " B",NULL, "Any " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_apply_as_selected_AnytoB_cb)},
+ { "/Apply as Filter/Selected/Any_from_B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_apply_as_selected_AnyfromB_cb)},
+
+ /* Apply as Not Selected */
+ { "/Apply as Filter/Not Selected/A_to_from_B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_apply_as_not_selected_AtofromB_cb)},
+ { "/Apply as Filter/Not Selected/A_to_B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_apply_as_not_selected_AtoB_cb)},
+ { "/Apply as Filter/Not Selected/A_from_B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_apply_as_not_selected_AfromB_cb)},
+ { "/Apply as Filter/Not Selected/A_to_from_Any", NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any", G_CALLBACK(conv_apply_as_not_selected_AtofromAny_cb)},
+ { "/Apply as Filter/Not Selected/A_to_Any", NULL, "A " UTF8_RIGHTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_apply_as_not_selected_AtoAny_cb)},
+ { "/Apply as Filter/Not Selected/A_from_Any", NULL, "A " UTF8_LEFTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_apply_as_not_selected_AfromAny_cb)},
+ { "/Apply as Filter/Not Selected/Any_tofrom_B", NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B",NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_apply_as_not_selected_AnytofromB_cb)},
+ { "/Apply as Filter/Not Selected/Any_to_B", NULL, "Any " UTF8_RIGHTWARDS_ARROW " B",NULL, "Any " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_apply_as_not_selected_AnytoB_cb)},
+ { "/Apply as Filter/Not Selected/Any_from_B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_apply_as_not_selected_AnyfromB_cb)},
+
+ /* Apply as ... and Selected */
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_to_from_B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_apply_and_selected_AtofromB_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_to_B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_apply_and_selected_AtoB_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_from_B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_apply_and_selected_AfromB_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_to_from_Any", NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",G_CALLBACK(conv_apply_and_selected_AtofromAny_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_to_Any", NULL, "A " UTF8_RIGHTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_apply_and_selected_AtoAny_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_from_Any", NULL, "A " UTF8_LEFTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_apply_and_selected_AfromAny_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/Any_tofrom_B", NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B",NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_apply_and_selected_AnytofromB_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/Any_to_B", NULL, "Any " UTF8_RIGHTWARDS_ARROW " B",NULL, "Any " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_apply_and_selected_AnytoB_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/Any_from_B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_apply_and_selected_AnyfromB_cb)},
+
+ /* Apply as ... or Selected */
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_to_from_B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_apply_or_selected_AtofromB_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_to_B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_apply_or_selected_AtoB_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_from_B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_apply_or_selected_AfromB_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_to_from_Any", NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",G_CALLBACK(conv_apply_or_selected_AtofromAny_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_to_Any", NULL, "A " UTF8_RIGHTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_apply_or_selected_AtoAny_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_from_Any", NULL, "A " UTF8_LEFTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_apply_or_selected_AfromAny_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/Any_tofrom_B", NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B",NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_apply_or_selected_AnytofromB_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/Any_to_B", NULL, "Any " UTF8_RIGHTWARDS_ARROW " B",NULL, "Any " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_apply_or_selected_AnytoB_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/Any_from_B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_apply_or_selected_AnyfromB_cb)},
+
+ /* Apply as ... and not Selected */
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_to_from_B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_apply_and_not_selected_AtofromB_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_to_B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_apply_and_not_selected_AtoB_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_from_B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_apply_and_not_selected_AfromB_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_to_from_Any", NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",G_CALLBACK(conv_apply_and_not_selected_AtofromAny_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_to_Any", NULL, "A " UTF8_RIGHTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_apply_and_not_selected_AtoAny_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_from_Any", NULL, "A " UTF8_LEFTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_apply_and_not_selected_AfromAny_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/Any_tofrom_B", NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B",NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_apply_and_not_selected_AnytofromB_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/Any_to_B", NULL, "Any " UTF8_RIGHTWARDS_ARROW " B",NULL, "Any " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_apply_and_not_selected_AnytoB_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/Any_from_B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_apply_and_not_selected_AnyfromB_cb)},
+
+ /* Apply as ... or not Selected */
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_to_from_B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_apply_or_not_selected_AtofromB_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_to_B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_apply_or_not_selected_AtoB_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_from_B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_apply_or_not_selected_AfromB_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_to_from_Any", NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any", G_CALLBACK(conv_apply_or_not_selected_AtofromAny_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_to_Any", NULL, "A " UTF8_RIGHTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_apply_or_not_selected_AtoAny_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_from_Any", NULL, "A " UTF8_LEFTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_apply_or_not_selected_AfromAny_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/Any_tofrom_B", NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B",NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_apply_or_not_selected_AnytofromB_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/Any_to_B", NULL, "Any " UTF8_RIGHTWARDS_ARROW " B",NULL, "Any " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_apply_or_not_selected_AnytoB_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/Any_from_B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_apply_or_not_selected_AnyfromB_cb)},
+
+ /* Prepare a */
+ { "/Prepare a Filter/Selected", NULL, "Selected", NULL, NULL, NULL },
+ { "/Prepare a Filter/Not Selected", NULL, "Not Selected", NULL, NULL, NULL },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, NULL, NULL },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, NULL, NULL },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, NULL, NULL },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, NULL, NULL },
+
+ /* Prepare as Selected */
+ { "/Prepare a Filter/Selected/A_to_from_B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_prepare_as_selected_AtofromB_cb)},
+ { "/Prepare a Filter/Selected/A_to_B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_prepare_as_selected_AtoB_cb)},
+ { "/Prepare a Filter/Selected/A_from_B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_prepare_as_selected_AfromB_cb)},
+ { "/Prepare a Filter/Selected/A_to_from_Any", NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any", G_CALLBACK(conv_prepare_as_selected_AtofromAny_cb)},
+ { "/Prepare a Filter/Selected/A_to_Any", NULL, "A " UTF8_RIGHTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_prepare_as_selected_AtoAny_cb)},
+ { "/Prepare a Filter/Selected/A_from_Any", NULL, "A " UTF8_LEFTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_prepare_as_selected_AfromAny_cb)},
+ { "/Prepare a Filter/Selected/Any_tofrom_B", NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B",NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_prepare_as_selected_AnytofromB_cb)},
+ { "/Prepare a Filter/Selected/Any_to_B", NULL, "Any " UTF8_RIGHTWARDS_ARROW " B",NULL, "Any " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_prepare_as_selected_AnytoB_cb)},
+ { "/Prepare a Filter/Selected/Any_from_B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_prepare_as_selected_AnyfromB_cb)},
+
+ /* Prepare a Not Selected */
+ { "/Prepare a Filter/Not Selected/A_to_from_B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_prepare_as_not_selected_AtofromB_cb)},
+ { "/Prepare a Filter/Not Selected/A_to_B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_prepare_as_not_selected_AtoB_cb)},
+ { "/Prepare a Filter/Not Selected/A_from_B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_prepare_as_not_selected_AfromB_cb)},
+ { "/Prepare a Filter/Not Selected/A_to_from_Any", NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any", G_CALLBACK(conv_prepare_as_not_selected_AtofromAny_cb)},
+ { "/Prepare a Filter/Not Selected/A_to_Any", NULL, "A " UTF8_RIGHTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_prepare_as_not_selected_AtoAny_cb)},
+ { "/Prepare a Filter/Not Selected/A_from_Any", NULL, "A " UTF8_LEFTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_prepare_as_not_selected_AfromAny_cb)},
+ { "/Prepare a Filter/Not Selected/Any_tofrom_B", NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B",NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_prepare_as_not_selected_AnytofromB_cb)},
+ { "/Prepare a Filter/Not Selected/Any_to_B", NULL, "Any " UTF8_RIGHTWARDS_ARROW " B",NULL, "Any " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_prepare_as_not_selected_AnytoB_cb)},
+ { "/Prepare a Filter/Not Selected/Any_from_B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_prepare_as_not_selected_AnyfromB_cb)},
+
+ /* Prepare a ... and Selected */
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_to_from_B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_prepare_and_selected_AtofromB_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_to_B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_prepare_and_selected_AtoB_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_from_B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_prepare_and_selected_AfromB_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_to_from_Any", NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any", G_CALLBACK(conv_prepare_and_selected_AtofromAny_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_to_Any", NULL, "A " UTF8_RIGHTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_prepare_and_selected_AtoAny_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/A_from_Any", NULL, "A " UTF8_LEFTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_prepare_and_selected_AfromAny_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/Any_tofrom_B", NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B",NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_prepare_and_selected_AnytofromB_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/Any_to_B", NULL, "Any " UTF8_RIGHTWARDS_ARROW " B",NULL, "Any " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_prepare_and_selected_AnytoB_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/Any_from_B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_prepare_and_selected_AnyfromB_cb)},
+
+ /* Prepare a ... or Selected */
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_to_from_B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_prepare_or_selected_AtofromB_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_to_B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_prepare_or_selected_AtoB_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_from_B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_prepare_or_selected_AfromB_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_to_from_Any", NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any", G_CALLBACK(conv_prepare_or_selected_AtofromAny_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_to_Any", NULL, "A " UTF8_RIGHTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_prepare_or_selected_AtoAny_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/A_from_Any", NULL, "A " UTF8_LEFTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_prepare_or_selected_AfromAny_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/Any_tofrom_B", NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B",NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_prepare_or_selected_AnytofromB_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/Any_to_B", NULL, "Any " UTF8_RIGHTWARDS_ARROW " B",NULL, "Any " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_prepare_or_selected_AnytoB_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/Any_from_B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_prepare_or_selected_AnyfromB_cb)},
+
+ /* Prepare a ... and not Selected */
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_to_from_B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_prepare_and_not_selected_AtofromB_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_to_B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_prepare_and_not_selected_AtoB_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_from_B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_prepare_and_not_selected_AfromB_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_to_from_Any",NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any", G_CALLBACK(conv_prepare_and_not_selected_AtofromAny_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_to_Any", NULL, "A " UTF8_RIGHTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_prepare_and_not_selected_AtoAny_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/A_from_Any", NULL, "A " UTF8_LEFTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_prepare_and_not_selected_AfromAny_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/Any_tofrom_B", NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B",NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_prepare_and_not_selected_AnytofromB_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/Any_to_B", NULL, "Any " UTF8_RIGHTWARDS_ARROW " B",NULL, "Any " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_prepare_and_not_selected_AnytoB_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/Any_from_B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_prepare_and_not_selected_AnyfromB_cb)},
+
+ /* Prepare a ... or not Selected */
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_to_from_B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_prepare_or_not_selected_AtofromB_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_to_B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_prepare_or_not_selected_AtoB_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_from_B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_prepare_or_not_selected_AfromB_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_to_from_Any", NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any", G_CALLBACK(conv_prepare_or_not_selected_AtofromAny_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_to_Any", NULL, "A " UTF8_RIGHTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_prepare_or_not_selected_AtoAny_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/A_from_Any", NULL, "A " UTF8_LEFTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_prepare_or_not_selected_AfromAny_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/Any_tofrom_B", NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B",NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_prepare_or_not_selected_AnytofromB_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/Any_to_B", NULL, "Any " UTF8_RIGHTWARDS_ARROW " B",NULL, "Any " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_prepare_or_not_selected_AnytoB_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/Any_from_B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_prepare_or_not_selected_AnyfromB_cb)},
+
+ /* Find packet*/
+ { "/Find Packet/Find Packet", NULL, "Find Packet", NULL, NULL, NULL },
+ { "/Find Packet/Find Next", NULL, "Find Next", NULL, NULL, NULL },
+ { "/Find Packet/Find Previous", NULL, "Find Previous", NULL, NULL, NULL },
+
+ /* Find packet*/
+ { "/Find Packet/Find Packet/A_to_from_B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_find_AtofromB_cb)},
+ { "/Find Packet/Find Packet/A_to_B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_find_AtoB_cb)},
+ { "/Find Packet/Find Packet/A_from_B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_find_AfromB_cb)},
+ { "/Find Packet/Find Packet/A_to_from_Any", NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",G_CALLBACK(conv_find_AtofromAny_cb)},
+ { "/Find Packet/Find Packet/A_to_Any", NULL, "A " UTF8_RIGHTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_find_AtoAny_cb)},
+ { "/Find Packet/Find Packet/A_from_Any", NULL, "A " UTF8_LEFTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_find_AfromAny_cb)},
+ { "/Find Packet/Find Packet/Any_tofrom_B", NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B",NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_find_AnytofromB_cb)},
+ { "/Find Packet/Find Packet/Any_to_B", NULL, "Any " UTF8_RIGHTWARDS_ARROW " B",NULL, "Any " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_find_AnytoB_cb)},
+ { "/Find Packet/Find Packet/Any_from_B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_find_AnyfromB_cb)},
+
+ /* Find Next*/
+ { "/Find Packet/Find Next/A_to_from_B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_find_next_AtofromB_cb)},
+ { "/Find Packet/Find Next/A_to_B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_find_next_AtoB_cb)},
+ { "/Find Packet/Find Next/A_from_B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_find_next_AfromB_cb)},
+ { "/Find Packet/Find Next/A_to_from_Any", NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",G_CALLBACK(conv_find_next_AtofromAny_cb)},
+ { "/Find Packet/Find Next/A_to_Any", NULL, "A " UTF8_RIGHTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_find_next_AtoAny_cb)},
+ { "/Find Packet/Find Next/A_from_Any", NULL, "A " UTF8_LEFTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_find_next_AfromAny_cb)},
+ { "/Find Packet/Find Next/Any_tofrom_B", NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B",NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_find_next_AnytofromB_cb)},
+ { "/Find Packet/Find Next/Any_to_B", NULL, "Any " UTF8_RIGHTWARDS_ARROW " B",NULL, "Any " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_find_next_AnytoB_cb)},
+ { "/Find Packet/Find Next/Any_from_B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_find_next_AnyfromB_cb)},
+
+ /* Find packet*/
+ { "/Find Packet/Find Previous/A_to_from_B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_find_previous_AtofromB_cb)},
+ { "/Find Packet/Find Previous/A_to_B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_find_previous_AtoB_cb)},
+ { "/Find Packet/Find Previous/A_from_B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_find_previous_AfromB_cb)},
+ { "/Find Packet/Find Previous/A_to_from_Any", NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",G_CALLBACK(conv_find_previous_AtofromAny_cb)},
+ { "/Find Packet/Find Previous/A_to_Any", NULL, "A " UTF8_RIGHTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_find_previous_AtoAny_cb)},
+ { "/Find Packet/Find Previous/A_from_Any", NULL, "A " UTF8_LEFTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_find_previous_AfromAny_cb)},
+ { "/Find Packet/Find Previous/Any_tofrom_B", NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B",NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_find_previous_AnytofromB_cb)},
+ { "/Find Packet/Find Previous/Any_to_B", NULL, "Any " UTF8_RIGHTWARDS_ARROW " B",NULL, "Any " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_find_previous_AnytoB_cb)},
+ { "/Find Packet/Find Previous/Any_from_B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_find_previous_AnyfromB_cb)},
+
+ /* Colorize Conversation */
+ { "/Colorize Conversation/A_to_from_B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", NULL, "A " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_color_AtofromB_cb)},
+ { "/Colorize Conversation/A_to_B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", NULL, "A " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_color_AtoB_cb)},
+ { "/Colorize Conversation/A_from_B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", NULL, "A " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_color_AfromB_cb)},
+ { "/Colorize Conversation/A_to_from_Any", NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",NULL, "A " UTF8_LEFT_RIGHT_ARROW " Any",G_CALLBACK(conv_color_AtofromAny_cb)},
+ { "/Colorize Conversation/A_to_Any", NULL, "A " UTF8_RIGHTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_color_AtoAny_cb)},
+ { "/Colorize Conversation/A_from_Any", NULL, "A " UTF8_LEFTWARDS_ARROW " Any",NULL, "A " UTF8_LEFTWARDS_ARROW " Any", G_CALLBACK(conv_color_AfromAny_cb)},
+ { "/Colorize Conversation/Any_tofrom_B", NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B",NULL, "Any " UTF8_LEFT_RIGHT_ARROW " B", G_CALLBACK(conv_color_AnytofromB_cb)},
+ { "/Colorize Conversation/Any_to_B", NULL, "Any " UTF8_RIGHTWARDS_ARROW " B",NULL, "Any " UTF8_RIGHTWARDS_ARROW " B", G_CALLBACK(conv_color_AnytoB_cb)},
+ { "/Colorize Conversation/Any_from_B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", NULL, "Any " UTF8_LEFTWARDS_ARROW " B", G_CALLBACK(conv_color_AnyfromB_cb)},
+
+};
+
+static void
+ct_create_popup_menu(conversations_table *ct)
+{
+ GtkUIManager *ui_manager;
+ GtkActionGroup *action_group;
+ GError *error = NULL;
+
+ action_group = gtk_action_group_new ("ConvActionGroup");
+ gtk_action_group_add_actions (action_group, /* the action group */
+ (gpointer)conv_filter_menu_entries, /* an array of action descriptions */
+ G_N_ELEMENTS(conv_filter_menu_entries), /* the number of entries */
+ (gpointer)ct); /* data to pass to the action callbacks */
+
+ ui_manager = gtk_ui_manager_new ();
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ gtk_ui_manager_add_ui_from_string (ui_manager,ui_desc_conv_filter_popup, -1, &error);
+ if (error != NULL)
+ {
+ fprintf (stderr, "Warning: building conversation filter popup failed: %s\n",
+ error->message);
+ g_error_free (error);
+ error = NULL;
+ }
+ ct->menu = gtk_ui_manager_get_widget(ui_manager, "/ConversationFilterPopup");
+
+ g_signal_connect(ct->table, "button_press_event", G_CALLBACK(ct_show_popup_menu_cb), ct);
+
+}
+
+/* Draw/refresh the address fields of a single entry at the specified index */
+static void
+get_ct_table_address(conversations_table *ct, conv_t *conv, char **entries)
+{
+ char *port;
+ guint32 pt;
+
+ if(!ct->resolve_names)
+ entries[0] = ep_address_to_str(&conv->src_address);
+ else {
+ entries[0] = (char *)get_addr_name(&conv->src_address);
+ }
+
+ pt = conv->port_type;
+ if(!ct->resolve_names) pt = PT_NONE;
+ switch(pt) {
+ case(PT_TCP):
+ entries[1] = get_tcp_port(conv->src_port);
+ break;
+ case(PT_UDP):
+ entries[1] = get_udp_port(conv->src_port);
+ break;
+ case(PT_SCTP):
+ entries[1] = get_sctp_port(conv->src_port);
+ break;
+ default:
+ port=ct_port_to_str(conv->port_type, conv->src_port);
+ entries[1] = port?port:"";
+ }
+
+ if(!ct->resolve_names)
+ entries[2]=ep_address_to_str(&conv->dst_address);
+ else {
+ entries[2]=(char *)get_addr_name(&conv->dst_address);
+ }
+
+ switch(pt) {
+ case(PT_TCP):
+ entries[3]=get_tcp_port(conv->dst_port);
+ break;
+ case(PT_UDP):
+ entries[3]=get_udp_port(conv->dst_port);
+ break;
+ case(PT_SCTP):
+ entries[3]=get_sctp_port(conv->dst_port);
+ break;
+ default:
+ port=ct_port_to_str(conv->port_type, conv->dst_port);
+ entries[3]=port?port:"";
+ }
+}
+
+/* Refresh the address fields of all entries in the list */
+static void
+draw_ct_table_addresses(conversations_table *ct)
+{
+ guint32 i;
+ char *entries[4];
+ GtkListStore *store;
+
+ if (!ct->num_conversations)
+ return;
+
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(ct->table));
+ g_object_ref(store);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(ct->table), NULL);
+
+ for(i=0;i<ct->num_conversations;i++){
+ conv_t *conv = &g_array_index(ct->conversations, conv_t, i);
+ if (!conv->iter_valid)
+ continue;
+ get_ct_table_address(ct, conv, entries);
+ gtk_list_store_set (store, &conv->iter,
+ SRC_ADR_COLUMN, entries[0],
+ SRC_PORT_COLUMN, entries[1],
+ DST_ADR_COLUMN, entries[2],
+ DST_PORT_COLUMN, entries[3],
+ -1);
+ }
+
+ gtk_tree_view_set_model(GTK_TREE_VIEW(ct->table), GTK_TREE_MODEL(store));
+ g_object_unref(store);
+}
+
+static void
+draw_ct_table_data(conversations_table *ct)
+{
+ guint32 i;
+ char title[256];
+ GtkListStore *store;
+ gboolean first = TRUE;
+
+ if (ct->page_lb) {
+ if(ct->num_conversations) {
+ g_snprintf(title, sizeof(title), "%s: %u", ct->name, ct->num_conversations);
+ } else {
+ g_snprintf(title, sizeof(title), "%s", ct->name);
+ }
+ gtk_label_set_text(GTK_LABEL(ct->page_lb), title);
+ gtk_widget_set_sensitive(ct->page_lb, ct->num_conversations);
+ } else {
+ if(ct->num_conversations) {
+ g_snprintf(title, sizeof(title), "%s Conversations: %u", ct->name, ct->num_conversations);
+ } else {
+ g_snprintf(title, sizeof(title), "%s Conversations", ct->name);
+ }
+ gtk_label_set_text(GTK_LABEL(ct->name_lb), title);
+ }
+
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(ct->table));
+
+ for(i=0;i<ct->num_conversations;i++){
+ char start_time[COL_STR_LEN], duration[COL_STR_LEN],
+ txbps[COL_STR_LEN], rxbps[COL_STR_LEN];
+ char *tx_ptr, *rx_ptr;
+ double duration_s;
+ conv_t *conversation = &g_array_index(ct->conversations, conv_t, i);
+
+ if (!conversation->modified)
+ continue;
+
+ if (first) {
+ g_object_ref(store);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(ct->table), NULL);
+
+ first = FALSE;
+ }
+ duration_s = nstime_to_sec(&conversation->stop_time) - nstime_to_sec(&conversation->start_time);
+ g_snprintf(start_time, COL_STR_LEN, "%.9f", nstime_to_sec(&conversation->start_time));
+ g_snprintf(duration, COL_STR_LEN, "%.4f", duration_s);
+
+ if (duration_s > 0 && conversation->tx_frames > 1) {
+ g_snprintf(txbps, COL_STR_LEN, "%.2f", (gint64) conversation->tx_bytes * 8 / duration_s);
+ tx_ptr = txbps;
+ } else {
+ tx_ptr = NO_BPS_STR;
+ }
+ if (duration_s > 0 && conversation->rx_frames > 1) {
+ g_snprintf(rxbps, COL_STR_LEN, "%.2f", (gint64) conversation->rx_bytes * 8 / duration_s);
+ rx_ptr = rxbps;
+ } else {
+ rx_ptr = NO_BPS_STR;
+ }
+ conversation->modified = FALSE;
+ if (!conversation->iter_valid) {
+ char *entries[4];
+
+ get_ct_table_address(ct, conversation, entries);
+ conversation->iter_valid = TRUE;
+ gtk_list_store_insert_with_values( store , &conversation->iter, G_MAXINT,
+ SRC_ADR_COLUMN, entries[0],
+ SRC_PORT_COLUMN, entries[1],
+ DST_ADR_COLUMN, entries[2],
+ DST_PORT_COLUMN, entries[3],
+ PACKETS_COLUMN, conversation->tx_frames+conversation->rx_frames,
+ BYTES_COLUMN, conversation->tx_bytes+conversation->rx_bytes,
+ PKT_AB_COLUMN, conversation->tx_frames,
+ BYTES_AB_COLUMN, conversation->tx_bytes,
+ PKT_BA_COLUMN, conversation->rx_frames,
+ BYTES_BA_COLUMN, conversation->rx_bytes,
+ START_COLUMN, start_time,
+ DURATION_COLUMN, duration,
+ BPS_AB_COLUMN, tx_ptr,
+ BPS_BA_COLUMN, rx_ptr,
+ INDEX_COLUMN, i,
+ -1);
+ }
+ else {
+ gtk_list_store_set (store, &conversation->iter,
+ PACKETS_COLUMN, conversation->tx_frames+conversation->rx_frames,
+ BYTES_COLUMN, conversation->tx_bytes+conversation->rx_bytes,
+ PKT_AB_COLUMN, conversation->tx_frames,
+ BYTES_AB_COLUMN, conversation->tx_bytes,
+ PKT_BA_COLUMN, conversation->rx_frames,
+ BYTES_BA_COLUMN, conversation->rx_bytes,
+ START_COLUMN, start_time,
+ DURATION_COLUMN, duration,
+ BPS_AB_COLUMN, tx_ptr,
+ BPS_BA_COLUMN, rx_ptr,
+ -1);
+ }
+ }
+ if (!first) {
+ if (!ct->fixed_col && ct->num_conversations >= 1000) {
+ /* finding the right size for a column isn't easy
+ * let it run in autosize a little (1000 is arbitrary)
+ * and then switch to fixed width.
+ */
+ ct->fixed_col = TRUE;
+
+ switch_to_fixed_col(ct->table);
+ }
+
+ gtk_tree_view_set_model(GTK_TREE_VIEW(ct->table), GTK_TREE_MODEL(store));
+ g_object_unref(store);
+ }
+}
+
+static void
+draw_ct_table_data_cb(void *arg)
+{
+ draw_ct_table_data(arg);
+}
+
+typedef struct {
+ int nb_cols;
+ gint columns_order[N_COLUMNS];
+ GString *CSV_str;
+ conversations_table *talkers;
+} csv_t;
+
+/* output in C locale */
+static gboolean
+csv_handle(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
+ gpointer data)
+{
+ csv_t *csv = (csv_t *)data;
+ gchar *table_text;
+ int i;
+ unsigned idx;
+ conv_t *conv;
+ double duration_s;
+ guint64 value;
+
+ gtk_tree_model_get(model, iter, INDEX_COLUMN, &idx, -1);
+ conv=&g_array_index(csv->talkers->conversations, conv_t, idx);
+ duration_s = nstime_to_sec(&conv->stop_time) - nstime_to_sec(&conv->start_time);
+
+ for (i=0; i< csv->nb_cols; i++) {
+ if (i)
+ g_string_append(csv->CSV_str, ",");
+
+ switch(csv->columns_order[i]) {
+ case SRC_ADR_COLUMN:
+ case SRC_PORT_COLUMN:
+ case DST_ADR_COLUMN:
+ case DST_PORT_COLUMN:
+ 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 PACKETS_COLUMN:
+ case BYTES_COLUMN:
+ case PKT_AB_COLUMN:
+ case BYTES_AB_COLUMN:
+ case PKT_BA_COLUMN:
+ case BYTES_BA_COLUMN:
+ 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;
+ case START_COLUMN:
+ g_string_append_printf(csv->CSV_str, "\"%.9f\"", nstime_to_sec(&conv->start_time));
+ break;
+ case DURATION_COLUMN:
+ g_string_append_printf(csv->CSV_str, "\"%.4f\"", duration_s);
+ break;
+ case BPS_AB_COLUMN:
+ if (duration_s > 0 && conv->tx_frames > 1) {
+ g_string_append_printf(csv->CSV_str, "\"%.2f\"", (gint64) conv->tx_bytes * 8 / duration_s);
+ } else {
+ g_string_append(csv->CSV_str, "\"" NO_BPS_STR "\"");
+ }
+ break;
+ case BPS_BA_COLUMN:
+ if (duration_s > 0 && conv->rx_frames > 1) {
+ g_string_append_printf(csv->CSV_str, "\"%.2f\"", (gint64) conv->rx_bytes * 8 / duration_s);
+ } else {
+ g_string_append(csv->CSV_str, "\"" NO_BPS_STR "\"");
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ g_string_append(csv->CSV_str,"\n");
+
+ return FALSE;
+}
+
+
+static void
+copy_as_csv_cb(GtkWindow *copy_bt, gpointer data _U_)
+{
+ GtkClipboard *cb;
+ GList *columns, *list;
+ GtkTreeViewColumn *column;
+ GtkListStore *store;
+ csv_t csv;
+
+ csv.talkers=g_object_get_data(G_OBJECT(copy_bt), CONV_PTR_KEY);
+ if (!csv.talkers)
+ return;
+
+ csv.CSV_str = g_string_new("");
+
+ columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(csv.talkers->table));
+ csv.nb_cols = 0;
+ list = columns;
+ while(columns) {
+ column = columns->data;
+ if (gtk_tree_view_column_get_visible(column)) {
+ csv.columns_order[csv.nb_cols] = gtk_tree_view_column_get_sort_column_id(column);
+ if (csv.nb_cols)
+ g_string_append(csv.CSV_str, ",");
+ g_string_append_printf(csv.CSV_str, "\"%s\"", gtk_tree_view_column_get_title(column));
+ csv.nb_cols++;
+ }
+ columns = g_list_next(columns);
+ }
+ g_list_free(list);
+
+ g_string_append(csv.CSV_str,"\n");
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(csv.talkers->table)));
+ gtk_tree_model_foreach(GTK_TREE_MODEL(store), csv_handle, &csv);
+
+ /* Now that we have the CSV data, copy it into the default clipboard */
+ cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); /* Get the default clipboard */
+ gtk_clipboard_set_text(cb, csv.CSV_str->str, -1); /* Copy the CSV data into the clipboard */
+ g_string_free(csv.CSV_str, TRUE); /* Free the memory */
+}
+
+static gint default_col_size[N_COLUMNS];
+
+static void
+init_default_col_size(GtkWidget *view)
+{
+
+ default_col_size[SRC_ADR_COLUMN] = get_default_col_size(view, "00000000.000000000000");
+ default_col_size[DST_ADR_COLUMN] = default_col_size[SRC_ADR_COLUMN];
+ default_col_size[SRC_PORT_COLUMN] = get_default_col_size(view, "000000");
+ default_col_size[DST_PORT_COLUMN] = default_col_size[SRC_PORT_COLUMN];
+ default_col_size[PACKETS_COLUMN] = get_default_col_size(view, "00 000 000");
+ default_col_size[BYTES_COLUMN] = get_default_col_size(view, "0 000 000 000");
+ default_col_size[PKT_AB_COLUMN] = default_col_size[PACKETS_COLUMN];
+ default_col_size[PKT_BA_COLUMN] = default_col_size[PACKETS_COLUMN];
+ default_col_size[BYTES_AB_COLUMN] = default_col_size[BYTES_COLUMN];
+ default_col_size[BYTES_BA_COLUMN] = default_col_size[BYTES_COLUMN];
+ default_col_size[START_COLUMN] = get_default_col_size(view, "000000.000000000");
+ default_col_size[DURATION_COLUMN] = get_default_col_size(view, "000000.0000");
+ default_col_size[BPS_AB_COLUMN] = get_default_col_size(view, "000000000.00");
+ default_col_size[BPS_BA_COLUMN] = default_col_size[BPS_AB_COLUMN];
+}
+
+static gboolean
+init_ct_table_page(conversations_table *conversations, GtkWidget *vbox, gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter,
+ tap_packet_cb packet_func)
+{
+ int i;
+ GString *error_string;
+ char title[256];
+
+ GtkListStore *store;
+ GtkWidget *tree;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSortable *sortable;
+ GtkTreeSelection *sel;
+ static gboolean col_size = FALSE;
+
+ conversations->page_lb=NULL;
+ conversations->resolve_names=TRUE;
+ conversations->has_ports=!hide_ports;
+ conversations->fixed_col = FALSE;
+ conversations->default_titles[0]="Address A",
+ conversations->default_titles[1]="Port A";
+ conversations->default_titles[2]="Address B";
+ conversations->default_titles[3]="Port B";
+ conversations->default_titles[4]="Packets";
+ conversations->default_titles[5]="Bytes";
+ conversations->default_titles[6]="Packets A" UTF8_RIGHTWARDS_ARROW "B";
+ conversations->default_titles[7]="Bytes A" UTF8_RIGHTWARDS_ARROW "B";
+ conversations->default_titles[8]="Packets A" UTF8_LEFTWARDS_ARROW "B";
+ conversations->default_titles[9]="Bytes A" UTF8_LEFTWARDS_ARROW "B";
+ conversations->default_titles[10]="Rel Start";
+ conversations->default_titles[11]="Duration";
+ conversations->default_titles[12]="bps A" UTF8_RIGHTWARDS_ARROW "B";
+ conversations->default_titles[13]="bps A" UTF8_LEFTWARDS_ARROW "B";
+
+ if (strcmp(table_name, "NCP")==0) {
+ conversations->default_titles[1]="Connection A";
+ conversations->default_titles[3]="Connection B";
+ }
+
+ g_snprintf(title, sizeof(title), "%s Conversations", table_name);
+ conversations->name_lb=gtk_label_new(title);
+
+
+ /* Create the store */
+ store = gtk_list_store_new (N_COLUMNS, /* Total number of columns */
+ G_TYPE_STRING, /* Address A */
+ G_TYPE_STRING, /* Port A */
+ G_TYPE_STRING, /* Address B */
+ G_TYPE_STRING, /* Port B */
+ G_TYPE_UINT64, /* Packets */
+ G_TYPE_UINT64, /* Bytes */
+ G_TYPE_UINT64, /* Packets A->B */
+ G_TYPE_UINT64, /* Bytes A->B */
+ G_TYPE_UINT64, /* Packets A<-B */
+ G_TYPE_UINT64, /* Bytes A<-B */
+ G_TYPE_STRING, /* Start */
+ G_TYPE_STRING, /* Duration */
+ G_TYPE_STRING, /* bps A->B */
+ G_TYPE_STRING, /* bps A<-B */
+ G_TYPE_UINT); /* Index */
+
+ gtk_box_pack_start(GTK_BOX(vbox), conversations->name_lb, FALSE, FALSE, 0);
+
+ conversations->scrolled_window=scrolled_window_new(NULL, NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), conversations->scrolled_window, TRUE, TRUE, 0);
+
+ tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
+ conversations->table = GTK_TREE_VIEW(tree);
+ sortable = GTK_TREE_SORTABLE(store);
+
+ if (!col_size) {
+ col_size = TRUE;
+ init_default_col_size(GTK_WIDGET(conversations->table));
+ }
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref (G_OBJECT (store));
+
+ g_object_set_data(G_OBJECT(store), CONV_PTR_KEY, conversations);
+ g_object_set_data(G_OBJECT(conversations->table), CONV_PTR_KEY, conversations);
+
+ for (i = 0; i < N_COLUMNS -1; i++) {
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set(renderer, "ypad", 0, NULL);
+ switch(i) {
+ case SRC_ADR_COLUMN: /* addresses and ports */
+ case SRC_PORT_COLUMN:
+ case DST_ADR_COLUMN:
+ case DST_PORT_COLUMN:
+ column = gtk_tree_view_column_new_with_attributes (conversations->default_titles[i], renderer, "text",
+ i, NULL);
+ if(hide_ports && (i == 1 || i == 3)){
+ /* 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, ct_sort_func, GINT_TO_POINTER(i), NULL);
+ break;
+ case PACKETS_COLUMN: /* counts */
+ case BYTES_COLUMN:
+ case PKT_AB_COLUMN:
+ case BYTES_AB_COLUMN:
+ case PKT_BA_COLUMN:
+ case BYTES_BA_COLUMN:
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+ column = gtk_tree_view_column_new_with_attributes (conversations->default_titles[i], renderer, NULL);
+ gtk_tree_view_column_set_cell_data_func(column, renderer, u64_data_func, GINT_TO_POINTER(i), NULL);
+ break;
+ default: /* times and bps */
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+ column = gtk_tree_view_column_new_with_attributes (conversations->default_titles[i], renderer, "text",
+ i, NULL);
+
+ gtk_tree_sortable_set_sort_func(sortable, i, ct_sort_func, GINT_TO_POINTER(i), NULL);
+ break;
+ }
+ gtk_tree_view_column_set_sort_column_id(column, i);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_reorderable(column, TRUE);
+ gtk_tree_view_column_set_min_width(column, 40);
+ gtk_tree_view_column_set_fixed_width(column, default_col_size[i]);
+ gtk_tree_view_append_column (conversations->table, column);
+#if 0
+ /* for capture with ten thousands conversations it's too slow */
+ if (i == PACKETS_COLUMN) {
+ gtk_tree_view_column_clicked(column);
+ gtk_tree_view_column_clicked(column);
+ }
+#endif
+ }
+ gtk_container_add(GTK_CONTAINER(conversations->scrolled_window), (GtkWidget *)conversations->table);
+ gtk_tree_view_set_rules_hint(conversations->table, TRUE);
+ gtk_tree_view_set_headers_clickable(conversations->table, TRUE);
+ gtk_tree_view_set_reorderable (conversations->table, TRUE);
+
+ conversations->num_conversations=0;
+ conversations->conversations=NULL;
+ conversations->hashtable=NULL;
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(conversations->table));
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+
+ /* create popup menu for this table */
+ ct_create_popup_menu(conversations);
+
+ /* register the tap and rerun the taps on the packet list */
+ error_string=register_tap_listener(tap_name, conversations, filter, 0, reset_ct_table_data_cb, packet_func,
+ draw_ct_table_data_cb);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static void
+follow_stream_cb(GtkWidget *follow_stream_bt, gpointer data _U_)
+{
+ conversations_table *ct = g_object_get_data (G_OBJECT(follow_stream_bt), CONV_PTR_KEY);
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreeSelection *sel;
+ guint32 idx = 0;
+ gchar *filter;
+ conv_t *conv;
+
+ if (!ct)
+ return;
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(ct->table));
+ if (!gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No conversation selected");
+ return;
+ }
+
+ gtk_tree_model_get (model, &iter, INDEX_COLUMN, &idx, -1);
+ if (idx >= ct->num_conversations) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No conversation selected");
+ return;
+ }
+
+ conv = &g_array_index(ct->conversations, conv_t, idx);
+ filter = g_strdup_printf("%s==%s && %s==%s && %s==%s && %s==%s",
+ ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_ANY_ADDRESS),
+ ep_address_to_str(&conv->src_address),
+ ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_ANY_PORT),
+ ct_port_to_str(conv->port_type, conv->src_port),
+ ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_ANY_ADDRESS),
+ ep_address_to_str(&conv->dst_address),
+ ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_ANY_PORT),
+ ct_port_to_str(conv->port_type, conv->dst_port));
+
+ apply_selected_filter (ACTYPE_SELECTED|ACTION_MATCH, filter);
+ if (strcmp(ct->name, "TCP") == 0)
+ follow_tcp_stream_cb (follow_stream_bt, data);
+ else if (strcmp(ct->name, "UDP") == 0)
+ follow_udp_stream_cb (follow_stream_bt, data);
+ else
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Unknown stream: %s", ct->name);
+
+ g_free (filter);
+}
+
+
+void
+init_conversation_table(gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, tap_packet_cb packet_func)
+{
+ conversations_table *conversations;
+ char title[256];
+ GtkWidget *vbox;
+ GtkWidget *bbox;
+ GtkWidget *close_bt, *help_bt;
+ gboolean ret;
+ GtkWidget *copy_bt, *follow_stream_bt;
+ gboolean add_follow_stream_button = FALSE;
+
+ conversations=g_malloc0(sizeof(conversations_table));
+
+ conversations->name=table_name;
+ conversations->filter=filter;
+ conversations->use_dfilter=FALSE;
+ g_snprintf(title, sizeof(title), "%s Conversations: %s", table_name, cf_get_display_name(&cfile));
+ conversations->win = dlg_window_new(title); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(conversations->win), TRUE);
+
+ gtk_window_set_default_size(GTK_WINDOW(conversations->win), 750, 400);
+
+ vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(conversations->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ ret = init_ct_table_page(conversations, vbox, hide_ports, table_name, tap_name, filter, packet_func);
+ if(ret == FALSE) {
+ g_free(conversations);
+ return;
+ }
+
+ if ((strcmp(table_name, "TCP") == 0) || (strcmp(table_name, "UDP") == 0))
+ add_follow_stream_button = TRUE;
+
+ /* Button row. */
+ /* XXX - maybe we want to have a "Copy as CSV" stock button here? */
+ /*copy_bt = gtk_button_new_with_label ("Copy content to clipboard as CSV");*/
+ if (add_follow_stream_button)
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_COPY, WIRESHARK_STOCK_FOLLOW_STREAM, GTK_STOCK_HELP, NULL);
+ else
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_COPY, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(conversations->win, close_bt, window_cancel_button_cb);
+
+ copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY);
+ gtk_widget_set_tooltip_text(copy_bt, "Copy all statistical values of this page to the clipboard in CSV (Comma Separated Values) format.");
+ g_object_set_data(G_OBJECT(copy_bt), CONV_PTR_KEY, conversations);
+ g_signal_connect(copy_bt, "clicked", G_CALLBACK(copy_as_csv_cb), NULL);
+
+ if (add_follow_stream_button) {
+ follow_stream_bt = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_FOLLOW_STREAM);
+ g_object_set_data(G_OBJECT(follow_stream_bt), E_DFILTER_TE_KEY, main_display_filter_widget);
+ g_object_set_data(G_OBJECT(follow_stream_bt), CONV_PTR_KEY, conversations);
+ g_signal_connect(follow_stream_bt, "clicked", G_CALLBACK(follow_stream_cb), NULL);
+ }
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_CONVERSATIONS_DIALOG);
+
+ g_signal_connect(conversations->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(conversations->win, "destroy", G_CALLBACK(ct_win_destroy_cb), conversations);
+
+ gtk_widget_show_all(conversations->win);
+ window_present(conversations->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(conversations->win));
+}
+
+
+
+static void
+ct_nb_switch_page_cb(GtkNotebook *nb, gpointer *pg _U_, guint page, gpointer data)
+{
+ GtkWidget *copy_bt = (GtkWidget *) data;
+ GtkWidget *follow_stream_bt = g_object_get_data(G_OBJECT(nb), FOLLOW_STREAM_BT_KEY);
+ void **pages = g_object_get_data(G_OBJECT(nb), NB_PAGES_KEY);
+
+ page++;
+
+ if (pages && page > 0 && (int) page <= GPOINTER_TO_INT(pages[0]) && copy_bt) {
+ g_object_set_data(G_OBJECT(copy_bt), CONV_PTR_KEY, pages[page]);
+ g_object_set_data(G_OBJECT(follow_stream_bt), CONV_PTR_KEY, pages[page]);
+ }
+
+ /* Filter Stream only available for TCP and UDP */
+ if (strcmp(((conversations_table *)pages[page])->name, "TCP") == 0) {
+ gtk_widget_set_tooltip_text(follow_stream_bt, "Follow TCP Stream.");
+ gtk_widget_set_sensitive(follow_stream_bt, TRUE);
+ } else if (strcmp(((conversations_table *)pages[page])->name, "UDP") == 0) {
+ gtk_widget_set_tooltip_text(follow_stream_bt, "Follow UDP Stream.");
+ gtk_widget_set_sensitive(follow_stream_bt, TRUE);
+ } else {
+ gtk_widget_set_tooltip_text(follow_stream_bt, "Follow TCP or UDP Stream.");
+ gtk_widget_set_sensitive(follow_stream_bt, FALSE);
+ }
+}
+
+static void
+ct_win_destroy_notebook_cb(GtkWindow *win _U_, gpointer data)
+{
+ void ** pages = data;
+ int page;
+
+ /* first "page" contains the number of pages */
+ for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) {
+ ct_win_destroy_cb(NULL, pages[page]);
+ }
+ g_free(pages);
+}
+
+static conversations_table *
+init_ct_notebook_page_cb(gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, tap_packet_cb packet_func)
+{
+ gboolean ret;
+ GtkWidget *page_vbox;
+ conversations_table *conversations;
+
+ conversations=g_malloc0(sizeof(conversations_table));
+ conversations->name=table_name;
+ conversations->filter=filter;
+ conversations->resolve_names=TRUE;
+ conversations->use_dfilter=FALSE;
+
+ page_vbox=gtk_vbox_new(FALSE, 6);
+ conversations->win = page_vbox;
+ gtk_container_set_border_width(GTK_CONTAINER(page_vbox), 6);
+
+ ret = init_ct_table_page(conversations, page_vbox, hide_ports, table_name, tap_name, filter, packet_func);
+ if(ret == FALSE) {
+ g_free(conversations);
+ return NULL;
+ }
+
+ return conversations;
+}
+
+
+typedef struct {
+ gboolean hide_ports; /* hide TCP / UDP port columns */
+ const char *table_name; /* GUI output name */
+ const char *tap_name; /* internal name */
+ const char *filter; /* display filter string (unused) */
+ tap_packet_cb packet_func; /* function to be called for new incoming packets */
+} register_ct_t;
+
+
+static GSList *registered_ct_tables = NULL;
+
+void
+register_conversation_table(gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, tap_packet_cb packet_func)
+{
+ register_ct_t *table;
+
+ table = g_malloc(sizeof(register_ct_t));
+
+ table->hide_ports = hide_ports;
+ table->table_name = table_name;
+ table->tap_name = tap_name;
+ table->filter = filter;
+ table->packet_func = packet_func;
+
+ registered_ct_tables = g_slist_append(registered_ct_tables, table);
+}
+
+
+static void
+ct_resolve_toggle_dest(GtkWidget *widget, gpointer data)
+{
+ int page;
+ void ** pages = data;
+ gboolean resolve_names;
+ conversations_table *conversations;
+
+
+ resolve_names = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
+
+ for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) {
+ conversations = pages[page];
+ conversations->resolve_names = resolve_names;
+
+ draw_ct_table_addresses(conversations);
+
+ }
+}
+
+
+static void
+ct_filter_toggle_dest(GtkWidget *widget, gpointer data)
+{
+ int page;
+ void ** pages = data;
+ gboolean use_filter;
+ conversations_table *conversations = NULL;
+
+ use_filter = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
+
+ for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) {
+ conversations = pages[page];
+ conversations->use_dfilter = use_filter;
+ reset_ct_table_data(conversations);
+ }
+
+ cf_retap_packets(&cfile);
+ if (conversations) {
+ gdk_window_raise(gtk_widget_get_window(conversations->win));
+ }
+}
+
+
+void
+init_conversation_notebook_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ conversations_table *conversations;
+ char title[256];
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *bbox;
+ GtkWidget *close_bt, *help_bt;
+ GtkWidget *win;
+ GtkWidget *resolv_cb;
+ GtkWidget *filter_cb;
+ int page;
+ void ** pages;
+ GtkWidget *nb;
+ GtkWidget *page_lb;
+ GSList *current_table;
+ register_ct_t *registered;
+ GtkWidget *copy_bt;
+ GtkWidget *follow_stream_bt;
+
+ pages = g_malloc(sizeof(void *) * (g_slist_length(registered_ct_tables) + 1));
+
+ g_snprintf(title, sizeof(title), "Conversations: %s", cf_get_display_name(&cfile));
+ win = dlg_window_new(title); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(win), TRUE);
+
+ gtk_window_set_default_size(GTK_WINDOW(win), 750, 400);
+
+ vbox=gtk_vbox_new(FALSE, 6);
+ gtk_container_add(GTK_CONTAINER(win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ nb = gtk_notebook_new();
+ gtk_container_add(GTK_CONTAINER(vbox), nb);
+ g_object_set_data(G_OBJECT(nb), NB_PAGES_KEY, pages);
+
+ page = 0;
+
+ current_table = registered_ct_tables;
+ while(current_table) {
+ registered = current_table->data;
+ page_lb = gtk_label_new("");
+ conversations = init_ct_notebook_page_cb(registered->hide_ports, registered->table_name, registered->tap_name,
+ registered->filter, registered->packet_func);
+ g_object_set_data(G_OBJECT(conversations->win), CONV_PTR_KEY, conversations);
+ gtk_notebook_append_page(GTK_NOTEBOOK(nb), conversations->win, page_lb);
+ conversations->win = win;
+ conversations->page_lb = page_lb;
+ pages[++page] = conversations;
+
+ current_table = g_slist_next(current_table);
+ }
+
+ pages[0] = GINT_TO_POINTER(page);
+
+ hbox = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+ resolv_cb = gtk_check_button_new_with_mnemonic("Name resolution");
+ gtk_container_add(GTK_CONTAINER(hbox), resolv_cb);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolv_cb), TRUE);
+ gtk_widget_set_tooltip_text(resolv_cb, "Show results of name resolutions rather than the \"raw\" values. "
+ "Please note: The corresponding name resolution must be enabled.");
+ g_signal_connect(resolv_cb, "toggled", G_CALLBACK(ct_resolve_toggle_dest), pages);
+
+ filter_cb = gtk_check_button_new_with_mnemonic("Limit to display filter");
+ gtk_container_add(GTK_CONTAINER(hbox), filter_cb);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(filter_cb), FALSE);
+ gtk_widget_set_tooltip_text(filter_cb, "Limit the list to conversations matching the current display filter.");
+ g_signal_connect(filter_cb, "toggled", G_CALLBACK(ct_filter_toggle_dest), pages);
+
+ /* Button row. */
+ /* XXX - maybe we want to have a "Copy as CSV" stock button here? */
+ /*copy_bt = gtk_button_new_with_label ("Copy content to clipboard as CSV");*/
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_COPY, WIRESHARK_STOCK_FOLLOW_STREAM, GTK_STOCK_HELP, NULL);
+
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(win, close_bt, window_cancel_button_cb);
+
+ copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY);
+ gtk_widget_set_tooltip_text(copy_bt, "Copy all statistical values of this page to the clipboard in CSV (Comma Separated Values) format.");
+ g_signal_connect(copy_bt, "clicked", G_CALLBACK(copy_as_csv_cb), NULL);
+ g_object_set_data(G_OBJECT(copy_bt), CONV_PTR_KEY, pages[page]);
+
+ follow_stream_bt = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_FOLLOW_STREAM);
+ gtk_widget_set_tooltip_text(follow_stream_bt, "Follow Stream.");
+ g_object_set_data(G_OBJECT(follow_stream_bt), E_DFILTER_TE_KEY, main_display_filter_widget);
+ g_object_set_data(G_OBJECT(follow_stream_bt), CONV_PTR_KEY, pages[page]);
+ g_signal_connect(follow_stream_bt, "clicked", G_CALLBACK(follow_stream_cb), NULL);
+
+ g_object_set_data(G_OBJECT(nb), FOLLOW_STREAM_BT_KEY, follow_stream_bt);
+ g_signal_connect(nb, "switch-page", G_CALLBACK(ct_nb_switch_page_cb), copy_bt);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_CONVERSATIONS_DIALOG);
+
+ g_signal_connect(win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(win, "destroy", G_CALLBACK(ct_win_destroy_notebook_cb), pages);
+
+ gtk_widget_show_all(win);
+ window_present(win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(win));
+}
+
+typedef struct _key {
+ address addr1;
+ address addr2;
+ guint32 port1;
+ guint32 port2;
+} conv_key_t;
+
+
+/*
+ * Compute the hash value for two given address/port pairs if the match
+ * is to be exact.
+ */
+static guint
+conversation_hash(gconstpointer v)
+{
+ const conv_key_t *key = (const conv_key_t *)v;
+ guint hash_val;
+
+ hash_val = 0;
+ ADD_ADDRESS_TO_HASH(hash_val, &key->addr1);
+ hash_val += key->port1;
+ ADD_ADDRESS_TO_HASH(hash_val, &key->addr2);
+ hash_val += key->port2;
+
+ return hash_val;
+}
+
+/*
+ * Compare two conversation keys for an exact match.
+ */
+static gint
+conversation_match(gconstpointer v, gconstpointer w)
+{
+ const conv_key_t *v1 = (const conv_key_t *)v;
+ const conv_key_t *v2 = (const conv_key_t *)w;
+
+ if (v1->port1 == v2->port1 &&
+ v1->port2 == v2->port2 &&
+ ADDRESSES_EQUAL(&v1->addr1, &v2->addr1) &&
+ ADDRESSES_EQUAL(&v1->addr2, &v2->addr2)) {
+ return 1;
+ }
+
+ if (v1->port2 == v2->port1 &&
+ v1->port1 == v2->port2 &&
+ ADDRESSES_EQUAL(&v1->addr2, &v2->addr1) &&
+ ADDRESSES_EQUAL(&v1->addr1, &v2->addr2)) {
+ return 1;
+ }
+
+ /*
+ * The addresses or the ports don't match.
+ */
+ return 0;
+}
+
+
+void
+add_conversation_table_data(conversations_table *ct, const address *src, const address *dst, guint32 src_port, guint32 dst_port, int num_frames, int num_bytes, nstime_t *ts, SAT_E sat, int port_type_val)
+{
+ const address *addr1, *addr2;
+ guint32 port1, port2;
+ conv_t *conversation=NULL;
+ unsigned int conversation_idx=0;
+
+ if(src_port>dst_port){
+ addr1=src;
+ addr2=dst;
+ port1=src_port;
+ port2=dst_port;
+ } else if(src_port<dst_port){
+ addr2=src;
+ addr1=dst;
+ port2=src_port;
+ port1=dst_port;
+ } else if(CMP_ADDRESS(src, dst)<0){
+ addr1=src;
+ addr2=dst;
+ port1=src_port;
+ port2=dst_port;
+ } else {
+ addr2=src;
+ addr1=dst;
+ port2=src_port;
+ port1=dst_port;
+ }
+
+ /* if we dont have any entries at all yet */
+ if(ct->conversations==NULL){
+ ct->conversations= g_array_sized_new(FALSE, FALSE, sizeof(conv_t), 10000);
+
+ ct->hashtable = g_hash_table_new_full(conversation_hash,
+ conversation_match, /* key_equal_func */
+ g_free, /* key_destroy_func */
+ NULL); /* value_destroy_func */
+
+ }
+ else {
+ /* try to find it among the existing known conversations */
+ conv_key_t existing_key;
+
+ existing_key.addr1 = *addr1;
+ existing_key.addr2 = *addr2;
+ existing_key.port1 = port1;
+ existing_key.port2 = port2;
+ conversation_idx = GPOINTER_TO_UINT(g_hash_table_lookup(ct->hashtable, &existing_key));
+ if (conversation_idx) {
+ conversation_idx--;
+ conversation=&g_array_index(ct->conversations, conv_t, conversation_idx);
+ }
+ }
+
+ /* if we still dont know what conversation this is it has to be a new one
+ and we have to allocate it and append it to the end of the list */
+ if(conversation==NULL){
+ conv_key_t *new_key;
+ conv_t conv;
+
+ COPY_ADDRESS(&conv.src_address, addr1);
+ COPY_ADDRESS(&conv.dst_address, addr2);
+ conv.sat=sat;
+ conv.port_type=port_type_val;
+ conv.src_port=port1;
+ conv.dst_port=port2;
+ conv.rx_frames=0;
+ conv.tx_frames=0;
+ conv.rx_bytes=0;
+ conv.tx_bytes=0;
+ conv.iter_valid = FALSE;
+ conv.modified = TRUE;
+
+ if (ts) {
+ memcpy(&conv.start_time, ts, sizeof(conv.start_time));
+ memcpy(&conv.stop_time, ts, sizeof(conv.stop_time));
+ } else {
+ nstime_set_unset(&conv.start_time);
+ nstime_set_unset(&conv.stop_time);
+ }
+ g_array_append_val(ct->conversations, conv);
+ conversation_idx=ct->num_conversations;
+ conversation=&g_array_index(ct->conversations, conv_t, conversation_idx);
+
+ /* ct->conversations address is not a constant but src/dst_address.data are */
+ new_key = g_new(conv_key_t, 1);
+ SET_ADDRESS(&new_key->addr1, conversation->src_address.type, conversation->src_address.len, conversation->src_address.data);
+ SET_ADDRESS(&new_key->addr2, conversation->dst_address.type, conversation->dst_address.len, conversation->dst_address.data);
+ new_key->port1 = port1;
+ new_key->port2 = port2;
+ g_hash_table_insert(ct->hashtable, new_key, GUINT_TO_POINTER(conversation_idx +1));
+
+ ct->num_conversations++;
+ }
+
+ /* update the conversation struct */
+ conversation->modified = TRUE;
+ if( (!CMP_ADDRESS(src, addr1))&&(!CMP_ADDRESS(dst, addr2))&&(src_port==port1)&&(dst_port==port2) ){
+ conversation->tx_frames+=num_frames;
+ conversation->tx_bytes+=num_bytes;
+ } else {
+ conversation->rx_frames+=num_frames;
+ conversation->rx_bytes+=num_bytes;
+ }
+
+ if (ts) {
+ if (nstime_cmp(ts, &conversation->stop_time) > 0) {
+ memcpy(&conversation->stop_time, ts, sizeof(conversation->stop_time));
+ } else if (nstime_cmp(ts, &conversation->start_time) < 0) {
+ memcpy(&conversation->start_time, ts, sizeof(conversation->start_time));
+ }
+ }
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/gtk/conversations_table.h b/ui/gtk/conversations_table.h
new file mode 100644
index 0000000000..42596982a2
--- /dev/null
+++ b/ui/gtk/conversations_table.h
@@ -0,0 +1,122 @@
+/* conversations_table.h
+ * conversations_table 2003 Ronnie Sahlberg
+ * Helper routines common to all conversations taps.
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __CONVERSATIONS_TABLE_H__
+#define __CONVERSATIONS_TABLE_H__
+
+#include "sat.h"
+
+/** @file
+ * Conversation definitions.
+ */
+
+/** Conversation information */
+typedef struct _conversation_t {
+ address src_address; /**< source address */
+ address dst_address; /**< destination address */
+ SAT_E sat; /**< address type */
+ guint32 port_type; /**< port_type (e.g. PT_TCP) */
+ guint32 src_port; /**< source port */
+ guint32 dst_port; /**< destination port */
+
+ guint64 rx_frames; /**< number of received packets */
+ guint64 tx_frames; /**< number of transmitted packets */
+ guint64 rx_bytes; /**< number of received bytes */
+ guint64 tx_bytes; /**< number of transmitted bytes */
+
+ nstime_t start_time; /**< start time for the conversation */
+ nstime_t stop_time; /**< stop time for the conversation */
+
+ gboolean modified; /**< new to redraw the row */
+ GtkTreeIter iter;
+ gboolean iter_valid; /**< not a new row */
+} conv_t;
+
+/** Conversation widget */
+typedef struct _conversations_table {
+ const char *name; /**< the name of the table */
+ const char *filter; /**< the filter used */
+ gboolean use_dfilter; /**< use display filter */
+ GtkWidget *win; /**< GTK window */
+ GtkWidget *page_lb; /**< page label */
+ GtkWidget *name_lb; /**< name label */
+ GtkWidget *scrolled_window; /**< the scrolled window */
+ GtkTreeView *table; /**< the GTK table */
+ const char *default_titles[14]; /**< Column headers */
+ GtkWidget *menu; /**< context menu */
+ gboolean has_ports; /**< table has ports */
+ guint32 num_conversations; /**< number of conversations */
+ GArray *conversations; /**< array of conversation values */
+ GHashTable *hashtable; /**< conversations hash table */
+
+ gboolean fixed_col; /**< if switched to fixed column */
+ gboolean resolve_names; /**< resolve address names? */
+} conversations_table;
+
+/** Register the conversation table for the multiple conversation window.
+ *
+ * @param hide_ports hide the port columns
+ * @param table_name the table name to be displayed
+ * @param tap_name the registered tap name
+ * @param filter the optional filter name or NULL
+ * @param packet_func the function to be called for each incoming packet
+ */
+extern void register_conversation_table(gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, tap_packet_cb packet_func);
+
+/** Init the conversation table for the single conversation window.
+ *
+ * @param hide_ports hide the port columns
+ * @param table_name the table name to be displayed
+ * @param tap_name the registered tap name
+ * @param filter the optional filter name or NULL
+ * @param packet_func the function to be called for each incoming packet
+ */
+extern void init_conversation_table(gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, tap_packet_cb packet_func);
+
+/** Callback for "Conversations" statistics item.
+ *
+ * @param widget unused
+ * @param data unused
+ */
+extern void init_conversation_notebook_cb(GtkWidget *widget, gpointer data);
+
+/** Add some data to the conversation table.
+ *
+ * @param ct the table to add the data to
+ * @param src source address
+ * @param dst destination address
+ * @param src_port source port
+ * @param dst_port destination port
+ * @param num_frames number of packets
+ * @param num_bytes number of bytes
+ * @param ts timestamp
+ * @param sat address type
+ * @param port_type the port type (e.g. PT_TCP)
+ */
+extern void add_conversation_table_data(conversations_table *ct, const address *src, const address *dst,
+ guint32 src_port, guint32 dst_port, int num_frames, int num_bytes, nstime_t *ts,
+ SAT_E sat, int port_type);
+#endif /* __CONVERSATIONS_TABLE_H__ */
+
diff --git a/ui/gtk/conversations_tcpip.c b/ui/gtk/conversations_tcpip.c
new file mode 100644
index 0000000000..ce5f02b65a
--- /dev/null
+++ b/ui/gtk/conversations_tcpip.c
@@ -0,0 +1,84 @@
+/* conversations_tcpip.c
+ * conversations_tcpip 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-tcp.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/conversations_table.h"
+
+static int
+tcpip_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ const struct tcpheader *tcphdr=vip;
+
+ add_conversation_table_data((conversations_table *)pct, &tcphdr->ip_src, &tcphdr->ip_dst, tcphdr->th_sport, tcphdr->th_dport, 1, pinfo->fd->pkt_len, &pinfo->fd->rel_ts, SAT_NONE, PT_TCP);
+
+ return 1;
+}
+
+
+
+static void
+tcpip_conversation_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"conv,tcp,",9)){
+ filter=optarg+9;
+ } else {
+ filter=NULL;
+ }
+
+ init_conversation_table(FALSE, "TCP", "tcp", filter, tcpip_conversation_packet);
+
+}
+
+void
+tcpip_conversation_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ tcpip_conversation_init("conv,tcp",NULL);
+}
+
+void
+register_tap_listener_tcpip_conversation(void)
+{
+ register_stat_cmd_arg("conv,tcp", tcpip_conversation_init,NULL);
+ register_conversation_table(FALSE, "TCP", "tcp", NULL /*filter*/, tcpip_conversation_packet);
+}
diff --git a/ui/gtk/conversations_tr.c b/ui/gtk/conversations_tr.c
new file mode 100644
index 0000000000..13f8306872
--- /dev/null
+++ b/ui/gtk/conversations_tr.c
@@ -0,0 +1,84 @@
+/* conversations_tr.c
+ * conversations_tr 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-tr.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/conversations_table.h"
+
+static int
+tr_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ const tr_hdr *trhdr=vip;
+
+ add_conversation_table_data((conversations_table *)pct, &trhdr->src, &trhdr->dst, 0, 0, 1, pinfo->fd->pkt_len, &pinfo->fd->rel_ts, SAT_TOKENRING, PT_NONE);
+
+ return 1;
+}
+
+
+
+static void
+tr_conversation_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"conv,tr,",8)){
+ filter=optarg+8;
+ } else {
+ filter=NULL;
+ }
+
+ init_conversation_table(TRUE, "Token Ring", "tr", filter, tr_conversation_packet);
+
+}
+
+void
+tr_conversation_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ tr_conversation_init("conv,tr",NULL);
+}
+
+void
+register_tap_listener_tr_conversation(void)
+{
+ register_stat_cmd_arg("conv,tr", tr_conversation_init, NULL);
+ register_conversation_table(TRUE, "Token Ring", "tr", NULL /*filter*/, tr_conversation_packet);
+}
diff --git a/ui/gtk/conversations_udpip.c b/ui/gtk/conversations_udpip.c
new file mode 100644
index 0000000000..34360c0376
--- /dev/null
+++ b/ui/gtk/conversations_udpip.c
@@ -0,0 +1,84 @@
+/* conversations_udpip.c
+ * conversations_udpip 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-udp.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/conversations_table.h"
+
+static int
+udpip_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ const e_udphdr *udphdr=vip;
+
+ add_conversation_table_data((conversations_table *)pct, &udphdr->ip_src, &udphdr->ip_dst, udphdr->uh_sport, udphdr->uh_dport, 1, pinfo->fd->pkt_len, &pinfo->fd->rel_ts, SAT_NONE, PT_UDP);
+
+ return 1;
+}
+
+
+
+static void
+udpip_conversation_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"conv,udp,",9)){
+ filter=optarg+9;
+ } else {
+ filter=NULL;
+ }
+
+ init_conversation_table(FALSE, "UDP", "udp", filter, udpip_conversation_packet);
+
+}
+
+void
+udpip_conversation_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ udpip_conversation_init("conv,udp",NULL);
+}
+
+void
+register_tap_listener_udpip_conversation(void)
+{
+ register_stat_cmd_arg("conv,udp", udpip_conversation_init, NULL);
+ register_conversation_table(FALSE, "UDP", "udp", NULL /*filter*/, udpip_conversation_packet);
+}
diff --git a/ui/gtk/conversations_usb.c b/ui/gtk/conversations_usb.c
new file mode 100644
index 0000000000..2bd4af3c5a
--- /dev/null
+++ b/ui/gtk/conversations_usb.c
@@ -0,0 +1,81 @@
+/* conversations_usb.c 2007 Jon Smirl
+ * modified from conversations_eth.c 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/conversations_table.h"
+
+static int
+usb_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip _U_)
+{
+ add_conversation_table_data((conversations_table *)pct, &pinfo->src, &pinfo->dst, 0, 0, 1, pinfo->fd->pkt_len, &pinfo->fd->rel_ts, SAT_NONE, PT_NONE);
+
+ return 1;
+}
+
+
+
+static void
+usb_conversation_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if (!strncmp(optarg, "conv,usb,", 9)) {
+ filter = optarg + 9;
+ } else {
+ filter = NULL;
+ }
+
+ init_conversation_table(TRUE, "USB", "usb", filter, usb_conversation_packet);
+
+}
+
+void
+usb_endpoints_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ usb_conversation_init("conv,usb", NULL);
+}
+
+void
+register_tap_listener_usb_conversation(void)
+{
+ register_stat_cmd_arg("conv,usb", usb_conversation_init, NULL);
+ register_conversation_table(TRUE, "USB", "usb", NULL /*filter*/, usb_conversation_packet);
+}
diff --git a/ui/gtk/conversations_wlan.c b/ui/gtk/conversations_wlan.c
new file mode 100644
index 0000000000..b4579cfbe1
--- /dev/null
+++ b/ui/gtk/conversations_wlan.c
@@ -0,0 +1,84 @@
+/* conversations_wlan.c 2004 Giles Scott
+ * modified from conversations_eth.c 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-ieee80211.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/conversations_table.h"
+
+static int
+wlan_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ const wlan_hdr *whdr=vip;
+
+ add_conversation_table_data((conversations_table *)pct, &whdr->src, &whdr->dst, 0, 0, 1, pinfo->fd->pkt_len, &pinfo->fd->rel_ts, SAT_WLAN, PT_NONE);
+
+ return 1;
+}
+
+
+
+static void
+wlan_conversation_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"conv,wlan,",10)){
+ filter=optarg+10;
+ } else {
+ filter=NULL;
+ }
+
+ init_conversation_table(TRUE, "WLAN", "wlan", filter, wlan_conversation_packet);
+
+}
+
+void
+wlan_endpoints_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ wlan_conversation_init("conv,wlan",NULL);
+}
+
+void
+register_tap_listener_wlan_conversation(void)
+{
+ register_stat_cmd_arg("conv,wlan", wlan_conversation_init,NULL);
+ register_conversation_table(TRUE, "WLAN", "wlan", NULL /*filter*/, wlan_conversation_packet);
+}
diff --git a/ui/gtk/dcerpc_stat.c b/ui/gtk/dcerpc_stat.c
new file mode 100644
index 0000000000..e44b5bc20b
--- /dev/null
+++ b/ui/gtk/dcerpc_stat.c
@@ -0,0 +1,728 @@
+/* dcerpc_stat.c
+ * dcerpc_stat 2002 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* This module provides rpc call/reply SRT statistics to Wireshark,
+ * and displays them graphically.
+ * It is only used by Wireshark and not tshark
+ *
+ * It serves as an example on how to use the tap api.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-dcerpc.h>
+
+#include "../stat_menu.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/service_response_time_table.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/filter_autocomplete.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _dcerpcstat_t {
+ GtkWidget *win;
+ srt_stat_table srt_table;
+ const char *prog;
+ e_uuid_t uuid;
+ guint16 ver;
+ int num_procedures;
+} dcerpcstat_t;
+
+
+static gboolean
+uuid_equal(e_uuid_t *uuid1, e_uuid_t *uuid2)
+{
+ if( (uuid1->Data1 != uuid2->Data1)
+ ||(uuid1->Data2 != uuid2->Data2)
+ ||(uuid1->Data3 != uuid2->Data3)
+ ||(uuid1->Data4[0] != uuid2->Data4[0])
+ ||(uuid1->Data4[1] != uuid2->Data4[1])
+ ||(uuid1->Data4[2] != uuid2->Data4[2])
+ ||(uuid1->Data4[3] != uuid2->Data4[3])
+ ||(uuid1->Data4[4] != uuid2->Data4[4])
+ ||(uuid1->Data4[5] != uuid2->Data4[5])
+ ||(uuid1->Data4[6] != uuid2->Data4[6])
+ ||(uuid1->Data4[7] != uuid2->Data4[7]) ){
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static char *
+dcerpcstat_gen_title(dcerpcstat_t *rs)
+{
+ char *title;
+
+ title = g_strdup_printf("DCE-RPC Service Response Time statistics for %s major version %u: %s", rs->prog, rs->ver, cf_get_display_name(&cfile));
+ return title;
+}
+
+static void
+dcerpcstat_set_title(dcerpcstat_t *rs)
+{
+ char *title;
+
+ title = dcerpcstat_gen_title(rs);
+ gtk_window_set_title(GTK_WINDOW(rs->win), title);
+ g_free(title);
+}
+
+static void
+dcerpcstat_reset(void *rs_arg)
+{
+ dcerpcstat_t *rs = rs_arg;
+
+ reset_srt_table_data(&rs->srt_table);
+ dcerpcstat_set_title(rs);
+}
+
+
+static gboolean
+dcerpcstat_packet(void *rs_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *ri_arg)
+{
+ dcerpcstat_t *rs = rs_arg;
+ const dcerpc_info *ri = ri_arg;
+
+ if(!ri->call_data){
+ return FALSE;
+ }
+ if(!ri->call_data->req_frame){
+ /* we have not seen the request so we don't know the delta*/
+ return FALSE;
+ }
+ if(ri->call_data->opnum >= rs->num_procedures){
+ /* don't handle this since its outside of known table */
+ return FALSE;
+ }
+
+ /* we are only interested in reply packets */
+ if(ri->ptype != PDU_RESP){
+ return FALSE;
+ }
+
+ /* we are only interested in certain program/versions */
+ if( (!uuid_equal( (&ri->call_data->uuid), (&rs->uuid)))
+ ||(ri->call_data->ver != rs->ver)){
+ return FALSE;
+ }
+
+
+ add_srt_table_data(&rs->srt_table, ri->call_data->opnum, &ri->call_data->req_time, pinfo);
+
+
+ return TRUE;
+}
+
+static void
+dcerpcstat_draw(void *rs_arg)
+{
+ dcerpcstat_t *rs = rs_arg;
+
+ draw_srt_table_data(&rs->srt_table);
+}
+
+
+/* since the gtk2 implementation of tap is multithreaded we must protect
+ * remove_tap_listener() from modifying the list while draw_tap_listener()
+ * is running. the other protected block is in main.c
+ *
+ * there should not be any other critical regions in gtk2
+ */
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ dcerpcstat_t *rs = (dcerpcstat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(rs);
+ unprotect_thread_critical_region();
+
+ free_srt_table_data(&rs->srt_table);
+ g_free(rs);
+}
+
+
+
+/* When called, this function will create a new instance of gtk-dcerpcstat.
+ */
+static void
+gtk_dcerpcstat_init(const char *optarg, void* userdata _U_)
+{
+ dcerpcstat_t *rs;
+ guint32 i, max_procs;
+ char *title_string;
+ char *filter_string;
+ GtkWidget *vbox;
+ GtkWidget *stat_label;
+ GtkWidget *filter_label;
+ GtkWidget *bbox;
+ GtkWidget *close_bt;
+ dcerpc_sub_dissector *procs;
+ e_uuid_t uuid;
+ guint d1,d2,d3,d40,d41,d42,d43,d44,d45,d46,d47;
+ int major, minor;
+ guint16 ver;
+ int pos = 0;
+ const char *filter = NULL;
+ GString *error_string;
+ int hf_opnum;
+
+ /*
+ * XXX - DCE RPC statistics are maintained only by major version,
+ * not by major and minor version, so the minor version number is
+ * ignored.
+ *
+ * Should we just stop supporting minor version numbers here?
+ * Or should we allow it to be omitted? Or should we keep
+ * separate statistics for different minor version numbers,
+ * and allow the minor version number to be omitted, and
+ * report aggregate statistics for all minor version numbers
+ * if it's omitted?
+ */
+ if(sscanf(
+ optarg,
+ "dcerpc,srt,%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x,%d.%d,%n",
+ &d1,&d2,&d3,&d40,&d41,&d42,&d43,&d44,&d45,&d46,&d47,&major,&minor,&pos)
+ == 13) {
+ uuid.Data1 = d1;
+ uuid.Data2 = d2;
+ uuid.Data3 = d3;
+ uuid.Data4[0] = d40;
+ uuid.Data4[1] = d41;
+ uuid.Data4[2] = d42;
+ uuid.Data4[3] = d43;
+ uuid.Data4[4] = d44;
+ uuid.Data4[5] = d45;
+ uuid.Data4[6] = d46;
+ uuid.Data4[7] = d47;
+ if(pos) {
+ filter = optarg+pos;
+ } else {
+ filter = NULL;
+ }
+ } else {
+ fprintf(stderr, "wireshark: invalid \"-z dcerpc,srt,<uuid>,<major version>.<minor version>[,<filter>]\" argument\n");
+ exit(1);
+ }
+ if ((major < 0) || (major > 65535)) {
+ fprintf(stderr,"wireshark: dcerpcstat_init() Major version number %d is invalid - must be positive and <= 65535\n", major);
+ exit(1);
+ }
+ if ((minor < 0) || (minor > 65535)) {
+ fprintf(stderr,"wireshark: dcerpcstat_init() Minor version number %d is invalid - must be positive and <= 65535\n", minor);
+ exit(1);
+ }
+ ver = major;
+
+ rs = g_malloc(sizeof(dcerpcstat_t));
+ rs->prog = dcerpc_get_proto_name(&uuid, ver);
+ if(!rs->prog){
+ g_free(rs);
+ fprintf(stderr,
+ "wireshark: dcerpcstat_init() Protocol with uuid:%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x v%u not supported\n",
+ uuid.Data1,uuid.Data2,uuid.Data3,uuid.Data4[0],uuid.Data4[1],uuid.Data4[2],uuid.Data4[3],uuid.Data4[4],uuid.Data4[5],uuid.Data4[6],uuid.Data4[7],ver);
+ exit(1);
+ }
+ hf_opnum = dcerpc_get_proto_hf_opnum(&uuid, ver);
+ procs = dcerpc_get_proto_sub_dissector(&uuid, ver);
+ rs->uuid = uuid;
+ rs->ver = ver;
+
+ rs->win = dlg_window_new("dcerpc-stat"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(rs->win), TRUE);
+
+ dcerpcstat_set_title(rs);
+ gtk_window_set_default_size(GTK_WINDOW(rs->win), 550, 400);
+
+ vbox = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(rs->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ title_string = dcerpcstat_gen_title(rs);
+ stat_label = gtk_label_new(title_string);
+ g_free(title_string);
+ gtk_box_pack_start(GTK_BOX(vbox), stat_label, FALSE, FALSE, 0);
+
+ filter_string = g_strdup_printf("Filter: %s",filter ? filter : "");
+ filter_label = gtk_label_new(filter_string);
+ g_free(filter_string);
+ gtk_label_set_line_wrap(GTK_LABEL(filter_label), TRUE);
+ gtk_box_pack_start(GTK_BOX(vbox), filter_label, FALSE, FALSE, 0);
+
+ for(i=0,max_procs=0;procs[i].name;i++){
+ if(procs[i].num>max_procs){
+ max_procs = procs[i].num;
+ }
+ }
+ rs->num_procedures = max_procs+1;
+
+ /* We must display TOP LEVEL Widget before calling init_srt_table() */
+ gtk_widget_show_all(rs->win);
+
+ if(hf_opnum != -1){
+ init_srt_table(&rs->srt_table, max_procs+1, vbox, proto_registrar_get_nth(hf_opnum)->abbrev);
+ } else {
+ init_srt_table(&rs->srt_table, max_procs+1, vbox, NULL);
+ }
+
+ for(i=0;i<(max_procs+1);i++){
+ int j;
+ const char *proc_name;
+
+ proc_name = "unknown";
+ for(j=0;procs[j].name;j++){
+ if (procs[j].num == i){
+ proc_name = procs[j].name;
+ }
+ }
+
+ init_srt_table_row(&rs->srt_table, i, proc_name);
+ }
+
+
+ error_string = register_tap_listener("dcerpc", rs, filter, 0, dcerpcstat_reset, dcerpcstat_packet, dcerpcstat_draw);
+ if(error_string){
+ /* error, we failed to attach to the tap. clean up */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ free_srt_table_data(&rs->srt_table);
+ g_free(rs);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(rs->win, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(rs->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(rs->win, "destroy", G_CALLBACK(win_destroy_cb), rs);
+
+ gtk_widget_show_all(rs->win);
+ window_present(rs->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(rs->win));
+}
+
+
+
+static e_uuid_t *dcerpc_uuid_program;
+static guint16 dcerpc_version;
+static GtkWidget *dlg = NULL;
+static GtkWidget *filter_entry;
+static dcerpc_uuid_key *current_uuid_key;
+static dcerpc_uuid_value *current_uuid_value;
+static dcerpc_uuid_key *new_uuid_key;
+static dcerpc_uuid_value *new_uuid_value;
+
+static void
+dcerpcstat_start_button_clicked(GtkWidget *item _U_, gpointer data _U_)
+{
+ GString *str;
+ const char *filter;
+
+ if (dcerpc_uuid_program == NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Please select a program");
+ return;
+ }
+ str = g_string_new("dcerpc,srt");
+ g_string_append_printf(str,
+ ",%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x,%u.%u",
+ dcerpc_uuid_program->Data1, dcerpc_uuid_program->Data2,
+ dcerpc_uuid_program->Data3,
+ dcerpc_uuid_program->Data4[0], dcerpc_uuid_program->Data4[1],
+ dcerpc_uuid_program->Data4[2], dcerpc_uuid_program->Data4[3],
+ dcerpc_uuid_program->Data4[4], dcerpc_uuid_program->Data4[5],
+ dcerpc_uuid_program->Data4[6], dcerpc_uuid_program->Data4[7],
+ dcerpc_version, 0);
+ filter = gtk_entry_get_text(GTK_ENTRY(filter_entry));
+ if(filter[0] != 0){
+ g_string_append_printf(str, ",%s", filter);
+ }
+
+ gtk_dcerpcstat_init(str->str,NULL);
+ g_string_free(str, TRUE);
+}
+
+
+static void
+dcerpcstat_version_select(GtkWidget *vers_combo_box, gpointer user_data _U_)
+{
+ dcerpc_uuid_key *k;
+
+ if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(vers_combo_box), (gpointer)&k)) {
+ g_assert_not_reached(); /* Programming error: somehow no active item */
+ }
+
+ dcerpc_version = k->ver;
+}
+
+static void
+dcerpcstat_find_vers(gpointer *key, gpointer *value _U_, gpointer user_data)
+{
+ dcerpc_uuid_key *k = (dcerpc_uuid_key *)key;
+ GtkWidget *vers_combo_box = user_data;
+ char vs[5];
+
+ if(!uuid_equal(&(k->uuid), dcerpc_uuid_program)){
+ return;
+ }
+ g_snprintf(vs, sizeof(vs), "%u", k->ver);
+ ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(vers_combo_box), vs, k);
+}
+
+static void
+dcerpcstat_program_select(GtkWidget *prog_combo_box, gpointer user_data)
+{
+ dcerpc_uuid_key *k;
+ GtkWidget *vers_combo_box;
+
+ vers_combo_box = user_data;
+
+ if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(prog_combo_box), (gpointer)&k)) {
+ g_assert_not_reached(); /* Programming error: somehow no active item */
+ }
+
+ g_signal_handlers_disconnect_by_func(vers_combo_box, G_CALLBACK(dcerpcstat_version_select), NULL );
+ ws_combo_box_clear_text_and_pointer(GTK_COMBO_BOX(vers_combo_box));
+
+ g_assert((k != NULL) && "dcerpc_stat: invalid selection"); /* Somehow selected top level ?? */
+ dcerpc_uuid_program = &(k->uuid);
+
+ /* re-create version menu */
+ g_signal_handlers_disconnect_by_func(vers_combo_box, G_CALLBACK(dcerpcstat_version_select), NULL );
+ ws_combo_box_clear_text_and_pointer(GTK_COMBO_BOX(vers_combo_box));
+
+ g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_vers, vers_combo_box);
+
+ g_signal_connect(vers_combo_box, "changed", G_CALLBACK(dcerpcstat_version_select), NULL);
+ ws_combo_box_set_active(GTK_COMBO_BOX(vers_combo_box), 0); /* default: triggers dcerpcstat_version_select callback */
+
+}
+
+static GtkTreeIter
+dcerpcstat_add_program_to_menu(dcerpc_uuid_key *k, dcerpc_uuid_value *v, GtkWidget *prog_combo_box, int program_item_index)
+{
+ static GtkTreeIter iter;
+ char str[64];
+
+ switch(program_item_index%15){
+ case 0:
+ g_snprintf(str,sizeof(str),"%s ...",v->name);
+ iter = ws_combo_box_append_text_and_pointer_full(
+ GTK_COMBO_BOX(prog_combo_box), NULL, str, NULL, FALSE); /* top-level entries are insensitive */
+ break;
+
+ default:
+ break;
+ }
+
+ return ws_combo_box_append_text_and_pointer_full(
+ GTK_COMBO_BOX(prog_combo_box), &iter, v->name, k, TRUE);
+}
+
+static void
+dcerpcstat_find_next_program(gpointer *key, gpointer *value, gpointer *user_data _U_)
+{
+ dcerpc_uuid_key *k = (dcerpc_uuid_key *)key;
+ dcerpc_uuid_value *v = (dcerpc_uuid_value *)value;
+
+ /* first time called, just set new_uuid to this one */
+ if((current_uuid_key==NULL) && (new_uuid_key==NULL)){
+ new_uuid_key = k;
+ new_uuid_value = v;
+ return;
+ }
+
+ /* if we haven't got a current one yet, just check the new
+ and scan for the first one alphabetically */
+ if(current_uuid_key==NULL){
+ if(strcmp(new_uuid_value->name, v->name)>0){
+ new_uuid_key = k;
+ new_uuid_value = v;
+ return;
+ }
+ return;
+ }
+
+ /* searching for the next one we are only interested in those
+ that sorts alphabetically after the current one */
+ if(strcmp(current_uuid_value->name, v->name) >= 0){
+ /* this one doesnt so just skip it */
+ return;
+ }
+
+ /* is it the first potential new entry? */
+ if(new_uuid_key==NULL){
+ new_uuid_key = k;
+ new_uuid_value = v;
+ return;
+ }
+
+ /* does it sort before the current new one? */
+ if(strcmp(new_uuid_value->name, v->name) > 0){
+ new_uuid_key = k;
+ new_uuid_value = v;
+ return;
+ }
+
+ return;
+}
+
+
+static void
+dlg_destroy_cb(GtkWidget *w _U_, gpointer user_data _U_)
+{
+ dlg = NULL;
+}
+
+
+void gtk_dcerpcstat_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ GtkWidget *dlg_box;
+ GtkWidget *prog_box, *prog_label, *prog_combo_box;
+ GtkWidget *vers_label, *vers_combo_box;
+ GtkWidget *filter_box, *filter_bt;
+ GtkWidget *bbox, *start_button, *cancel_button;
+ GtkCellRenderer *cell_renderer;
+#if 0
+ GtkTreeIter program_first_item_iter;
+#endif
+ const char *filter;
+ int program_item_index = 0;
+
+ static construct_args_t args = {
+ "Service Response Time Statistics Filter",
+ FALSE,
+ FALSE,
+ FALSE
+ };
+
+ /* if the window is already open, bring it to front and
+ un-minimize it, as necessary */
+ if(dlg){
+ reactivate_window(dlg);
+ return;
+ }
+
+ dlg = dlg_window_new("Wireshark: Compute DCE-RPC SRT statistics");
+ gtk_window_set_default_size(GTK_WINDOW(dlg), 400, -1);
+
+ dlg_box = gtk_vbox_new(FALSE, 10);
+ gtk_container_set_border_width(GTK_CONTAINER(dlg_box), 10);
+ gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
+ gtk_widget_show(dlg_box);
+
+ /* Program box */
+ prog_box = gtk_hbox_new(FALSE, 3);
+
+ /* Program label */
+ gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
+ prog_label = gtk_label_new("Program:");
+ gtk_box_pack_start(GTK_BOX(prog_box), prog_label, FALSE, FALSE, 0);
+ gtk_widget_show(prog_label);
+
+ /* Program menu */
+ dcerpc_uuid_program = NULL; /* default: no program selected */
+
+ /* The "program combo box" is implemented with a two-level tree.
+ Each top-level of the tree has (up to) 15 selectable "program name"
+ children and shows the name of the first child of that entry
+ as "child_name ...". Each of the top-level entries can be expanded
+ (to show the children) but is "insensitive": ie: cannot be selected.
+ (dcerpcstat_add_program_to_menu() does the actual work to add entries
+ to the combo box).
+ XXX: A simpler alternative might be to just do away with all the two-level
+ complexity and just use a standard ws_combo_box... even though the
+ list of "program names" is quite large.
+ XXX: The gtkrc file distributed with Windows Wireshark has the
+ "appears-as-list" GtkComboBox style property set to 1 and thus
+ on Windows the entries for this combo box will appear as a tree-view.
+ The default is 0(FALSE). In this case the the combo box entries will
+ display as a menu with sub-menus.
+ A possibility would be to set "appears-as-list" to 0 just for this
+ particular combo box on Windows so that the entries will appear as a
+ menu even on Windows).
+ */
+ prog_combo_box = ws_combo_box_new_text_and_pointer_full(&cell_renderer);
+ {
+ /* XXX: Hack So that the top-level insensitive entries don't show
+ as "grayed out"; The "foreground normal" color is used instead.
+ This may not really be necessary but seems better to me.
+ */
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context;
+ GdkRGBA *new_rgba_fg_color;
+ context = gtk_widget_get_style_context (prog_combo_box);
+ gtk_style_context_get (context, GTK_STATE_NORMAL,
+ "color", &new_rgba_fg_color,
+ NULL);
+
+ g_object_set(cell_renderer,
+ "foreground-rgba", &new_rgba_fg_color,
+ "foreground-set", TRUE,
+ NULL);
+
+#else
+ GtkStyle *s;
+ s = gtk_widget_get_style(prog_combo_box);
+ g_object_set(cell_renderer,
+ "foreground-gdk", &(s->fg[GTK_STATE_NORMAL]),
+ "foreground-set", TRUE,
+ NULL);
+#endif
+ }
+
+ current_uuid_key = NULL;
+ current_uuid_value = NULL;
+ do {
+ new_uuid_key = NULL;
+ new_uuid_value = NULL;
+ g_hash_table_foreach(dcerpc_uuids, (GHFunc)dcerpcstat_find_next_program, NULL);
+ if(new_uuid_key){
+#if 0
+ GtkTreeIter tmp_iter;
+ tmp_iter = dcerpcstat_add_program_to_menu(new_uuid_key, new_uuid_value,
+ prog_combo_box, program_item_index);
+ if (program_item_index == 0)
+ program_first_item_iter = tmp_iter;
+#else
+ dcerpcstat_add_program_to_menu(new_uuid_key, new_uuid_value,
+ prog_combo_box, program_item_index);
+#endif
+ program_item_index += 1;
+ }
+ current_uuid_key = new_uuid_key;
+ current_uuid_value = new_uuid_value;
+ } while(new_uuid_key != NULL);
+ gtk_box_pack_start(GTK_BOX(prog_box), prog_combo_box, TRUE, TRUE, 0);
+ gtk_widget_show(prog_combo_box);
+
+ /* Version label */
+ gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
+ vers_label = gtk_label_new("Version:");
+ gtk_box_pack_start(GTK_BOX(prog_box), vers_label, FALSE, FALSE, 0);
+ gtk_widget_show(vers_label);
+
+ /* Version combo-box */
+ /* Note: version combo box rows set when dcerpcstat_program_select() callback invoked */
+ vers_combo_box = ws_combo_box_new_text_and_pointer();
+ gtk_box_pack_start(GTK_BOX(prog_box), vers_combo_box, TRUE, TRUE, 0);
+ gtk_widget_show(vers_combo_box);
+
+ g_signal_connect(prog_combo_box, "changed", G_CALLBACK(dcerpcstat_program_select), vers_combo_box);
+#if 0 /* Don't select an active entry given the way the drop down treeview appears if a default (active) entry is set */
+ ws_combo_box_set_active_iter(GTK_COMBO_BOX(prog_combo_box), &program_first_item_iter); /* triggers callback */
+#endif
+ gtk_box_pack_start(GTK_BOX(dlg_box), prog_box, TRUE, TRUE, 0);
+ gtk_widget_show(prog_box);
+
+ /* Filter box */
+ filter_box = gtk_hbox_new(FALSE, 3);
+
+ /* Filter label */
+ filter_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
+ g_signal_connect(filter_bt, "clicked", G_CALLBACK(display_filter_construct_cb), &args);
+ gtk_box_pack_start(GTK_BOX(filter_box), filter_bt, FALSE, FALSE, 0);
+ gtk_widget_show(filter_bt);
+
+ /* Filter entry */
+ filter_entry = gtk_entry_new();
+ g_signal_connect(filter_entry, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
+ g_object_set_data(G_OBJECT(filter_box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+ g_signal_connect(filter_entry, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
+ g_signal_connect(dlg, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
+ gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, TRUE, TRUE, 0);
+ filter = gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
+ if(filter){
+ gtk_entry_set_text(GTK_ENTRY(filter_entry), filter);
+ } else {
+ colorize_filter_te_as_empty(filter_entry);
+ }
+ gtk_widget_show(filter_entry);
+
+ gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
+ gtk_widget_show(filter_box);
+
+ g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_entry);
+
+ /* button box */
+ bbox = dlg_button_row_new(WIRESHARK_STOCK_CREATE_STAT, GTK_STOCK_CANCEL, NULL);
+ gtk_box_pack_start(GTK_BOX(dlg_box), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+
+ start_button = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_CREATE_STAT);
+ g_signal_connect_swapped(start_button, "clicked",
+ G_CALLBACK(dcerpcstat_start_button_clicked), NULL);
+
+ cancel_button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ window_set_cancel_button(dlg, cancel_button, window_cancel_button_cb);
+
+ g_signal_connect(dlg, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(dlg, "destroy", G_CALLBACK(dlg_destroy_cb), NULL);
+
+ /* Catch the "activate" signal on the filter text entry, so that
+ if the user types Return there, we act as if the "Create Stat"
+ button had been selected, as happens if Return is typed if some
+ widget that *doesn't* handle the Return key has the input
+ focus. */
+ dlg_set_activate(filter_entry, start_button);
+
+ gtk_widget_grab_default(start_button );
+
+ /* Give the initial focus to the "Filter" entry box. */
+ gtk_widget_grab_focus(filter_entry);
+
+ gtk_widget_show_all(dlg);
+ window_present(dlg);
+}
+
+void
+register_tap_listener_gtkdcerpcstat(void)
+{
+ register_stat_cmd_arg("dcerpc,srt,", gtk_dcerpcstat_init,NULL);
+}
diff --git a/ui/gtk/decode_as_ber.c b/ui/gtk/decode_as_ber.c
new file mode 100644
index 0000000000..13d8131ccf
--- /dev/null
+++ b/ui/gtk/decode_as_ber.c
@@ -0,0 +1,141 @@
+/* decode_as_ber.c
+ *
+ * $Id$
+ *
+ * Routines to modify BER decoding on the fly.
+ *
+ * Copyright 2006 Graeme Lunt
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/epan_dissect.h>
+#include <epan/dissectors/packet-ber.h>
+
+#include "../simple_dialog.h"
+
+#include "ui/gtk/decode_as_dlg.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/decode_as_dcerpc.h"
+#include "ui/gtk/decode_as_ber.h"
+
+
+/**************************************************/
+/* Action routines for the "Decode As..." dialog */
+/* - called when the OK button pressed */
+/**************************************************/
+
+/*
+ * This routine is called when the user clicks the "OK" button in the
+ * "Decode As..." dialog window and the ASN.1 page is foremost.
+ * This routine takes care of making any changes requested to the ASN.1
+ * decoding.
+ *
+ * @param notebook_pg A pointer to the "ASN.1" notebook page.
+ */
+static void
+decode_ber(GtkWidget *notebook_pg)
+{
+ GtkWidget *list;
+ gchar *syntax;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ syntax = NULL;
+ list = g_object_get_data(G_OBJECT(notebook_pg), E_PAGE_LIST);
+
+ if (requested_action == E_DECODE_NO)
+ gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)));
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+ if (gtk_tree_selection_get_selected(selection, &model, &iter) == FALSE)
+ {
+ syntax = NULL;
+ } else {
+ gtk_tree_model_get(model, &iter, E_LIST_S_PROTO_NAME, &syntax, -1);
+ }
+
+ if ((syntax != NULL && strcmp(syntax, "(default)") == 0) ) {
+ ber_decode_as(NULL);
+ } else {
+ ber_decode_as(syntax);
+ }
+ g_free(syntax);
+}
+
+
+/**************************************************/
+/* Dialog setup */
+/**************************************************/
+
+
+/* add an interface to the list */
+static void
+decode_ber_add_to_list(gpointer key, gpointer value, gpointer user_data)
+{
+ decode_add_to_list("ASN.1", key, value, user_data);
+}
+
+
+/* add all interfaces to the list */
+static GtkWidget *
+decode_add_ber_menu (GtkWidget *page, const gchar *table_name _U_)
+{
+ GtkWidget *scrolled_window;
+ GtkWidget *list;
+
+ decode_list_menu_start(page, &list, &scrolled_window);
+
+ ber_decode_as_foreach(decode_ber_add_to_list, list);
+ decode_list_menu_finish(list);
+ return(scrolled_window);
+}
+
+
+/* add a BER page to the notebook */
+GtkWidget *
+decode_ber_add_page (packet_info *pinfo _U_)
+{
+ GtkWidget *page_hb, *info_vb, *label, *scrolled_window;
+
+ /* create page content */
+ page_hb = gtk_hbox_new(FALSE, 5);
+ g_object_set_data(G_OBJECT(page_hb), E_PAGE_ACTION, decode_ber);
+ g_object_set_data(G_OBJECT(page_hb), E_PAGE_TABLE, "ASN.1");
+ g_object_set_data(G_OBJECT(page_hb), E_PAGE_TITLE, "ASN.1");
+
+ info_vb = gtk_vbox_new(FALSE, 5);
+ gtk_box_pack_start(GTK_BOX(page_hb), info_vb, TRUE, TRUE, 0);
+
+ /* Always enabled */
+ label = gtk_label_new("Decode ASN.1 file as:");
+ gtk_box_pack_start(GTK_BOX(info_vb), label, TRUE, TRUE, 0);
+
+ scrolled_window = decode_add_ber_menu(page_hb, "ber" /*table_name*/);
+ gtk_box_pack_start(GTK_BOX(page_hb), scrolled_window, TRUE, TRUE, 0);
+ decode_dimmable = g_slist_prepend(decode_dimmable, scrolled_window);
+
+ return(page_hb);
+}
diff --git a/ui/gtk/decode_as_ber.h b/ui/gtk/decode_as_ber.h
new file mode 100644
index 0000000000..fb910cc07e
--- /dev/null
+++ b/ui/gtk/decode_as_ber.h
@@ -0,0 +1,39 @@
+/* decode_as_ber.h
+ *
+ * $Id$
+ *
+ * Routines to modify BER decoding on the fly.
+ * Only internally used between decode_as_dlg and decode_as_ber
+ *
+ * Copyright 2006 Graeme Lunt
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __DECODE_AS_BER_H__
+#define __DECODE_AS_BER_H__
+
+/** @file
+ * "Decode As" / "User Specified Decodes" dialog box.
+ * @ingroup dialog_group
+ */
+
+#define E_PAGE_BER "notebook_page_ber" /* ber only */
+
+extern GtkWidget *
+decode_ber_add_page(packet_info *pinfo);
+
+#endif
diff --git a/ui/gtk/decode_as_dcerpc.c b/ui/gtk/decode_as_dcerpc.c
new file mode 100644
index 0000000000..202e73a8a7
--- /dev/null
+++ b/ui/gtk/decode_as_dcerpc.c
@@ -0,0 +1,426 @@
+/* decode_as_dcerpc.c
+ *
+ * $Id$
+ *
+ * Routines to modify dcerpc bindings on the fly.
+ *
+ * Copyright 2004 Ulf Lamping
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/epan_dissect.h>
+#include <epan/dissectors/packet-dcerpc.h>
+
+#include "../simple_dialog.h"
+
+#include "ui/gtk/decode_as_dlg.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/decode_as_dcerpc.h"
+
+
+/**************************************************/
+/* Typedefs & Enums */
+/**************************************************/
+
+/* list of dcerpc "Decode As" bindings */
+GSList *decode_dcerpc_bindings = NULL;
+
+/**************************************************/
+/* Global Functions */
+/**************************************************/
+
+/* inject one of our bindings into the dcerpc binding table */
+static void
+decode_dcerpc_inject_binding(gpointer data, gpointer user_data _U_)
+{
+ dcerpc_add_conv_to_bind_table((decode_dcerpc_bind_values_t *) data);
+}
+
+
+/* inject all of our bindings into the dcerpc binding table */
+static void
+decode_dcerpc_inject_bindings(gpointer data _U_) {
+ g_slist_foreach(decode_dcerpc_bindings, decode_dcerpc_inject_binding, NULL /* user_data */);
+}
+
+
+/* init this file */
+void
+decode_dcerpc_init(void) {
+ GHook* hook_init_proto;
+
+
+ /* add a hook function to the dcerpc init_protocols hook */
+ hook_init_proto = g_hook_alloc(&dcerpc_hooks_init_protos);
+ hook_init_proto->func = decode_dcerpc_inject_bindings;
+ g_hook_prepend(&dcerpc_hooks_init_protos, hook_init_proto);
+}
+
+
+/* clone a binding (uses g_malloc) */
+static decode_dcerpc_bind_values_t *
+decode_dcerpc_binding_clone(decode_dcerpc_bind_values_t *binding_in)
+{
+ decode_dcerpc_bind_values_t *stored_binding;
+
+ stored_binding = g_malloc(sizeof(decode_dcerpc_bind_values_t));
+ *stored_binding = *binding_in;
+ COPY_ADDRESS(&stored_binding->addr_a, &binding_in->addr_a);
+ COPY_ADDRESS(&stored_binding->addr_b, &binding_in->addr_b);
+ stored_binding->ifname = g_string_new(binding_in->ifname->str);
+
+ return stored_binding;
+}
+
+
+/* free a binding */
+void
+decode_dcerpc_binding_free(void *binding_in)
+{
+ decode_dcerpc_bind_values_t *binding = binding_in;
+
+ g_free((void *) binding->addr_a.data);
+ g_free((void *) binding->addr_b.data);
+ if(binding->ifname)
+ g_string_free(binding->ifname, TRUE);
+ g_free(binding);
+}
+
+
+/* compare two bindings (except the interface related things, e.g. uuid) */
+static gint
+decode_dcerpc_binding_cmp(gconstpointer a, gconstpointer b)
+{
+ const decode_dcerpc_bind_values_t *binding_a = a;
+ const decode_dcerpc_bind_values_t *binding_b = b;
+
+
+ /* don't compare uuid and ver! */
+ if(
+ ADDRESSES_EQUAL(&binding_a->addr_a, &binding_b->addr_a) &&
+ ADDRESSES_EQUAL(&binding_a->addr_b, &binding_b->addr_b) &&
+ binding_a->ptype == binding_b->ptype &&
+ binding_a->port_a == binding_b->port_a &&
+ binding_a->port_b == binding_b->port_b &&
+ binding_a->ctx_id == binding_b->ctx_id &&
+ binding_a->smb_fid == binding_b->smb_fid)
+ {
+ /* equal */
+ return 0;
+ }
+
+ /* unequal */
+ return 1;
+}
+
+
+/**************************************************/
+/* Show Changed Bindings */
+/**************************************************/
+
+
+/* add a single binding to the Show list */
+static void
+decode_dcerpc_add_show_list_single(gpointer data, gpointer user_data)
+{
+ gchar string1[20];
+
+
+ decode_dcerpc_bind_values_t *binding = data;
+
+ g_snprintf(string1, sizeof(string1), "ctx_id: %u", binding->ctx_id);
+
+ decode_add_to_show_list (
+ user_data,
+ "DCE-RPC",
+ string1,
+ "-",
+ binding->ifname->str);
+}
+
+
+/* add all bindings to the Show list */
+void
+decode_dcerpc_add_show_list(gpointer user_data)
+{
+ g_slist_foreach(decode_dcerpc_bindings, decode_dcerpc_add_show_list_single, user_data);
+}
+
+
+/**************************************************/
+/* Modify the binding routines */
+/**************************************************/
+
+
+/* removes all bindings */
+void
+decode_dcerpc_reset_all(void)
+{
+ decode_dcerpc_bind_values_t *binding;
+
+ while(decode_dcerpc_bindings) {
+ binding = decode_dcerpc_bindings->data;
+
+ decode_dcerpc_binding_free(binding);
+ decode_dcerpc_bindings = g_slist_remove(
+ decode_dcerpc_bindings,
+ decode_dcerpc_bindings->data);
+ }
+}
+
+
+/* remove a binding (looking the same way as the given one) */
+static void
+decode_dcerpc_binding_reset(
+const gchar *table_name _U_,
+decode_dcerpc_bind_values_t *binding)
+{
+ GSList *le;
+ decode_dcerpc_bind_values_t *old_binding;
+
+
+ /* find the old binding (if it exists) */
+ le = g_slist_find_custom(decode_dcerpc_bindings,
+ binding,
+ decode_dcerpc_binding_cmp);
+ if(le == NULL)
+ return;
+
+ old_binding = le->data;
+
+ decode_dcerpc_bindings = g_slist_remove(decode_dcerpc_bindings, le->data);
+
+ g_free((void *) old_binding->addr_a.data);
+ g_free((void *) old_binding->addr_b.data);
+ g_string_free(old_binding->ifname, TRUE);
+ g_free(old_binding);
+}
+
+
+/* a binding has changed (remove a previously existing one) */
+static void
+decode_dcerpc_binding_change(
+const gchar *table_name,
+decode_dcerpc_bind_values_t *binding)
+{
+
+ decode_dcerpc_bind_values_t *stored_binding;
+
+ /* remove a probably existing old binding */
+ decode_dcerpc_binding_reset(table_name, binding);
+
+ /* clone the new binding and append it to the list */
+ stored_binding = decode_dcerpc_binding_clone(binding);
+ decode_dcerpc_bindings = g_slist_append (decode_dcerpc_bindings, stored_binding);
+}
+
+
+/* a binding has changed (add/replace/remove it) */
+static void
+decode_change_one_dcerpc_binding(const gchar *table_name, decode_dcerpc_bind_values_t *binding, GtkWidget *list)
+{
+ dcerpc_uuid_key *key;
+ gchar *abbrev;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+ if (gtk_tree_selection_get_selected(selection, &model, &iter) == FALSE)
+ {
+ abbrev = NULL;
+ key = NULL;
+ } else {
+ gtk_tree_model_get(model, &iter, E_LIST_S_PROTO_NAME, &abbrev,
+ E_LIST_S_TABLE+1, &key, -1);
+ }
+
+ if (key == NULL || (abbrev != NULL && strcmp(abbrev, "(default)") == 0) ) {
+ decode_dcerpc_binding_reset(table_name, binding);
+ } else {
+ binding->ifname = g_string_new(abbrev);
+ binding->uuid = key->uuid;
+ binding->ver = key->ver;
+ decode_dcerpc_binding_change(table_name, binding);
+ }
+ g_free(abbrev);
+}
+
+
+
+/**************************************************/
+/* Action routines for the "Decode As..." dialog */
+/* - called when the OK button pressed */
+/**************************************************/
+
+/*
+ * This routine is called when the user clicks the "OK" button in the
+ * "Decode As..." dialog window and the DCE-RPC page is foremost.
+ * This routine takes care of making any changes requested to the DCE-RPC
+ * binding tables.
+ *
+ * @param notebook_pg A pointer to the "DCE-RPC" notebook page.
+ */
+static void
+decode_dcerpc(GtkWidget *notebook_pg)
+{
+ GtkWidget *list;
+ const gchar *table_name;
+ decode_dcerpc_bind_values_t *binding;
+
+
+ list = g_object_get_data(G_OBJECT(notebook_pg), E_PAGE_LIST);
+ if (requested_action == E_DECODE_NO)
+ gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)));
+
+ binding = g_object_get_data(G_OBJECT(notebook_pg), E_PAGE_BINDING);
+
+ /*table_name = g_object_get_data(G_OBJECT(notebook_pg), E_PAGE_TABLE);*/
+ table_name = "DCE-RPC";
+ decode_change_one_dcerpc_binding(table_name, binding, list);
+}
+
+
+/**************************************************/
+/* Dialog setup */
+/**************************************************/
+
+
+/* add an interface to the list */
+static void
+decode_dcerpc_add_to_list(gpointer key, gpointer value, gpointer user_data)
+{
+ /*dcerpc_uuid_key *k = key;*/
+ dcerpc_uuid_value *v = value;
+
+ if(strcmp(v->name, "(none)"))
+ decode_add_to_list("DCE-RPC", v->name, key, user_data);
+}
+
+
+/* add all interfaces to the list */
+static GtkWidget *
+decode_add_dcerpc_menu (GtkWidget *page, const gchar *table_name _U_)
+{
+ GtkWidget *scrolled_window;
+ GtkWidget *list;
+
+ decode_list_menu_start(page, &list, &scrolled_window);
+ g_hash_table_foreach(dcerpc_uuids, decode_dcerpc_add_to_list, list);
+ decode_list_menu_finish(list);
+ return(scrolled_window);
+}
+
+
+/* add a DCE-RPC page to the notebook */
+GtkWidget *
+decode_dcerpc_add_page (packet_info *pinfo)
+{
+ GtkWidget *page_hb, *info_vb, *label, *scrolled_window;
+ GString *gs = g_string_new("");
+ GString *gs2 = g_string_new("");
+ decode_dcerpc_bind_values_t *binding;
+
+
+ /* clone binding */
+ binding = g_malloc(sizeof(decode_dcerpc_bind_values_t));
+ COPY_ADDRESS(&binding->addr_a, &pinfo->src);
+ COPY_ADDRESS(&binding->addr_b, &pinfo->dst);
+ binding->ptype = pinfo->ptype;
+ binding->port_a = pinfo->srcport;
+ binding->port_b = pinfo->destport;
+ binding->ctx_id = pinfo->dcectxid;
+ binding->smb_fid = dcerpc_get_transport_salt(pinfo);
+ binding->ifname = NULL;
+ /*binding->uuid = NULL;*/
+ binding->ver = 0;
+
+ /* create page content */
+ page_hb = gtk_hbox_new(FALSE, 5);
+ g_object_set_data(G_OBJECT(page_hb), E_PAGE_ACTION, decode_dcerpc);
+ g_object_set_data(G_OBJECT(page_hb), E_PAGE_TABLE, "DCE-RPC");
+ g_object_set_data(G_OBJECT(page_hb), E_PAGE_TITLE, "DCE-RPC");
+ g_object_set_data(G_OBJECT(page_hb), E_PAGE_BINDING, binding);
+
+ info_vb = gtk_vbox_new(FALSE, 5);
+ gtk_box_pack_start(GTK_BOX(page_hb), info_vb, TRUE, TRUE, 0);
+
+ /* Always enabled */
+ label = gtk_label_new("Replace binding between:");
+ gtk_box_pack_start(GTK_BOX(info_vb), label, TRUE, TRUE, 0);
+
+ switch(binding->ptype) {
+ case(PT_TCP):
+ g_string_printf(gs2, "TCP port");
+ break;
+ case(PT_UDP):
+ g_string_printf(gs2, "UDP port");
+ break;
+ default:
+ g_string_printf(gs2, "Unknown port type");
+ }
+
+ /* XXX - how to print the address binding->addr_a? */
+ g_string_printf(gs, "Address: ToBeDone %s: %u", gs2->str, binding->port_a);
+ label = gtk_label_new(gs->str);
+ gtk_box_pack_start(GTK_BOX(info_vb), label, TRUE, TRUE, 0);
+
+ label = gtk_label_new("&");
+ gtk_box_pack_start(GTK_BOX(info_vb), label, TRUE, TRUE, 0);
+
+ /* XXX - how to print the address binding->addr_b? */
+ g_string_printf(gs, "Address: ToBeDone %s: %u", gs2->str, binding->port_b);
+ label = gtk_label_new(gs->str);
+ gtk_box_pack_start(GTK_BOX(info_vb), label, TRUE, TRUE, 0);
+
+ label = gtk_label_new("&");
+ gtk_box_pack_start(GTK_BOX(info_vb), label, TRUE, TRUE, 0);
+
+ g_string_printf(gs, "Context ID: %u", binding->ctx_id);
+ label = gtk_label_new(gs->str);
+ gtk_box_pack_start(GTK_BOX(info_vb), label, TRUE, TRUE, 0);
+
+ label = gtk_label_new("&");
+ gtk_box_pack_start(GTK_BOX(info_vb), label, TRUE, TRUE, 0);
+ gtk_widget_set_sensitive(label, binding->smb_fid);
+
+ g_string_printf(gs, "SMB FID: %u", binding->smb_fid);
+ label = gtk_label_new(gs->str);
+ gtk_box_pack_start(GTK_BOX(info_vb), label, TRUE, TRUE, 0);
+ gtk_widget_set_sensitive(label, binding->smb_fid);
+
+ /* Conditionally enabled - only when decoding packets */
+ label = gtk_label_new("with:");
+ gtk_box_pack_start(GTK_BOX(info_vb), label, TRUE, TRUE, 0);
+
+ decode_dimmable = g_slist_prepend(decode_dimmable, label);
+ scrolled_window = decode_add_dcerpc_menu(page_hb, "dcerpc" /*table_name*/);
+ gtk_box_pack_start(GTK_BOX(page_hb), scrolled_window, TRUE, TRUE, 0);
+ decode_dimmable = g_slist_prepend(decode_dimmable, scrolled_window);
+
+ g_string_free(gs, TRUE);
+
+ return(page_hb);
+}
diff --git a/ui/gtk/decode_as_dcerpc.h b/ui/gtk/decode_as_dcerpc.h
new file mode 100644
index 0000000000..07a6765b6a
--- /dev/null
+++ b/ui/gtk/decode_as_dcerpc.h
@@ -0,0 +1,156 @@
+/* decode_as_dcerpc.h
+ *
+ * $Id$
+ *
+ * Routines to modify dcerpc bindings on the fly.
+ * Only internally used between decode_as_dlg and decode_as_dcerpc
+ *
+ * Copyright 2004 Ulf Lamping
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __DECODE_AS_DCERPC_H__
+#define __DECODE_AS_DCERPC_H__
+
+/** @file
+ * "Decode As" / "User Specified Decodes" dialog box.
+ * @ingroup dialog_group
+ */
+
+
+/*
+ * Columns for a "Select" list.
+ * Note that most of these columns aren't displayed; they're attached
+ * to the row of the table as additional information.
+ */
+#define E_LIST_S_PROTO_NAME 0
+#define E_LIST_S_TABLE 1
+/* The following is for debugging in decode_add_to_list */
+#define E_LIST_S_MAX E_LIST_S_TABLE
+#define E_LIST_S_COLUMNS (E_LIST_S_MAX + 1)
+
+#define E_PAGE_LIST "notebook_page_list"
+#define E_PAGE_TABLE "notebook_page_table_name"
+#define E_PAGE_TITLE "notebook_page_title"
+#define E_PAGE_VALUE "notebook_page_value"
+
+#define E_PAGE_ACTION "notebook_page_action"
+
+#define E_PAGE_DCERPC "notebook_page_dcerpc" /* dcerpc only */
+#define E_PAGE_BINDING "notebook_page_binding" /* dcerpc only */
+
+
+/*
+ * Enum used to track which radio button is currently selected in the
+ * dialog. These buttons are labeled "Decode" and "Do not decode".
+ */
+enum action_type {
+ /* The "Decode" button is currently selected. */
+ E_DECODE_YES,
+
+ /* The "Do not decode" button is currently selected. */
+ E_DECODE_NO
+};
+
+extern enum action_type requested_action;
+
+/*
+ * A list of the dialog items that only have meaning when the user has
+ * selected the "Decode" radio button. When the "Do not decode"
+ * button is selected these items should be dimmed.
+ */
+extern GSList *decode_dimmable;
+
+/* init decode_dcerpc internals */
+extern void decode_dcerpc_init(void);
+
+/* remove all bindings */
+extern void decode_dcerpc_reset_all(void);
+
+extern void
+decode_dcerpc_add_show_list(gpointer user_data);
+
+extern GtkWidget *
+decode_dcerpc_add_page(packet_info *pinfo);
+
+extern void
+decode_dcerpc_binding_free(void *binding);
+
+
+
+/** Add an item the the Show list.
+ */
+extern void
+decode_add_to_show_list (
+gpointer list_data,
+const gchar *table_name,
+gchar *selector_name,
+const gchar *initial_proto_name,
+const gchar *current_proto_name);
+
+
+/*
+ * This routine creates one entry in the list of protocol dissector
+ * that can be used. It is called by the dissector_table_foreach_handle
+ * routine once for each entry in a dissector table's list of handles
+ * for dissectors that could be used in that table. It guarantees unique
+ * entries by iterating over the list of entries build up to this point,
+ * looking for a duplicate name. If there is no duplicate, then this
+ * entry is added to the list of possible dissectors.
+ *
+ * @param table_name The name of the dissector table currently
+ * being walked.
+ *
+ * @param value The dissector handle for this entry. This is an opaque
+ * pointer that can only be handed back to routines in the file packet.c
+ *
+ * @param user_data A data block passed into each instance of this
+ * routine. It contains information from the caller of the foreach
+ * routine, specifying information about the dissector table and where
+ * to store any information generated by this routine.
+ */
+extern void
+decode_add_to_list (const gchar *table_name, const gchar *proto_name, gpointer value, gpointer user_data);
+
+/*
+ * This routine starts the creation of a List on a notebook page. It
+ * creates both a scrolled window and a list, adds the list to the
+ * window, and attaches the list as a data object on the page.
+ *
+ * @param page A pointer to the notebook page being created.
+ *
+ * @param list_p Will be filled in with the address of a newly
+ * created List.
+ *
+ * @param scrolled_win_p Will be filled in with the address of a newly
+ * created GtkScrolledWindow.
+ */
+extern void
+decode_list_menu_start(GtkWidget *page, GtkWidget **list_p,
+ GtkWidget **scrolled_win_p);
+
+/*
+ * This routine finishes the creation of a List on a notebook page.
+ * It adds the default entry, sets the default entry as the
+ * highlighted entry, and sorts the List.
+ *
+ * @param list A pointer the the List to finish.
+ */
+extern void
+decode_list_menu_finish(GtkWidget *list);
+
+#endif
diff --git a/ui/gtk/decode_as_dlg.c b/ui/gtk/decode_as_dlg.c
new file mode 100644
index 0000000000..8f954eeb3d
--- /dev/null
+++ b/ui/gtk/decode_as_dlg.c
@@ -0,0 +1,2038 @@
+/* decode_as_dlg.c
+ *
+ * $Id$
+ *
+ * Routines to modify dissector tables on the fly.
+ *
+ * By David Hampton <dhampton@mac.com>
+ * Copyright 2001 David Hampton
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#if GTK_CHECK_VERSION(3,0,0)
+# include <gdk/gdkkeysyms-compat.h>
+#endif
+
+#include <epan/packet.h>
+#include <epan/epan_dissect.h>
+#include <epan/filesystem.h>
+#include <epan/prefs.h>
+#include <epan/prefs-int.h>
+
+#include <wsutil/file_util.h>
+
+#include "../simple_dialog.h"
+
+#include "ui/gtk/main.h"
+#include "ui/gtk/decode_as_dlg.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/decode_as_dcerpc.h"
+#include "ui/gtk/decode_as_ber.h"
+#include "ui/gtk/help_dlg.h"
+#include "utf8_entities.h"
+
+#undef DEBUG
+
+/**************************************************/
+/* Typedefs & Enums */
+/**************************************************/
+
+/*
+ * Enum used to track which transport layer port combo_box item is
+ * currently selected in the dialog. These items are labeled "source",
+ * "destination", and "source/destination".
+ */
+enum srcdst_type {
+ /* The "source port" combo_box item is currently selected. */
+ E_DECODE_SPORT,
+ /* The "destination port" combo_box item is currently selected. */
+ E_DECODE_DPORT,
+ /* The "source/destination port" combo_box item is currently selected. */
+ E_DECODE_BPORT,
+ /* For SCTP only. This MUST be the last entry! */
+ E_DECODE_PPID
+};
+
+#define E_DECODE_MIN_HEIGHT 300
+#define E_NOTEBOOK "notebook"
+
+#define E_COMBO_BOX_SRCDST "combo_box_src_dst"
+
+#define E_PAGE_DPORT "dport"
+#define E_PAGE_SPORT "sport"
+#define E_PAGE_PPID "ppid"
+#define E_PAGE_ASN1 "asn1"
+
+
+/*
+ * Columns for a "Display" list
+ */
+#define E_LIST_D_TABLE 0
+#define E_LIST_D_SELECTOR 1
+#define E_LIST_D_INITIAL 2
+#define E_LIST_D_CURRENT 3
+#define E_LIST_D_MAX E_LIST_D_CURRENT
+#define E_LIST_D_COLUMNS (E_LIST_D_MAX + 1)
+
+/**************************************************/
+/* File Global Variables */
+/**************************************************/
+
+/*
+ * Keep a static pointer to the current "Decode As" window. This is
+ * kept so that if somebody tries to do "Tools:Decode As" while
+ * there's already a "Decode As" window up, we just pop up the
+ * existing one, rather than creating a new one.
+ */
+static GtkWidget *decode_w = NULL;
+
+/*
+ * A static pointer to the current "Decode As:Show" window. This is
+ * kept so that if somebody tries to do clock the "Show Current"
+ * button or select the "Display:User Specified Decodes" menu item
+ * while there's already a "Decode As:Show" window up, we just pop up
+ * the existing one, rather than creating a new one.
+ */
+static GtkWidget *decode_show_w = NULL;
+
+/*
+ * A list of the dialog items that only have meaning when the user has
+ * selected the "Decode" radio button. When the "Do not decode"
+ * button is selected these items should be dimmed.
+ */
+GSList *decode_dimmable = NULL;
+
+/*
+ * Remember the "action" radio button that is currently selected in
+ * the dialog. This value is initialized when the dialog is created,
+ * modified in a callback routine, and read in the routine that
+ * handles a click in the "OK" button for the dialog.
+ */
+enum action_type requested_action = -1;
+
+
+/**************************************************/
+/* Global Functions */
+/**************************************************/
+
+/* init this module */
+void decode_as_init(void) {
+
+ decode_dcerpc_init();
+}
+
+/**************************************************/
+/* Reset Changed Dissectors */
+/**************************************************/
+
+/*
+ * Data structure for tracking which dissector need to be reset. This
+ * structure is necessary as a hash table entry cannot be removed
+ * while a g_hash_table_foreach walk is in progress.
+ */
+struct dissector_delete_item {
+ /* The name of the dissector table */
+ const gchar *ddi_table_name;
+ /* The type of the selector in that dissector table */
+ ftenum_t ddi_selector_type;
+ /* The selector in the dissector table */
+ union {
+ guint sel_uint;
+ char *sel_string;
+ } ddi_selector;
+};
+
+/*
+ * A typedef for the data structure to track the original dissector
+ * used for any given port on any given protocol.
+ */
+typedef struct dissector_delete_item dissector_delete_item_t;
+
+/*
+ * A list of dissectors that need to be reset.
+ */
+GSList *dissector_reset_list = NULL;
+
+/*
+ * This routine creates one entry in the list of protocol dissector
+ * that need to be reset. It is called by the g_hash_table_foreach
+ * routine once for each changed entry in a dissector table.
+ * Unfortunately it cannot delete the entry immediately as this screws
+ * up the foreach function, so it builds a list of dissectors to be
+ * reset once the foreach routine finishes.
+ *
+ * @param table_name The table name in which this dissector is found.
+ *
+ * @param key A pointer to the key for this entry in the dissector
+ * hash table. This is generally the numeric selector of the
+ * protocol, i.e. the ethernet type code, IP port number, TCP port
+ * number, etc.
+ *
+ * @param value A pointer to the value for this entry in the dissector
+ * hash table. This is an opaque pointer that can only be handed back
+ * to routine in the file packet.c - but it's unused.
+ *
+ * @param user_data Unused.
+ */
+static void
+decode_build_reset_list (const gchar *table_name, ftenum_t selector_type,
+ gpointer key, gpointer value _U_,
+ gpointer user_data _U_)
+{
+ dissector_delete_item_t *item;
+
+ item = g_malloc(sizeof(dissector_delete_item_t));
+ item->ddi_table_name = table_name;
+ item->ddi_selector_type = selector_type;
+ switch (selector_type) {
+
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ item->ddi_selector.sel_uint = GPOINTER_TO_UINT(key);
+ break;
+
+ case FT_STRING:
+ case FT_STRINGZ:
+ item->ddi_selector.sel_string = key;
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+ dissector_reset_list = g_slist_prepend(dissector_reset_list, item);
+}
+
+
+/**************************************************/
+/* Saving "Decode As" */
+/**************************************************/
+
+/*
+ * Data structure to hold information of the "Decode As" entry.
+ */
+struct da_entry {
+ gchar *table;
+ guint selector;
+ gchar *initial;
+ gchar *current;
+};
+
+/*
+ * A typedef for the "Decode As" entry.
+ */
+typedef struct da_entry da_entry_t;
+
+/*
+ * Container that holds the entries of the "Decode As"
+ */
+GSList *da_entries = NULL;
+
+/*
+ * Data structure used as user data when iterating diessector handles
+ */
+struct lookup_entry {
+ gchar* dissector_short_name;
+ dissector_handle_t handle;
+};
+
+typedef struct lookup_entry lookup_entry_t;
+
+/*
+ * Implementation of the dissector_table defined in packet.h
+ */
+struct dissector_table {
+ GHashTable *hash_table;
+ GSList *dissector_handles;
+ const char *ui_name;
+ ftenum_t type;
+ int base;
+};
+
+/*
+ * A callback function to changed a dissector_handle if matched
+ * This is used when iterating a dissector table
+ */
+void change_dissector_if_matched(gpointer item, gpointer user_data) {
+ dissector_handle_t handle = (dissector_handle_t)item;
+ lookup_entry_t * lookup = (lookup_entry_t *)user_data;
+ if (strcmp(lookup->dissector_short_name, dissector_handle_get_short_name(handle)) == 0) {
+ lookup->handle = handle;
+ }
+}
+
+/*
+ * A callback function to parse each "decode as" entry in the file and apply the change
+ */
+prefs_set_pref_e
+read_set_decode_as_entries(gchar *key, gchar *value,
+ void *user_data _U_,
+ gboolean return_range_errors _U_)
+{
+ gchar *values[4] = {NULL, NULL, NULL, NULL};
+ gchar delimiter[4] = {',', ',', ',','\0'};
+ gchar *pch;
+ guint i, j;
+ dissector_table_t sub_dissectors;
+ prefs_set_pref_e retval = PREFS_SET_OK;
+
+ if (strcmp(key, DECODE_AS_ENTRY) == 0) {
+ /* Parse csv into table, selector, initial, current */
+ for (i = 0; i < 4; i++) {
+ pch = strchr(value, delimiter[i]);
+ if (pch == NULL) {
+ for (j = 0; j < i; j++) {
+ g_free(values[j]);
+ }
+ return PREFS_SET_SYNTAX_ERR;
+ }
+ values[i] = g_strndup(value, pch - value);
+ value = pch + 1;
+ }
+ sub_dissectors = find_dissector_table(values[0]);
+ if (sub_dissectors != NULL) {
+ lookup_entry_t lookup;
+ lookup.dissector_short_name = values[3];
+ lookup.handle = NULL;
+ g_slist_foreach(sub_dissectors->dissector_handles, change_dissector_if_matched, &lookup);
+ if (lookup.handle != NULL) {
+ dissector_change_uint(values[0], atoi(values[1]), lookup.handle);
+ decode_build_reset_list(g_strdup(values[0]), sub_dissectors->type, g_strdup(values[1]), NULL, NULL);
+ }
+ } else {
+ retval = PREFS_SET_SYNTAX_ERR;
+ }
+
+ } else {
+ retval = PREFS_SET_NO_SUCH_PREF;
+ }
+
+ for (i = 0; i < 4; i++) {
+ g_free(values[i]);
+ }
+ return retval;
+}
+
+/*
+ * Save entries into preferences.
+ */
+void write_da_entry(gpointer item, gpointer user_data) {
+ da_entry_t *entry = (da_entry_t *)item;
+ FILE *daf = (FILE *)user_data;
+ fprintf (daf, DECODE_AS_ENTRY ": %s,%d,%s,%s\n", entry->table, entry->selector, entry->initial, entry->current);
+}
+
+/*
+ * Free memory used by the da_entry
+ */
+void free_da_entry(gpointer item, gpointer user_data _U_) {
+ da_entry_t *entry = (da_entry_t *)item;
+ g_free(entry->table);
+ g_free(entry->initial);
+ g_free(entry->current);
+}
+
+
+/**************************************************/
+/* Show Changed Dissectors */
+/**************************************************/
+
+#define SORT_ALPHABETICAL 0
+
+static gint
+sort_iter_compare_func (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ gint sortcol = GPOINTER_TO_INT(user_data);
+ gint ret = 0;
+ switch (sortcol)
+ {
+ case SORT_ALPHABETICAL:
+ {
+ gchar *name1, *name2;
+ gtk_tree_model_get(model, a, 0, &name1, -1);
+ gtk_tree_model_get(model, b, 0, &name2, -1);
+ if (name1 == NULL || name2 == NULL)
+ {
+ if (name1 == NULL && name2 == NULL)
+ break; /* both equal => ret = 0 */
+ ret = (name1 == NULL) ? -1 : 1;
+ }
+ else
+ {
+ ret = g_ascii_strcasecmp(name1,name2);
+ }
+ g_free(name1);
+ g_free(name2);
+ }
+ break;
+ default:
+ g_return_val_if_reached(0);
+ }
+ return ret;
+}
+
+
+void
+decode_add_to_show_list (gpointer list_data,
+ const gchar *table_name,
+ gchar *selector_name,
+ const gchar *initial_proto_name,
+ const gchar *current_proto_name)
+{
+ const gchar *text[E_LIST_D_COLUMNS];
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ store = (GtkListStore *)list_data;
+
+ text[E_LIST_D_TABLE] = table_name;
+ text[E_LIST_D_SELECTOR] = selector_name;
+ text[E_LIST_D_INITIAL] = initial_proto_name;
+ text[E_LIST_D_CURRENT] = current_proto_name;
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, E_LIST_D_TABLE, text[E_LIST_D_TABLE],
+ E_LIST_D_SELECTOR, text[E_LIST_D_SELECTOR],
+ E_LIST_D_INITIAL, text[E_LIST_D_INITIAL],
+ E_LIST_D_CURRENT, text[E_LIST_D_CURRENT], -1);
+}
+
+
+/*
+ * This routine creates one entry in the list of protocol dissector
+ * that have been changed. It is called by the g_hash_foreach routine
+ * once for each changed entry in a dissector table.
+ *
+ * @param table_name The table name in which this dissector is found.
+ *
+ * @param key A pointer to the key for this entry in the dissector
+ * hash table. This is generally the numeric selector of the
+ * protocol, i.e. the ethernet type code, IP port number, TCP port
+ * number, etc.
+ *
+ * @param value A pointer to the value for this entry in the dissector
+ * hash table. This is an opaque pointer that can only be handed back
+ * to routine in the file packet.c
+ *
+ * @param user_data A pointer to the list in which this information
+ * should be stored.
+ */
+static void
+decode_build_show_list (const gchar *table_name, ftenum_t selector_type,
+ gpointer key, gpointer value, gpointer user_data)
+{
+ dissector_handle_t current, initial;
+ const gchar *current_proto_name, *initial_proto_name;
+ gchar *selector_name;
+ gchar string1[20];
+ da_entry_t *entry;
+
+ entry = g_malloc(sizeof(da_entry_t));
+
+ g_assert(user_data);
+ g_assert(value);
+
+ current = dtbl_entry_get_handle(value);
+ if (current == NULL)
+ current_proto_name = "(none)";
+ else
+ current_proto_name = dissector_handle_get_short_name(current);
+ initial = dtbl_entry_get_initial_handle(value);
+ if (initial == NULL)
+ initial_proto_name = "(none)";
+ else
+ initial_proto_name = dissector_handle_get_short_name(initial);
+
+ switch (selector_type) {
+
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ switch (get_dissector_table_base(table_name)) {
+
+ case BASE_DEC:
+ g_snprintf(string1, sizeof(string1), "%u", GPOINTER_TO_UINT(key));
+ break;
+
+ case BASE_HEX:
+ switch (get_dissector_table_selector_type(table_name)) {
+
+ case FT_UINT8:
+ g_snprintf(string1, sizeof(string1), "0x%02x", GPOINTER_TO_UINT(key));
+ break;
+
+ case FT_UINT16:
+ g_snprintf(string1, sizeof(string1), "0x%04x", GPOINTER_TO_UINT(key));
+ break;
+
+ case FT_UINT24:
+ g_snprintf(string1, sizeof(string1), "0x%06x", GPOINTER_TO_UINT(key));
+ break;
+
+ case FT_UINT32:
+ g_snprintf(string1, sizeof(string1), "0x%08x", GPOINTER_TO_UINT(key));
+ break;
+
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ break;
+
+ case BASE_OCT:
+ g_snprintf(string1, sizeof(string1), "%#o", GPOINTER_TO_UINT(key));
+ break;
+ }
+ selector_name = string1;
+ break;
+
+ case FT_STRING:
+ case FT_STRINGZ:
+ selector_name = key;
+ break;
+
+ default:
+ g_assert_not_reached();
+ selector_name = NULL;
+ break;
+ }
+
+ decode_add_to_show_list (
+ user_data,
+ get_dissector_table_ui_name(table_name),
+ selector_name,
+ initial_proto_name,
+ current_proto_name);
+
+ entry->table = g_strdup(table_name);
+ entry->selector = GPOINTER_TO_UINT(key);
+ entry->initial = g_strdup(initial_proto_name);
+ entry->current = g_strdup(current_proto_name);
+ da_entries = g_slist_append(da_entries, entry);
+}
+
+
+/* clear all settings */
+static void
+decode_clear_all(gboolean redissect)
+{
+ dissector_delete_item_t *item;
+ GSList *tmp;
+
+ dissector_all_tables_foreach_changed(decode_build_reset_list, NULL);
+
+ for (tmp = dissector_reset_list; tmp; tmp = g_slist_next(tmp)) {
+ item = tmp->data;
+ switch (item->ddi_selector_type) {
+
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ dissector_reset_uint(item->ddi_table_name,
+ item->ddi_selector.sel_uint);
+ break;
+
+ case FT_STRING:
+ case FT_STRINGZ:
+ dissector_reset_string(item->ddi_table_name,
+ item->ddi_selector.sel_string);
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+ g_free(item);
+ }
+ g_slist_free(dissector_reset_list);
+ dissector_reset_list = NULL;
+
+ decode_dcerpc_reset_all();
+
+ if (redissect) {
+ redissect_packets();
+ }
+}
+
+
+/*
+ * This routine is called when the user clicks the "OK" button in
+ * the "Decode As:Show..." dialog window. This routine destroys the
+ * dialog box and performs other housekeeping functions.
+ *
+ * @param ok_bt A pointer to the "OK" button.
+ *
+ * @param parent_w A pointer to the dialog window.
+ */
+static void
+decode_show_ok_cb (GtkWidget *ok_bt _U_, gpointer parent_w)
+{
+ window_destroy(GTK_WIDGET(parent_w));
+}
+
+
+/*
+ * This routine is called when the user clicks the "Clear" button in
+ * the "Decode As:Show..." dialog window. This routine resets all the
+ * dissector values and then destroys the dialog box and performs
+ * other housekeeping functions.
+ *
+ * @param clear_bt A pointer to the "Clear" button.
+ *
+ * @param parent_w A pointer to the dialog window.
+ */
+static void
+decode_show_clear_cb (GtkWidget *clear_bt _U_, gpointer parent_w)
+{
+ decode_clear_all(TRUE);
+
+ window_destroy(GTK_WIDGET(parent_w));
+
+ decode_show_cb(NULL, NULL);
+}
+
+
+/*
+ * This routine is called when the user clicks the X at the top right end in
+ * the "Decode As:Show..." dialog window. This routine simply calls the
+ * ok routine as if the user had clicked the ok button.
+ *
+ * @param win A pointer to the dialog box.
+ *
+ * @param event A pointer to the event struct
+ *
+ * @param user_data Unused
+ */
+static gboolean
+decode_show_delete_cb (GtkWidget *win _U_, GdkEvent *event _U_, gpointer user_data _U_)
+{
+ decode_show_ok_cb(NULL, decode_show_w);
+ return FALSE;
+}
+
+
+/*
+ * This routine is called at the destruction of the "Decode As:Show"
+ * dialog box. It clears the pointer maintained by this file, so that
+ * the next time the user clicks the "Decode As:Show" button a new
+ * dialog box will be created.
+ *
+ * @param win A pointer to the dialog box.
+ *
+ * @param user_data Unused
+ */
+static void
+decode_show_destroy_cb (GtkWidget *win _U_, gpointer user_data _U_)
+{
+ /* Note that we no longer have a "Decode As:Show" dialog box. */
+ decode_show_w = NULL;
+
+ /* Clear saved "Decode As" entries. */
+ g_slist_foreach(da_entries, free_da_entry, NULL);
+ g_slist_free(da_entries);
+ da_entries = NULL;
+}
+
+
+/*
+ * This routine saves the current "Decode As"-entries into the
+ * preferences file
+ *
+ * @param win Unused
+ *
+ * @param user_data Unused
+ */
+void
+decode_show_save_cb (GtkWidget *win _U_, gpointer user_data _U_)
+{
+ char *pf_dir_path;
+ char *daf_path;
+ FILE *daf;
+
+ if (create_persconffile_dir(&pf_dir_path) == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't create directory\n\"%s\"\nfor recent file: %s.", pf_dir_path,
+ g_strerror(errno));
+ g_free(pf_dir_path);
+ }
+
+ daf_path = get_persconffile_path(DECODE_AS_ENTRIES_FILE_NAME, TRUE, TRUE);
+ if ((daf = ws_fopen(daf_path, "w")) == NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't open decode_as_entries file\n\"%s\": %s.", daf_path,
+ g_strerror(errno));
+ g_free(daf_path);
+ }
+
+ fputs("# \"Decode As\" entries file for Wireshark " VERSION ".\n"
+ "#\n"
+ "# This file is regenerated when saving the \"Decode As...\" list.\n"
+ "# So be careful, if you want to make manual changes here.\n"
+ "\n"
+ "######## Decode As table entries, can be altered through command line ########\n"
+ "\n", daf);
+
+ g_slist_foreach(da_entries, write_da_entry, daf);
+
+ fclose(daf);
+}
+
+
+/*
+ * This routine creates the "Decode As:Show" dialog box. This dialog box
+ * shows the user which protocols have had their dissectors changed.
+ *
+ * @param w Unused
+ *
+ * @param user_data Unused
+ */
+void
+decode_show_cb (GtkWidget *w _U_, gpointer user_data _U_)
+{
+ GtkWidget *main_vb, *bbox, *ok_bt, *clear_bt, *save_bt, *help_bt, *scrolled_window;
+ const gchar *titles[E_LIST_D_COLUMNS] = {
+ "Table", "Value", "Initial", "Current"
+ };
+ gint column;
+ GtkListStore *store;
+ GtkTreeView *list;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *tc;
+ GtkTreeIter iter;
+
+ if (decode_show_w != NULL) {
+ /* There's already a "Decode As" dialog box; reactivate it. */
+ reactivate_window(decode_show_w);
+ return;
+ }
+
+ decode_show_w = dlg_window_new("Wireshark: Decode As: Show");
+ /* Provide a minimum of a couple of rows worth of data */
+ gtk_window_set_default_size(GTK_WINDOW(decode_show_w), -1, E_DECODE_MIN_HEIGHT);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(decode_show_w), main_vb);
+
+ /* Initialize list */
+ store = gtk_list_store_new(E_LIST_D_COLUMNS, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+ list = GTK_TREE_VIEW(tree_view_new(GTK_TREE_MODEL(store)));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list), FALSE);
+ gtk_tree_selection_set_mode(gtk_tree_view_get_selection(list),
+ GTK_SELECTION_NONE);
+
+ for (column = 0; column < E_LIST_D_COLUMNS; column++) {
+ renderer = gtk_cell_renderer_text_new();
+ tc = gtk_tree_view_column_new_with_attributes(titles[column],
+ renderer, "text",
+ column, NULL);
+ gtk_tree_view_column_set_sizing(tc, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(list, tc);
+ }
+
+ /* Add data */
+ dissector_all_tables_foreach_changed(decode_build_show_list, store);
+ g_object_unref(G_OBJECT(store));
+ decode_dcerpc_add_show_list(store);
+
+ /* Put list into a scrolled window */
+ scrolled_window = scrolled_window_new(NULL, NULL);
+ /* this will result to set the width of the dialog to the required size */
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_SHADOW_IN);
+ gtk_container_add(GTK_CONTAINER(scrolled_window),
+ GTK_WIDGET(list));
+ gtk_box_pack_start(GTK_BOX(main_vb), scrolled_window, TRUE, TRUE, 0);
+
+ /* Button row */
+ bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_CLEAR, GTK_STOCK_SAVE, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+
+ ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(decode_show_ok_cb), decode_show_w);
+
+ clear_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLEAR);
+ g_signal_connect(clear_bt, "clicked", G_CALLBACK(decode_show_clear_cb), decode_show_w);
+
+ save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
+ g_signal_connect(save_bt, "clicked", G_CALLBACK(decode_show_save_cb), decode_show_w);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_DECODE_AS_SHOW_DIALOG);
+
+ /* set ok as default, this button won't change anything */
+ window_set_cancel_button(decode_show_w, ok_bt, NULL);
+
+ g_signal_connect(decode_show_w, "delete_event", G_CALLBACK(decode_show_delete_cb), NULL);
+ g_signal_connect(decode_show_w, "destroy", G_CALLBACK(decode_show_destroy_cb), NULL);
+
+ gtk_widget_set_sensitive(clear_bt,
+ gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter));
+
+ gtk_widget_show_all(decode_show_w);
+ window_present(decode_show_w);
+}
+
+
+/**************************************************/
+/* Modify the dissector routines */
+/**************************************************/
+
+/*
+ * Modify a single dissector. This routine first takes care of
+ * updating the internal table of original protocol/port/dissector
+ * combinations by adding a new entry (or removing an existing entry
+ * if the value is being set back to its default). This routine then
+ * performs the actual modification to the packet dissector tables.
+ *
+ * @param s Pointer to a string buffer. This buffer is used to build
+ * up a message indicating which ports have had their dissector
+ * changed. This output will be displayed all at once after all
+ * dissectors have been modified.
+ *
+ * @param table_name The table name in which the dissector should be
+ * modified.
+ *
+ * @param selector An enum value indication which selector value
+ * (i.e. IP protocol number, TCP port number, etc.)is to be changed.
+ *
+ * @param list The List in which all the selection information can
+ * be found.
+ *
+ * @return gchar * Pointer to the next free location in the string
+ * buffer.
+ */
+static void
+decode_change_one_dissector(gchar *table_name, guint selector, GtkWidget *list)
+{
+ dissector_handle_t handle;
+ gchar *abbrev;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ guint *selector_type;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+ if (gtk_tree_selection_get_selected(selection, &model, &iter) == FALSE)
+ {
+ abbrev = NULL;
+ handle = NULL;
+ } else {
+ gtk_tree_model_get(model, &iter, E_LIST_S_PROTO_NAME, &abbrev,
+ E_LIST_S_TABLE+1, &handle, -1);
+ }
+
+ if (abbrev != NULL && strcmp(abbrev, "(default)") == 0) {
+ dissector_reset_uint(table_name, selector);
+ } else {
+ dissector_change_uint(table_name, selector, handle);
+ }
+ selector_type = g_malloc(sizeof(guint));
+ *selector_type = selector;
+ decode_build_reset_list(g_strdup(table_name), FT_UINT32, selector_type, NULL, NULL);
+ g_free(abbrev);
+}
+
+
+/**************************************************/
+/* Action routines for the "Decode As..." dialog */
+/* - called when the OK button pressed */
+/* - one per notebook page */
+/**************************************************/
+
+
+#ifdef DEBUG
+/*
+ * Print debugging information about tree view selection. Extract all
+ * information from the tree view entry that was selected and print it to
+ * a dialog window.
+ *
+ * @param tree_view The tree view to dump.
+ *
+ * @param leadin A string to print at the start of each line.
+ */
+static void
+decode_debug (GtkTreeView *tree_view, gchar *leadin)
+{
+ GtkListStore *store;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ char *string, *text[E_LIST_S_COLUMNS];
+ dissector_handle_t handle;
+
+ selection = gtk_tree_view_get_selection(tree_view);
+
+ if (gtk_tree_selection_get_selected(selection, NULL, &iter)){
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(tree_view));
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
+ E_LIST_S_PROTO_NAME, &text[E_LIST_S_PROTO_NAME],
+ E_LIST_S_TABLE, &text[E_LIST_S_TABLE],
+ E_LIST_S_TABLE+1, &handle,
+ -1);
+ string = g_strdup_printf("%s list: <put handle here>, name %s, table %s",
+ leadin, text[E_LIST_S_PROTO_NAME],
+ text[E_LIST_S_TABLE]);
+ } else {
+ string = g_strdup_printf("%s list row (none), aka do not decode", leadin);
+ }
+ simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK, string);
+ g_free(string);
+}
+#endif
+
+
+/*
+ * This routine is called when the user clicks the "OK" button in the
+ * "Decode As..." dialog window and a 'simple' page is foremost.
+ * This routine takes care of making any changes requested to the
+ * dissector tables. This routine is currently used for IP and
+ * Ethertypes. Any 'single change' notebook page can use this
+ * routine.
+ *
+ * @param notebook_pg A pointer to the "network" notebook page.
+ */
+static void
+decode_simple (GtkWidget *notebook_pg)
+{
+ GtkWidget *list;
+#ifdef DEBUG
+ gchar *string;
+#endif
+ gchar *table_name;
+ guint value;
+
+ list = g_object_get_data(G_OBJECT(notebook_pg), E_PAGE_LIST);
+ if (requested_action == E_DECODE_NO)
+ gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)));
+
+#ifdef DEBUG
+ string = g_object_get_data(G_OBJECT(notebook_pg), E_PAGE_TITLE);
+ decode_debug(GTK_TREE_VIEW(list), string);
+#endif
+
+ table_name = g_object_get_data(G_OBJECT(notebook_pg), E_PAGE_TABLE);
+ value = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(notebook_pg), E_PAGE_VALUE));
+ decode_change_one_dissector(table_name, value, list);
+}
+
+
+/*
+ * This routine is called when the user clicks the "OK" button in the
+ * "Decode As..." dialog window and the transport page is foremost.
+ * This routine takes care of making any changes requested to the TCP
+ * or UDP dissector tables.
+ *
+ * @param notebook_pg A pointer to the "transport" notebook page.
+ */
+static void
+decode_transport(GtkWidget *notebook_pg)
+{
+ GtkWidget *combo_box;
+ GtkWidget *list;
+ gchar *table_name;
+ gint requested_srcdst, requested_port, ppid;
+ gpointer portp;
+ gpointer ptr;
+#ifdef DEBUG
+ gchar *string;
+#endif
+
+ list = g_object_get_data(G_OBJECT(notebook_pg), E_PAGE_LIST);
+ if (requested_action == E_DECODE_NO)
+ gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)));
+
+ combo_box = g_object_get_data(G_OBJECT(notebook_pg), E_COMBO_BOX_SRCDST);
+ if (!ws_combo_box_get_active_pointer(GTK_COMBO_BOX(combo_box), &ptr))
+ g_assert_not_reached(); /* Programming error if no active item in combo_box */
+ requested_srcdst = GPOINTER_TO_INT(ptr);
+
+#ifdef DEBUG
+ string = g_object_get_data(G_OBJECT(notebook_pg), E_PAGE_TITLE);
+ decode_debug(GTK_TREE_VIEW(list), string);
+#endif
+
+ table_name = g_object_get_data(G_OBJECT(notebook_pg), E_PAGE_TABLE);
+ if (requested_srcdst >= E_DECODE_PPID) {
+ if (requested_srcdst == E_DECODE_PPID)
+ ppid = 0;
+ else
+ if (requested_srcdst - E_DECODE_PPID - 1 < MAX_NUMBER_OF_PPIDS)
+ ppid = cfile.edt->pi.ppids[requested_srcdst - E_DECODE_PPID - 1];
+ else
+ return;
+ decode_change_one_dissector(table_name, ppid, list);
+ return;
+ }
+ if (requested_srcdst != E_DECODE_DPORT) {
+ portp = g_object_get_data(G_OBJECT(notebook_pg), E_PAGE_SPORT);
+ if (portp != NULL) {
+ requested_port = GPOINTER_TO_INT(portp);
+ decode_change_one_dissector(table_name, requested_port, list);
+ }
+ }
+ if (requested_srcdst != E_DECODE_SPORT) {
+ portp = g_object_get_data(G_OBJECT(notebook_pg), E_PAGE_DPORT);
+ if (portp != NULL) {
+ requested_port = GPOINTER_TO_INT(portp);
+ decode_change_one_dissector(table_name, requested_port, list);
+ }
+ }
+}
+
+
+/**************************************************/
+/* Signals from the "Decode As..." dialog */
+/**************************************************/
+
+/*
+ * This routine is called when the user clicks the "OK" button in the
+ * "Decode As..." dialog window. This routine calls various helper
+ * routines to set/clear dissector values as requested by the user.
+ * These routines accumulate information on what actions they have
+ * taken, and this summary information is printed by this routine.
+ * This routine then destroys the dialog box and performs other
+ * housekeeping functions.
+ *
+ * @param ok_bt A pointer to the "OK" button.
+ *
+ * @param parent_w A pointer to the dialog window.
+ */
+static void
+decode_ok_cb (GtkWidget *ok_bt _U_, gpointer parent_w)
+{
+ GtkWidget *notebook, *notebook_pg;
+ void (* func)(GtkWidget *);
+ gint page_num;
+ void *binding = NULL;
+
+ /* Call the right routine for the page that was currently in front. */
+ notebook = g_object_get_data(G_OBJECT(parent_w), E_NOTEBOOK);
+ page_num = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
+ notebook_pg = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page_num);
+
+ func = g_object_get_data(G_OBJECT(notebook_pg), E_PAGE_ACTION);
+ func(notebook_pg);
+
+ /* Now destroy the "Decode As" dialog. */
+ notebook_pg = g_object_get_data(G_OBJECT(parent_w), E_PAGE_DCERPC);
+ if(notebook_pg) {
+ binding = g_object_get_data(G_OBJECT(notebook_pg), E_PAGE_BINDING);
+ }
+ if(binding) {
+ decode_dcerpc_binding_free(binding);
+ }
+ window_destroy(GTK_WIDGET(parent_w));
+ g_slist_free(decode_dimmable);
+ decode_dimmable = NULL;
+
+ redissect_packets();
+}
+
+/*
+ * This routine is called when the user clicks the "Apply" button in the
+ * "Decode As..." dialog window. This routine calls various helper
+ * routines to set/clear dissector values as requested by the user.
+ * These routines accumulate information on what actions they have
+ * taken, and this summary information is printed by this routine.
+ *
+ * @param apply_bt A pointer to the "Apply" button.
+ *
+ * @param parent_w A pointer to the dialog window.
+ */
+static void
+decode_apply_cb (GtkWidget *apply_bt _U_, gpointer parent_w)
+{
+ GtkWidget *notebook, *notebook_pg;
+ void (* func)(GtkWidget *);
+ gint page_num;
+
+ /* Call the right routine for the page that was currently in front. */
+ notebook = g_object_get_data(G_OBJECT(parent_w), E_NOTEBOOK);
+ page_num = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
+ notebook_pg = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page_num);
+
+ func = g_object_get_data(G_OBJECT(notebook_pg), E_PAGE_ACTION);
+ func(notebook_pg);
+
+ redissect_packets();
+}
+
+/*
+ * This routine is called when the user clicks the "Close" button in
+ * the "Decode As..." dialog window. This routine then destroys the
+ * dialog box and performs other housekeeping functions.
+ *
+ * @param close_bt A pointer to the "Close" button.
+ *
+ * @param parent_w A pointer to the dialog window.
+ */
+static void
+decode_close_cb (GtkWidget *close_bt _U_, gpointer parent_w)
+{
+ GtkWidget *notebook_pg = NULL;
+ void *binding = NULL;
+
+
+ notebook_pg = g_object_get_data(G_OBJECT(parent_w), E_PAGE_DCERPC);
+ if(notebook_pg) {
+ binding = g_object_get_data(G_OBJECT(notebook_pg), E_PAGE_BINDING);
+ }
+ if(binding) {
+ decode_dcerpc_binding_free(binding);
+ }
+ window_destroy(GTK_WIDGET(parent_w));
+ g_slist_free(decode_dimmable);
+ decode_dimmable = NULL;
+}
+
+
+/*
+ * This routine is called when the user clicks the "Close" button in
+ * the "Decode As..." dialog window. This routine simply calls the
+ * close routine as if the user had clicked the close button instead
+ * of the close button.
+ *
+ * @param decode_w_lcl A pointer to the dialog box.
+ *
+ * @param event A pointer to the GdkEvent struct
+ *
+ * @param user_data Unused
+ */
+static gboolean
+decode_delete_cb (GtkWidget *decode_w_lcl, GdkEvent *event _U_, gpointer user_data _U_)
+{
+ decode_close_cb(NULL, decode_w_lcl);
+ return FALSE;
+}
+
+
+/*
+ * This routine is called at the destruction of the "Decode As..."
+ * dialog box. It clears the pointer maintained by this file, so that
+ * the next time the user selects the "Decode As..." menu item a new
+ * dialog box will be created.
+ *
+ * @param win A pointer to the dialog box.
+ *
+ * @param user_data Unused
+ *
+ * @return void
+ */
+static void
+decode_destroy_cb (GtkWidget *win _U_, gpointer user_data _U_)
+{
+ /* Note that we no longer have a "Decode As" dialog box. */
+ decode_w = NULL;
+}
+
+
+/*
+ * This routine is called when the user clicks the "Clear" button in
+ * the "Decode As..." dialog window. This routine resets all the
+ * dissector values and performs other housekeeping functions.
+ *
+ * @param clear_bt A pointer to the "Clear" button.
+ *
+ * @param user_data Unused
+ */
+static void
+decode_clear_cb(GtkWidget *clear_bt _U_, gpointer user_data _U_)
+{
+ decode_clear_all(TRUE);
+}
+
+
+
+/**************************************************/
+/* Dialog setup - radio buttons */
+/**************************************************/
+
+/*
+ * Update the requested action field of the dialog. This routine is
+ * called by GTK when either of the two radio buttons in the dialog is
+ * clicked.
+ *
+ * @param w The radio button that was clicked.
+ *
+ * @param user_data The enum value assigned to this radio button. This
+ * will be either E_DECODE_YES or E_DECODE_NO
+ */
+static void
+decode_update_action (GtkWidget *w _U_, gpointer user_data)
+{
+ GSList *tmp;
+ gboolean enable;
+
+ requested_action = GPOINTER_TO_INT(user_data);
+ enable = (requested_action == E_DECODE_YES);
+ for (tmp = decode_dimmable; tmp; tmp = g_slist_next(tmp)) {
+ gtk_widget_set_sensitive(tmp->data, enable);
+ }
+}
+
+/*
+ * This routine is called to create the "Decode" and "Do not decode"
+ * radio buttons. These buttons are installed into a vbox, and set up
+ * as a format group.
+ *
+ * @return GtkWidget * A pointer to the vbox containing the buttons
+ */
+static GtkWidget *
+decode_add_yes_no (void)
+{
+ GtkWidget *format_vb, *radio_button;
+ GSList *format_grp;
+
+ format_vb = gtk_vbox_new(FALSE, 2);
+
+ radio_button = gtk_radio_button_new_with_label(NULL, "Decode");
+ format_grp = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio_button));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_button), TRUE);
+ g_signal_connect(radio_button, "clicked", G_CALLBACK(decode_update_action),
+ GINT_TO_POINTER(E_DECODE_YES));
+ gtk_box_pack_start(GTK_BOX(format_vb), radio_button, TRUE, TRUE, 0);
+
+ radio_button = gtk_radio_button_new_with_label(format_grp, "Do not decode");
+ g_signal_connect(radio_button, "clicked", G_CALLBACK(decode_update_action),
+ GINT_TO_POINTER(E_DECODE_NO));
+ gtk_box_pack_start(GTK_BOX(format_vb), radio_button, TRUE, TRUE, 0);
+
+ return(format_vb);
+}
+
+/**************************************************/
+/* Dialog setup - simple combo_boxes */
+/**************************************************/
+
+/*
+ * This routine is called to pack an combo_box into an aligment, so
+ * that it doesn't expand vertically to fill up the space available to
+ * it.
+ *
+ * @param combo_box A pointer to the option menu to be so packed.
+ *
+ * @return GtkWidget * A pointer to the newly created alignment.
+ */
+static GtkWidget *
+decode_add_pack_combo_box (GtkWidget *combo_box)
+{
+ GtkWidget *alignment;
+
+ alignment = gtk_alignment_new(0.0f, 0.5f, 0.0f, 0.0f);
+ gtk_container_add(GTK_CONTAINER(alignment), combo_box);
+
+ return(alignment);
+}
+
+
+/*
+ * This routine is called to add the transport port selection combo_box to
+ * the dialog box. This is a three choice combo_box: source, destination
+ * and both. The default choice for the combo_box is set to the source
+ * port number of the currently selected packet.
+ *
+ * @param page A pointer notebook page that will contain all
+ * widgets created by this routine.
+ *
+ * @return GtkWidget * A pointer to the newly created alignment into
+ * which we've packed the newly created combo_box.
+ */
+static GtkWidget *
+decode_add_srcdst_combo_box (GtkWidget *page)
+{
+ GtkWidget *combo_box, *alignment;
+ gchar tmp[100];
+
+ combo_box = ws_combo_box_new_text_and_pointer();
+
+ g_snprintf(tmp, sizeof(tmp), "Source (%u%s)", cfile.edt->pi.srcport, UTF8_RIGHTWARDS_ARROW);
+ ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(combo_box), tmp, GINT_TO_POINTER(E_DECODE_SPORT));
+
+ g_snprintf(tmp, sizeof(tmp), "Destination (%s%u)", UTF8_RIGHTWARDS_ARROW, cfile.edt->pi.destport);
+ ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(combo_box), tmp, GINT_TO_POINTER(E_DECODE_DPORT));
+
+ g_snprintf(tmp, sizeof(tmp), "Both (%u%s%u)", cfile.edt->pi.srcport,UTF8_LEFT_RIGHT_ARROW, cfile.edt->pi.destport);
+ ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(combo_box), tmp, GINT_TO_POINTER(E_DECODE_BPORT));
+ ws_combo_box_set_active(GTK_COMBO_BOX(combo_box), 2); /* default "both" */
+ g_object_set_data(G_OBJECT(page), E_COMBO_BOX_SRCDST, combo_box);
+
+ g_object_set_data(G_OBJECT(page), E_PAGE_SPORT, GINT_TO_POINTER(cfile.edt->pi.srcport));
+ g_object_set_data(G_OBJECT(page), E_PAGE_DPORT, GINT_TO_POINTER(cfile.edt->pi.destport));
+
+ alignment = decode_add_pack_combo_box(combo_box);
+ return(alignment);
+}
+
+static GtkWidget *
+decode_add_ppid_combo_box (GtkWidget *page)
+{
+ GtkWidget *combo_box;
+ gchar tmp[100];
+ guint number_of_ppid;
+
+ combo_box = ws_combo_box_new_text_and_pointer();
+
+ g_snprintf(tmp, sizeof(tmp), "PPID (%u)", 0);
+ ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(combo_box),
+ tmp, GINT_TO_POINTER(E_DECODE_PPID));
+ ws_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0); /* default */
+
+ for(number_of_ppid = 0; number_of_ppid < MAX_NUMBER_OF_PPIDS; number_of_ppid++) {
+ if (cfile.edt->pi.ppids[number_of_ppid] != 0) {
+ g_snprintf(tmp, sizeof(tmp), "PPID (%u)", cfile.edt->pi.ppids[number_of_ppid]);
+ ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(combo_box),
+ tmp, GINT_TO_POINTER(E_DECODE_PPID + 1 + number_of_ppid));
+ } else
+ break;
+ }
+ g_object_set_data(G_OBJECT(page), E_COMBO_BOX_SRCDST, combo_box);
+ return(combo_box);
+}
+
+/*************************************************/
+/* Dialog setup - list based menus */
+/*************************************************/
+
+struct handle_lookup_info {
+ dissector_handle_t handle;
+ gboolean found;
+};
+
+static gboolean
+lookup_handle(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
+ gpointer user_data)
+{
+ dissector_handle_t handle;
+ struct handle_lookup_info *hli = user_data;
+
+ gtk_tree_model_get(model, iter, E_LIST_S_TABLE+1, &handle, -1);
+ if (hli->handle == handle) {
+ hli->found = TRUE;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * This routine creates one entry in the list of protocol dissector
+ * that can be used. It is called by the dissector_table_foreach_handle
+ * routine once for each entry in a dissector table's list of handles
+ * for dissectors that could be used in that table. It guarantees unique
+ * entries by iterating over the list of entries build up to this point,
+ * looking for a duplicate name. If there is no duplicate, then this
+ * entry is added to the list of possible dissectors.
+ *
+ * @param table_name The name of the dissector table currently
+ * being walked.
+ *
+ * @param proto_name The protocol name
+ *
+ * @param value The dissector handle for this entry. This is an opaque
+ * pointer that can only be handed back to routines in the file packet.c
+ *
+ * @param user_data A data block passed into each instance of this
+ * routine. It contains information from the caller of the foreach
+ * routine, specifying information about the dissector table and where
+ * to store any information generated by this routine.
+ */
+void
+decode_add_to_list (const gchar *table_name, const gchar *proto_name, gpointer value, gpointer user_data)
+{
+ const gchar *text[E_LIST_S_COLUMNS];
+ GtkTreeView *list;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ struct handle_lookup_info hli;
+
+ g_assert(user_data);
+ g_assert(value);
+
+ list = user_data;
+
+ hli.handle = value;
+ hli.found = FALSE;
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(list));
+ gtk_tree_model_foreach(GTK_TREE_MODEL(store), lookup_handle, &hli);
+ /* We already have an entry for this handle.
+ * XXX - will this ever happen? */
+ if (hli.found) return;
+
+ text[E_LIST_S_PROTO_NAME] = proto_name;
+ text[E_LIST_S_TABLE] = table_name;
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ E_LIST_S_PROTO_NAME, text[E_LIST_S_PROTO_NAME],
+ E_LIST_S_TABLE, text[E_LIST_S_TABLE],
+ E_LIST_S_TABLE+1, value, -1);
+}
+
+static void
+decode_proto_add_to_list (const gchar *table_name, gpointer value, gpointer user_data)
+{
+ const gchar *proto_name;
+ gint i;
+ dissector_handle_t handle;
+
+
+ handle = value;
+ proto_name = dissector_handle_get_short_name(handle);
+
+ i = dissector_handle_get_protocol_index(handle);
+ if (i >= 0 && !proto_is_protocol_enabled(find_protocol_by_id(i)))
+ return;
+
+ decode_add_to_list (table_name, proto_name, value, user_data);
+}
+
+
+static gboolean
+decode_list_button_press_cb(GtkWidget *list, GdkEventButton *event, gpointer user_data _U_)
+{
+ if (event->type == GDK_2BUTTON_PRESS) {
+ GtkWidget *main_w = gtk_widget_get_toplevel(list);
+
+ decode_ok_cb (NULL, main_w);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+decode_list_key_release_cb(GtkWidget *list, GdkEventKey *event, gpointer user_data _U_)
+{
+ if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter) {
+ GtkWidget *main_w = gtk_widget_get_toplevel(list);
+
+ decode_ok_cb (NULL, main_w);
+ }
+
+ return FALSE;
+}
+
+/*
+ * This routine starts the creation of a List on a notebook page. It
+ * creates both a scrolled window and a list, adds the list to the
+ * window, and attaches the list as a data object on the page.
+ *
+ * @param page A pointer to the notebook page being created.
+ *
+ * @param list_p Will be filled in with the address of a newly
+ * created List.
+ *
+ * @param scrolled_win_p Will be filled in with the address of a newly
+ * created GtkScrolledWindow.
+ */
+void
+decode_list_menu_start(GtkWidget *page, GtkWidget **list_p,
+ GtkWidget **scrolled_win_p)
+{
+ GtkTreeView *list;
+ GtkListStore *store;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *tc;
+ GtkTreeSortable *sortable;
+
+ store = gtk_list_store_new(E_LIST_S_COLUMNS+1, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_POINTER);
+ g_object_set_data(G_OBJECT(decode_w), "sctp_data", store);
+ list = GTK_TREE_VIEW(tree_view_new(GTK_TREE_MODEL(store)));
+ g_object_unref(G_OBJECT(store));
+ sortable = GTK_TREE_SORTABLE(store);
+ gtk_tree_sortable_set_sort_func(sortable, SORT_ALPHABETICAL, sort_iter_compare_func, GINT_TO_POINTER(SORT_ALPHABETICAL), NULL);
+ gtk_tree_sortable_set_sort_column_id(sortable, SORT_ALPHABETICAL, GTK_SORT_ASCENDING);
+ gtk_tree_view_set_headers_clickable(list, FALSE);
+#ifndef DEBUG
+ gtk_tree_view_set_headers_visible(list, FALSE);
+#endif
+ renderer = gtk_cell_renderer_text_new();
+ tc = gtk_tree_view_column_new_with_attributes("Short Name", renderer,
+ "text", E_LIST_S_PROTO_NAME,
+ NULL);
+ gtk_tree_view_column_set_sizing(tc, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(list, tc);
+ g_object_set_data(G_OBJECT(page), E_PAGE_LIST, list);
+
+ *scrolled_win_p = scrolled_window_new(NULL, NULL);
+ /* this will result to set the width of the dialog to the required size */
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(*scrolled_win_p),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(*scrolled_win_p),
+ GTK_SHADOW_IN);
+ gtk_container_add(GTK_CONTAINER(*scrolled_win_p), GTK_WIDGET(list));
+
+ *list_p = GTK_WIDGET(list);
+}
+
+/*
+ * This routine finishes the creation of a List on a notebook page.
+ * It adds the default entry, sets the default entry as the
+ * highlighted entry, and sorts the List.
+ *
+ * @param list A pointer the the List to finish.
+ */
+void
+decode_list_menu_finish(GtkWidget *list)
+{
+ const gchar *text[E_LIST_S_COLUMNS];
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ text[E_LIST_S_PROTO_NAME] = "(default)";
+ text[E_LIST_S_TABLE] = "(none)";
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
+ gtk_list_store_prepend(store, &iter);
+ gtk_list_store_set(store, &iter,
+ E_LIST_S_PROTO_NAME, text[E_LIST_S_PROTO_NAME],
+ E_LIST_S_TABLE, text[E_LIST_S_TABLE],
+ E_LIST_S_TABLE+1, NULL, -1);
+
+ gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), &iter);
+ g_signal_connect(list, "button_press_event", G_CALLBACK(decode_list_button_press_cb), NULL);
+ g_signal_connect(list, "key_release_event", G_CALLBACK(decode_list_key_release_cb), NULL);
+}
+
+/*
+ * This routine is called to add the dissector selection list to a
+ * notebook page. This scrolled list contains an entry labeled
+ * "default", and an entry for each protocol that has had a dissector
+ * registered. The default choice for the list is set to the
+ * "default" choice, which will return the protocol/port selections to
+ * their original dissector(s).
+ *
+ * @param page A pointer to the notebook page currently being created.
+ *
+ * @param table_name The name of the dissector table to use to build
+ * this (list) menu.
+ *
+ * @return GtkWidget * A pointer to the newly created list within a
+ * scrolled window.
+ */
+static GtkWidget *
+decode_add_simple_menu (GtkWidget *page, const gchar *table_name)
+{
+ GtkWidget *scrolled_window;
+ GtkWidget *list;
+
+ decode_list_menu_start(page, &list, &scrolled_window);
+ dissector_table_foreach_handle(table_name, decode_proto_add_to_list, list);
+ decode_list_menu_finish(list);
+ return(scrolled_window);
+}
+
+
+/**************************************************/
+/* Dialog setup */
+/**************************************************/
+
+/*
+ * This routine creates a sample notebook page in the dialog box.
+ * This notebook page provides a prompt specifying what is being
+ * changed and its current value (e.g. "IP Protocol number (17)"), and
+ * a list specifying all the available choices. The list of choices
+ * is conditionally enabled, based upon the setting of the
+ * "decode"/"do not decode" radio buttons.
+ *
+ * @param prompt The prompt for this notebook page
+ *
+ * @param title A title for this page to use when debugging.
+ *
+ * @param table_name The name of the dissector table to use to
+ * build this page.
+ *
+ * @param value The protocol/port value that is to be changed.
+ *
+ * @return GtkWidget * A pointer to the notebook page created by this
+ * routine.
+ */
+static GtkWidget *
+decode_add_simple_page (const gchar *prompt, const gchar *title, const gchar *table_name,
+ guint value)
+{
+ GtkWidget *page, *label, *scrolled_window;
+
+ page = gtk_hbox_new(FALSE, 5);
+ g_object_set_data(G_OBJECT(page), E_PAGE_ACTION, decode_simple);
+ g_object_set_data(G_OBJECT(page), E_PAGE_TABLE, (gchar *) table_name);
+ g_object_set_data(G_OBJECT(page), E_PAGE_TITLE, (gchar *) title);
+ g_object_set_data(G_OBJECT(page), E_PAGE_VALUE, GUINT_TO_POINTER(value));
+
+ /* Always enabled */
+ label = gtk_label_new(prompt);
+ gtk_box_pack_start(GTK_BOX(page), label, TRUE, TRUE, 0);
+
+ /* Conditionally enabled - only when decoding packets */
+ label = gtk_label_new("as");
+ gtk_box_pack_start(GTK_BOX(page), label, TRUE, TRUE, 0);
+ decode_dimmable = g_slist_prepend(decode_dimmable, label);
+ scrolled_window = decode_add_simple_menu(page, table_name);
+ gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
+ decode_dimmable = g_slist_prepend(decode_dimmable, scrolled_window);
+
+ return(page);
+}
+
+
+/*
+ * This routine creates the TCP or UDP notebook page in the dialog box.
+ * All items created by this routine are packed into a single
+ * horizontal box. First is a label indicating whether the port(s) for
+ * which the user can set the dissection is a TCP port or a UDP port.
+ * Second is a combo_box allowing the user to select whether the source port,
+ * destination port, or both ports will have dissectors added for them.
+ * Last is a (conditionally enabled) popup menu listing all possible
+ * dissectors that can be used to decode the packets, and the choice
+ * or returning to the default dissector for these ports.
+ *
+ * The defaults for these items are the transport layer protocol of
+ * the currently selected packet, the source port of the currently
+ * selected packet, and the "default dissector".
+ *
+ * @param prompt The prompt for this notebook page
+ *
+ * @param table_name The name of the dissector table to use to
+ * build this page.
+ *
+ * @return GtkWidget * A pointer to the notebook page created by
+ * this routine.
+ */
+static GtkWidget *
+decode_add_tcpudp_page (const gchar *prompt, const gchar *table_name)
+{
+ GtkWidget *page, *label, *scrolled_window, *combo_box;
+
+ page = gtk_hbox_new(FALSE, 5);
+ g_object_set_data(G_OBJECT(page), E_PAGE_ACTION, decode_transport);
+ g_object_set_data(G_OBJECT(page), E_PAGE_TABLE, (gchar *) table_name);
+ g_object_set_data(G_OBJECT(page), E_PAGE_TITLE, "Transport");
+
+ /* Always enabled */
+ label = gtk_label_new(prompt);
+ gtk_box_pack_start(GTK_BOX(page), label, TRUE, TRUE, 0);
+ combo_box = decode_add_srcdst_combo_box(page);
+ gtk_box_pack_start(GTK_BOX(page), combo_box, TRUE, TRUE, 0);
+ label = gtk_label_new("port(s)");
+ gtk_box_pack_start(GTK_BOX(page), label, TRUE, TRUE, 0);
+
+ /* Conditionally enabled - only when decoding packets */
+ label = gtk_label_new("as");
+ gtk_box_pack_start(GTK_BOX(page), label, TRUE, TRUE, 0);
+ decode_dimmable = g_slist_prepend(decode_dimmable, label);
+ scrolled_window = decode_add_simple_menu(page, table_name);
+ gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
+ decode_dimmable = g_slist_prepend(decode_dimmable, scrolled_window);
+
+ return(page);
+}
+
+static void
+decode_sctp_list_menu_start(GtkWidget **list_p, GtkWidget **scrolled_win_p)
+{
+ GtkTreeView *list;
+ GtkListStore *sctp_store;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *tc;
+ GtkTreeSortable *sortable;
+
+ sctp_store = g_object_get_data(G_OBJECT(decode_w), "sctp_data");
+ list = GTK_TREE_VIEW(tree_view_new(GTK_TREE_MODEL(sctp_store)));
+ g_object_unref(G_OBJECT(sctp_store));
+ sortable = GTK_TREE_SORTABLE(sctp_store);
+ gtk_tree_sortable_set_sort_func(sortable, SORT_ALPHABETICAL, sort_iter_compare_func, GINT_TO_POINTER(SORT_ALPHABETICAL), NULL);
+ gtk_tree_sortable_set_sort_column_id(sortable, SORT_ALPHABETICAL, GTK_SORT_ASCENDING);
+ gtk_tree_view_set_headers_clickable(list, FALSE);
+#ifndef DEBUG
+ gtk_tree_view_set_headers_visible(list, FALSE);
+#endif
+ renderer = gtk_cell_renderer_text_new();
+ tc = gtk_tree_view_column_new_with_attributes("Short Name", renderer,
+ "text", E_LIST_S_PROTO_NAME,
+ NULL);
+ gtk_tree_view_column_set_sizing(tc, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(list, tc);
+
+ *scrolled_win_p = scrolled_window_new(NULL, NULL);
+ /* this will result to set the width of the dialog to the required size */
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(*scrolled_win_p), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(*scrolled_win_p), GTK_SHADOW_IN);
+ gtk_container_add(GTK_CONTAINER(*scrolled_win_p), GTK_WIDGET(list));
+ *list_p = GTK_WIDGET(list);
+}
+
+static void
+decode_sctp_update_ppid_combo_box(GtkWidget *w _U_, GtkWidget *page)
+{
+ GtkWidget *list, *scrolled_window;
+ GtkWidget *sctp_combo_box;
+ gchar tmp[100];
+ guint number_of_ppid;
+ GtkListStore *sctp_store;
+
+ sctp_combo_box = g_object_get_data(G_OBJECT(page), E_COMBO_BOX_SRCDST);
+ ws_combo_box_clear_text_and_pointer(GTK_COMBO_BOX(sctp_combo_box));
+
+ g_snprintf(tmp, sizeof(tmp), "PPID (%u)", 0);
+ ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(sctp_combo_box), tmp, GINT_TO_POINTER(E_DECODE_PPID));
+ ws_combo_box_set_active(GTK_COMBO_BOX(sctp_combo_box), 0); /* default */
+
+ for(number_of_ppid = 0; number_of_ppid < MAX_NUMBER_OF_PPIDS; number_of_ppid++) {
+ if (cfile.edt->pi.ppids[number_of_ppid] != 0) {
+ g_snprintf(tmp, sizeof(tmp), "PPID (%u)", cfile.edt->pi.ppids[number_of_ppid]);
+ ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(sctp_combo_box),
+ tmp, GINT_TO_POINTER(E_DECODE_PPID + 1 + number_of_ppid));
+ }
+ }
+
+ g_object_set_data(G_OBJECT(page), E_PAGE_TABLE, "sctp.ppi");
+
+ sctp_store = g_object_get_data(G_OBJECT(G_OBJECT(decode_w)), "sctp_data");
+ gtk_list_store_clear(sctp_store);
+ decode_sctp_list_menu_start(&list, &scrolled_window);
+ dissector_table_foreach_handle("sctp.ppi", decode_proto_add_to_list, list);
+ decode_list_menu_finish(list);
+}
+
+
+static void
+decode_sctp_update_srcdst_combo_box(GtkWidget *w _U_, GtkWidget *page)
+{
+ GtkWidget *scrolled_window, *list;
+ GtkWidget *sctp_combo_box;
+ gchar tmp[100];
+ GtkListStore *sctp_store;
+
+ sctp_combo_box = g_object_get_data(G_OBJECT(page), E_COMBO_BOX_SRCDST);
+ ws_combo_box_clear_text_and_pointer(GTK_COMBO_BOX(sctp_combo_box));
+
+ g_snprintf(tmp, sizeof(tmp), "source (%u)", cfile.edt->pi.srcport);
+ ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(sctp_combo_box), tmp, GINT_TO_POINTER(E_DECODE_SPORT));
+ g_snprintf(tmp, sizeof(tmp), "destination (%u)", cfile.edt->pi.destport);
+ ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(sctp_combo_box), tmp, GINT_TO_POINTER(E_DECODE_DPORT));
+ ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(sctp_combo_box), "both", GINT_TO_POINTER(E_DECODE_BPORT));
+ ws_combo_box_set_active(GTK_COMBO_BOX(sctp_combo_box), 0);
+
+ g_object_set_data(G_OBJECT(page), E_PAGE_TABLE, "sctp.port");
+ g_object_set_data(G_OBJECT(page), E_PAGE_SPORT, GINT_TO_POINTER(cfile.edt->pi.srcport));
+ g_object_set_data(G_OBJECT(page), E_PAGE_DPORT, GINT_TO_POINTER(cfile.edt->pi.destport));
+ sctp_store = g_object_get_data(G_OBJECT(G_OBJECT(decode_w)), "sctp_data");
+ gtk_list_store_clear(sctp_store);
+ decode_sctp_list_menu_start(&list, &scrolled_window);
+ dissector_table_foreach_handle("sctp.port", decode_proto_add_to_list, list);
+ decode_list_menu_finish(list);
+}
+
+
+
+static GtkWidget *
+decode_sctp_add_port_ppid (GtkWidget *page)
+{
+ GtkWidget *format_vb, *radio_button;
+ GSList *format_grp;
+
+ format_vb = gtk_vbox_new(FALSE, 2);
+
+ radio_button = gtk_radio_button_new_with_label(NULL, "PPID");
+ format_grp = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio_button));
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_button), TRUE);
+ g_signal_connect(radio_button, "clicked", G_CALLBACK(decode_sctp_update_ppid_combo_box), page);
+
+ gtk_box_pack_start(GTK_BOX(format_vb), radio_button, TRUE, TRUE, 0);
+
+ radio_button = gtk_radio_button_new_with_label(format_grp, "Port");
+ g_signal_connect(radio_button, "clicked", G_CALLBACK(decode_sctp_update_srcdst_combo_box), page);
+
+ gtk_box_pack_start(GTK_BOX(format_vb), radio_button, TRUE, TRUE, 0);
+
+ return(format_vb);
+}
+
+
+static GtkWidget *
+decode_add_sctp_page (const gchar *prompt, const gchar *table_name)
+{
+ GtkWidget *page, *label, *scrolled_window, *radio, *vbox, *alignment, *sctpbox, *sctp_combo_box;
+
+ page = gtk_hbox_new(FALSE, 5);
+ g_object_set_data(G_OBJECT(page), E_PAGE_ACTION, decode_transport);
+ g_object_set_data(G_OBJECT(page), E_PAGE_TABLE, (gchar *) table_name);
+ g_object_set_data(G_OBJECT(page), E_PAGE_TITLE, "Transport");
+
+ vbox = gtk_vbox_new(FALSE, 5);
+ radio = decode_sctp_add_port_ppid(page);
+ gtk_box_pack_start(GTK_BOX(vbox), radio, TRUE, TRUE, 0);
+
+ /* Always enabled */
+ sctpbox = gtk_hbox_new(FALSE, 5);
+ label = gtk_label_new(prompt);
+ gtk_box_pack_start(GTK_BOX(sctpbox), label, TRUE, TRUE, 0);
+ sctp_combo_box = decode_add_ppid_combo_box(page);
+ alignment = decode_add_pack_combo_box(sctp_combo_box);
+
+ gtk_box_pack_start(GTK_BOX(sctpbox), alignment, TRUE, TRUE, 0);
+
+ /* Conditionally enabled - only when decoding packets */
+ label = gtk_label_new("as");
+ gtk_box_pack_start(GTK_BOX(sctpbox), label, TRUE, TRUE, 0);
+ decode_dimmable = g_slist_prepend(decode_dimmable, label);
+ gtk_box_pack_start(GTK_BOX(vbox), sctpbox, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(page), vbox, TRUE, TRUE, 0);
+
+ scrolled_window = decode_add_simple_menu(page, table_name);
+ gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
+ decode_dimmable = g_slist_prepend(decode_dimmable, scrolled_window);
+
+ return(page);
+}
+
+
+/*
+ * This routine indicates whether we'd actually have any pages in the
+ * notebook in a "Decode As" dialog box; if there wouldn't be, we
+ * inactivate the menu item for "Decode As".
+ */
+gboolean
+decode_as_ok(void)
+{
+ return (cfile.edt->pi.ethertype != G_MAXINT) || cfile.edt->pi.ipproto ||
+ cfile.edt->pi.ptype == PT_TCP || cfile.edt->pi.ptype == PT_UDP ||
+ cfile.edt->pi.mpls_label ||
+ cfile.cd_t == WTAP_FILE_BER;
+}
+
+
+/*
+ * This routine creates the bulk of the "Decode As" dialog box. All
+ * items created by this routine are packed as pages into a notebook.
+ * There will be a page for each protocol layer that can be changed.
+ *
+ * @param format_hb A pointer to the widget in which the notebook
+ * should be installed.
+ */
+static void
+decode_add_notebook (GtkWidget *format_hb)
+{
+ GtkWidget *notebook, *page, *label;
+ gchar buffer[40];
+
+ /* Start a nootbook for flipping between sets of changes */
+ notebook = gtk_notebook_new();
+ gtk_container_add(GTK_CONTAINER(format_hb), notebook);
+ g_object_set_data(G_OBJECT(decode_w), E_NOTEBOOK, notebook);
+
+ /* Add link level selection page */
+ if (cfile.edt->pi.ethertype != G_MAXINT) {
+ g_snprintf(buffer, sizeof(buffer), "Ethertype 0x%04x", cfile.edt->pi.ethertype);
+ page = decode_add_simple_page(buffer, "Link", "ethertype", cfile.edt->pi.ethertype);
+ label = gtk_label_new("Link");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
+ }
+
+ /* Add mpls selection page */
+ if (cfile.edt->pi.mpls_label) {
+ g_snprintf(buffer, sizeof(buffer), "Data after label %u", cfile.edt->pi.mpls_label);
+ page = decode_add_simple_page(buffer, "MPLS", "mpls.label", cfile.edt->pi.mpls_label);
+ label = gtk_label_new("MPLS");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
+ }
+
+ /* Add network selection page */
+ if (cfile.edt->pi.ipproto) {
+ /*
+ * The network-layer protocol is IP.
+ */
+ g_snprintf(buffer, sizeof(buffer), "IP protocol %u", cfile.edt->pi.ipproto);
+ page = decode_add_simple_page(buffer, "Network", "ip.proto", cfile.edt->pi.ipproto);
+ g_object_set_data(G_OBJECT(page), E_PAGE_ACTION, decode_simple);
+ label = gtk_label_new("Network");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
+ }
+
+ /* Add transport selection page */
+ switch (cfile.edt->pi.ptype) {
+
+ case PT_TCP:
+ page = decode_add_tcpudp_page("TCP", "tcp.port");
+ break;
+
+ case PT_UDP:
+ page = decode_add_tcpudp_page("UDP", "udp.port");
+ break;
+
+ case PT_SCTP:
+ page = decode_add_sctp_page("SCTP", "sctp.ppi");
+ break;
+
+ default:
+ page = NULL;
+ break;
+ }
+ if (page != NULL) {
+ label = gtk_label_new("Transport");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
+ }
+
+ if(cfile.edt->pi.dcetransporttype != -1) {
+ page = decode_dcerpc_add_page(&cfile.edt->pi);
+ label = gtk_label_new("DCE-RPC");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
+ g_object_set_data(G_OBJECT(decode_w), E_PAGE_DCERPC, page);
+ }
+
+ if(cfile.cd_t == WTAP_FILE_BER) {
+ page = decode_ber_add_page(&cfile.edt->pi);
+ label = gtk_label_new("ASN.1");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
+ g_object_set_data(G_OBJECT(decode_w), E_PAGE_ASN1, page);
+ }
+
+ /* Select the last added page (selects first by default) */
+ /* Notebook must be visible for set_page to work. */
+ gtk_widget_show_all(notebook);
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), -1);
+
+}
+
+
+/*
+ * This routine creates the "Decode As" dialog box. This dialog box
+ * asks the user which protocol to use for decoding the currently
+ * selected packet. This will affect the last packet that we called a
+ * dissection routine on belongs (this might be the most recently
+ * selected packet, or it might be the last packet in the file).
+ *
+ * This routine uses an auxiliary function to create the bulk of the
+ * dialog box, and then hand crafts the button box at the bottom of
+ * the dialog.
+ *
+ * @param w Unused
+ *
+ * @param user_data Unused
+ */
+void
+decode_as_cb (GtkWidget * w _U_, gpointer user_data _U_)
+{
+ GtkWidget *main_vb, *format_hb, *bbox, *ok_bt, *close_bt, *help_bt, *button;
+ GtkWidget *button_vb, *apply_bt;
+
+ if (decode_w != NULL) {
+ /* There's already a "Decode As" dialog box; reactivate it. */
+ reactivate_window(decode_w);
+ return;
+ }
+
+ requested_action = E_DECODE_YES;
+ decode_w = dlg_window_new("Wireshark: Decode As");
+ /* Provide a minimum of a couple of rows worth of data */
+ gtk_window_set_default_size(GTK_WINDOW(decode_w), -1, E_DECODE_MIN_HEIGHT);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(decode_w), main_vb);
+
+ /* First row - Buttons and Notebook */
+ format_hb = gtk_hbox_new(FALSE, 5);
+ gtk_box_pack_start(GTK_BOX(main_vb), format_hb, TRUE, TRUE, 10);
+
+ button_vb = decode_add_yes_no();
+ gtk_box_pack_start(GTK_BOX(format_hb), button_vb, TRUE, TRUE, 10);
+
+ button = gtk_button_new_with_label("Show Current");
+ g_signal_connect(button, "clicked", G_CALLBACK(decode_show_cb), NULL);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(button, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+#endif
+ gtk_box_pack_start(GTK_BOX(button_vb), button, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text(button, "Open a dialog showing the current settings.\n"
+ "Note you need to select and press apply first to be able to save the current setting");
+
+ button = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
+ g_signal_connect(button, "clicked", G_CALLBACK(decode_clear_cb), NULL);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(button, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+#endif
+ gtk_box_pack_start(GTK_BOX(button_vb), button, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text(button, "Clear ALL settings.");
+
+ decode_add_notebook(format_hb);
+
+ /* Button row */
+ bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+
+ ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(decode_ok_cb), decode_w);
+ gtk_widget_set_tooltip_text(ok_bt, "Apply current setting, close dialog and redissect packets.");
+
+ apply_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_APPLY);
+ g_signal_connect(apply_bt, "clicked", G_CALLBACK(decode_apply_cb), decode_w);
+ gtk_widget_set_tooltip_text(apply_bt, "Apply current setting, redissect packets and keep dialog open.");
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(decode_w, close_bt, NULL);
+ g_signal_connect(close_bt, "clicked", G_CALLBACK(decode_close_cb), decode_w);
+ gtk_widget_set_tooltip_text(close_bt, "Close the dialog, don't redissect packets.");
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_DECODE_AS_DIALOG);
+
+ gtk_widget_grab_default(ok_bt);
+
+ g_signal_connect(decode_w, "delete_event", G_CALLBACK(decode_delete_cb), NULL);
+ g_signal_connect(decode_w, "destroy", G_CALLBACK(decode_destroy_cb), NULL);
+
+ gtk_widget_show_all(decode_w);
+ window_present(decode_w);
+}
+
+void load_decode_as_entries(void)
+{
+ char *daf_path;
+ FILE *daf;
+
+ if (dissector_reset_list) {
+ decode_clear_all(FALSE);
+ }
+
+ daf_path = get_persconffile_path(DECODE_AS_ENTRIES_FILE_NAME, TRUE, FALSE);
+ if ((daf = ws_fopen(daf_path, "r")) != NULL) {
+ read_prefs_file(daf_path, daf, read_set_decode_as_entries, NULL);
+ fclose(daf);
+ }
+ g_free(daf_path);
+}
diff --git a/ui/gtk/decode_as_dlg.h b/ui/gtk/decode_as_dlg.h
new file mode 100644
index 0000000000..efe32d0f76
--- /dev/null
+++ b/ui/gtk/decode_as_dlg.h
@@ -0,0 +1,71 @@
+/* decode_as_dlg.h
+ *
+ * $Id$
+ *
+ * Routines to modify dissector tables on the fly.
+ *
+ * By David Hampton <dhampton@mac.com>
+ * Copyright 2001 David Hampton
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __DECODE_AS_DLG_H__
+#define __DECODE_AS_DLG_H__
+
+/** @file
+ * "Decode As" / "User Specified Decodes" dialog box.
+ * @ingroup dialog_group
+ */
+
+/*
+ * Filename of the "decode as" entry preferences
+ */
+#define DECODE_AS_ENTRIES_FILE_NAME "decode_as_entries"
+
+#define DECODE_AS_ENTRY "decode_as_entry"
+
+/** Init the "Decode As" module
+ */
+void decode_as_init(void);
+
+/** User requested the "Decode As" dialog box by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+void decode_as_cb(GtkWidget *widget, gpointer data);
+
+/** User requested the "User Specified Decodes" dialog box by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+void decode_show_cb(GtkWidget *widget, gpointer data);
+
+/** Have any pages in the notebook in a "Decode As" dialog box? If there
+ * wouldn't be, we inactivate the menu item for "Decode As".
+ *
+ * @return TRUE, if we have at least one notebook page in "Decode As"
+ */
+gboolean decode_as_ok(void);
+
+/*
+ * Reset the "decode as"entries and reload ones of the current profile
+ */
+void load_decode_as_entries(void);
+
+#endif
diff --git a/ui/gtk/dfilter_expr_dlg.c b/ui/gtk/dfilter_expr_dlg.c
new file mode 100644
index 0000000000..da690b5a01
--- /dev/null
+++ b/ui/gtk/dfilter_expr_dlg.c
@@ -0,0 +1,1206 @@
+/* dfilter_expr_dlg.c
+ *
+ * Allow the user to construct a subexpression of a display filter
+ * expression, testing a particular field; display the tree of fields
+ * and the relations and values with which it can be compared.
+ *
+ * Copyright 2000, Jeffrey C. Foster <jfoste@woodward.com> and
+ * Guy Harris <guy@alum.mit.edu>
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2000 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.
+ */
+
+/* Todo -
+ * may want to check the enable field to decide if protocol should be in tree
+ * improve speed of dialog box creation
+ * - I believe this is slow because of tree widget creation.
+ * 1) could improve the widget
+ * 2) keep a copy in memory after the first time.
+ * user can pop multiple tree dialogs by pressing the "Tree" button multiple
+ * times. not a good thing.
+ * Sort the protocols and children
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <ctype.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "../simple_dialog.h"
+
+#include "ui/gtk/main.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/proto_dlg.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/dfilter_expr_dlg.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+#define E_DFILTER_EXPR_TREE_KEY "dfilter_expr_tree"
+#define E_DFILTER_EXPR_CURRENT_VAR_KEY "dfilter_expr_current_var"
+#define E_DFILTER_EXPR_RELATION_LIST_KEY "dfilter_expr_relation_list"
+#define E_DFILTER_EXPR_RANGE_LABEL_KEY "dfilter_expr_range_label"
+#define E_DFILTER_EXPR_RANGE_ENTRY_KEY "dfilter_expr_range_entry"
+#define E_DFILTER_EXPR_VALUE_LABEL_KEY "dfilter_expr_value_label"
+#define E_DFILTER_EXPR_VALUE_ENTRY_KEY "dfilter_expr_value_entry"
+#define E_DFILTER_EXPR_VALUE_LIST_LABEL_KEY "dfilter_expr_value_list_label"
+#define E_DFILTER_EXPR_VALUE_LIST_KEY "dfilter_expr_value_list"
+#define E_DFILTER_EXPR_VALUE_LIST_SW_KEY "dfilter_expr_value_list_sw"
+#define E_DFILTER_EXPR_OK_BT_KEY "dfilter_expr_accept_bt"
+#define E_DFILTER_EXPR_VALUE_KEY "dfilter_expr_value"
+
+typedef struct protocol_data {
+ char *abbrev;
+ int hfinfo_index;
+} protocol_data_t;
+
+static void show_relations(GtkWidget *relation_list, ftenum_t ftype);
+static gboolean relation_is_presence_test(const char *string);
+static void add_relation_list(GtkWidget *relation_list, const char *relation, gboolean sensitive);
+static void build_boolean_values(GtkWidget *value_list_scrolled_win,
+ GtkWidget *value_list,
+ const true_false_string *values);
+static void build_enum_values(GtkWidget *value_list_scrolled_win,
+ GtkWidget *value_list,
+ const value_string *values);
+static void add_value_list_item(GtkWidget *value_list, const gchar *string,
+ const gpointer data);
+static void display_value_fields(header_field_info *hfinfo,
+ gboolean is_comparison, GtkWidget *value_label,
+ GtkWidget *value_entry,
+ GtkWidget *value_list_label, GtkWidget *value_list,
+ GtkWidget *value_list_scrolled_win,
+ GtkWidget *range_label,
+ GtkWidget *range_entry);
+
+/*
+ * Note that this is called every time the user clicks on an item,
+ * whether it is already selected or not.
+ */
+static void
+field_select_row_cb(GtkTreeSelection *sel, gpointer tree)
+{
+ GtkWidget *window = gtk_widget_get_toplevel(tree);
+ GtkWidget *relation_list = g_object_get_data(G_OBJECT(window),
+ E_DFILTER_EXPR_RELATION_LIST_KEY);
+ GtkWidget *range_label = g_object_get_data(G_OBJECT(window),
+ E_DFILTER_EXPR_RANGE_LABEL_KEY);
+ GtkWidget *range_entry = g_object_get_data(G_OBJECT(window),
+ E_DFILTER_EXPR_RANGE_ENTRY_KEY);
+ GtkWidget *value_label = g_object_get_data(G_OBJECT(window),
+ E_DFILTER_EXPR_VALUE_LABEL_KEY);
+ GtkWidget *value_entry = g_object_get_data(G_OBJECT(window),
+ E_DFILTER_EXPR_VALUE_ENTRY_KEY);
+ GtkWidget *value_list_label = g_object_get_data(G_OBJECT(window),
+ E_DFILTER_EXPR_VALUE_LIST_LABEL_KEY);
+ GtkWidget *value_list = g_object_get_data(G_OBJECT(window),
+ E_DFILTER_EXPR_VALUE_LIST_KEY);
+ GtkWidget *value_list_scrolled_win = g_object_get_data(G_OBJECT(window),
+ E_DFILTER_EXPR_VALUE_LIST_SW_KEY);
+ GtkWidget *ok_bt = g_object_get_data(G_OBJECT(window),
+ E_DFILTER_EXPR_OK_BT_KEY);
+ header_field_info *hfinfo, *cur_hfinfo;
+ const char *value_type;
+ char value_label_string[1024+1]; /* XXX - should be large enough */
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ if (!gtk_tree_selection_get_selected(sel, &model, &iter))
+ return;
+ gtk_tree_model_get(model, &iter, 1, &hfinfo, -1);
+
+ /*
+ * What was the item that was last selected?
+ */
+ cur_hfinfo = g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_CURRENT_VAR_KEY);
+ if (cur_hfinfo == hfinfo) {
+ /*
+ * It's still selected; no need to change anything.
+ */
+ return;
+ }
+
+ /*
+ * Mark it as currently selected.
+ */
+ g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_CURRENT_VAR_KEY, hfinfo);
+
+ show_relations(relation_list, hfinfo->type);
+
+ /*
+ * Set the label for the value to indicate what type of value
+ * it is.
+ */
+ value_type = ftype_pretty_name(hfinfo->type);
+ if (value_type != NULL) {
+ /*
+ * Indicate what type of value it is.
+ */
+ g_snprintf(value_label_string, sizeof value_label_string,
+ "Value (%s)", value_type);
+ gtk_label_set_text(GTK_LABEL(value_label), value_label_string);
+ }
+
+ /*
+ * Clear the entry widget for the value, as whatever
+ * was there before doesn't apply.
+ */
+ gtk_entry_set_text(GTK_ENTRY(value_entry), "");
+
+ switch (hfinfo->type) {
+
+ case FT_BOOLEAN:
+ /*
+ * The list of values should be the strings for "true"
+ * and "false"; show them in the value list.
+ */
+ build_boolean_values(value_list_scrolled_win, value_list,
+ hfinfo->strings);
+ break;
+
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ /*
+ * If this has a value_string table (not a range_string table) associated with it,
+ * fill up the list of values, otherwise clear the list of values.
+ */
+ /* XXX: ToDo: Implement "range-string" filter ? */
+ if ((hfinfo->strings != NULL) && !(hfinfo->display & BASE_RANGE_STRING)) {
+ const value_string *vals = hfinfo->strings;
+ if (hfinfo->display & BASE_EXT_STRING)
+ vals = VALUE_STRING_EXT_VS_P((value_string_ext *) vals);
+ build_enum_values(value_list_scrolled_win, value_list, vals);
+ } else
+ gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list))));
+ break;
+
+ default:
+ /*
+ * Clear the list of values.
+ */
+ gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list))));
+ break;
+ }
+
+ /*
+ * Display various items for the value, as appropriate.
+ * The relation we start out with is never a comparison.
+ */
+ display_value_fields(hfinfo, FALSE, value_label, value_entry,
+ value_list_label, value_list, value_list_scrolled_win, range_label, range_entry);
+
+ /*
+ * XXX - in browse mode, there always has to be something
+ * selected, so this should always be sensitive.
+ */
+ gtk_widget_set_sensitive(ok_bt, TRUE);
+}
+
+static void
+show_relations(GtkWidget *relation_list, ftenum_t ftype)
+{
+ GtkTreeIter iter;
+ /*
+ * Clear out the currently displayed list of relations.
+ */
+ gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(relation_list))));
+
+ /*
+ * Add the supported relations.
+ */
+ add_relation_list(relation_list, "is present", TRUE);
+ add_relation_list(relation_list, "==",
+ ftype_can_eq(ftype) || (ftype_can_slice(ftype) && ftype_can_eq(FT_BYTES)));
+ add_relation_list(relation_list, "!=",
+ ftype_can_ne(ftype) || (ftype_can_slice(ftype) && ftype_can_ne(FT_BYTES)));
+ add_relation_list(relation_list, ">",
+ ftype_can_gt(ftype) || (ftype_can_slice(ftype) && ftype_can_gt(FT_BYTES)));
+
+ add_relation_list(relation_list, "<",
+ ftype_can_lt(ftype) || (ftype_can_slice(ftype) && ftype_can_lt(FT_BYTES)));
+ add_relation_list(relation_list, ">=",
+ ftype_can_ge(ftype) || (ftype_can_slice(ftype) && ftype_can_ge(FT_BYTES)));
+ add_relation_list(relation_list, "<=",
+ ftype_can_le(ftype) || (ftype_can_slice(ftype) && ftype_can_le(FT_BYTES)));
+ add_relation_list(relation_list, "contains",
+ ftype_can_contains(ftype) || (ftype_can_slice(ftype) && ftype_can_contains(FT_BYTES)));
+ add_relation_list(relation_list, "matches",
+ ftype_can_matches(ftype) || (ftype_can_slice(ftype) && ftype_can_matches(FT_BYTES)));
+
+ gtk_tree_model_get_iter_first(gtk_tree_view_get_model(GTK_TREE_VIEW(relation_list)), &iter);
+ gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list)), &iter);
+}
+
+/*
+ * Given a string that represents a test to be made on a field, returns
+ * TRUE if it tests for the field's presence, FALSE otherwise.
+ */
+static gboolean
+relation_is_presence_test(const char *string)
+{
+ return (strcmp(string, "is present") == 0);
+}
+
+static void
+add_relation_list(GtkWidget *relation_list, const char *relation, gboolean sensitive)
+{
+ GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(relation_list)));
+ GtkTreeIter iter;
+
+ /* XXX: I currently see no way to insensitive the item,
+ * so for a first step, just don't show it (as before these changes :-) */
+ if (!sensitive) {
+ return;
+ }
+
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, 0, relation, -1);
+}
+
+static void
+relation_list_sel_cb(GtkTreeSelection *sel, gpointer user_data _U_)
+{
+ GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(gtk_tree_selection_get_tree_view(sel)));
+ GtkWidget *range_label =
+ g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_RANGE_LABEL_KEY);
+ GtkWidget *range_entry =
+ g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_RANGE_ENTRY_KEY);
+ GtkWidget *value_label =
+ g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LABEL_KEY);
+ GtkWidget *value_entry =
+ g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_ENTRY_KEY);
+ GtkWidget *value_list_label =
+ g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_LABEL_KEY);
+ GtkWidget *value_list =
+ g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_KEY);
+ GtkWidget *value_list_scrolled_win =
+ g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_SW_KEY);
+ header_field_info *hfinfo =
+ g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_CURRENT_VAR_KEY);
+ gchar *item_str;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ /*
+ * What's the relation?
+ */
+ if (!gtk_tree_selection_get_selected(sel, &model, &iter))
+ return;
+ gtk_tree_model_get(model, &iter, 0, &item_str, -1);
+
+ /*
+ * Update the display of various items for the value, as appropriate.
+ */
+ display_value_fields(hfinfo,
+ !relation_is_presence_test(item_str),
+ value_label, value_entry, value_list_label, value_list,
+ value_list_scrolled_win, range_label, range_entry);
+ g_free(item_str);
+}
+
+static void
+build_boolean_values(GtkWidget *value_list_scrolled_win, GtkWidget *value_list,
+ const true_false_string *values)
+{
+ static const true_false_string true_false = { "True", "False" };
+ GtkTreeSelection *sel;
+ GtkTreeIter iter;
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(value_list));
+
+ /*
+ * Clear out the items for the list, and put in the names
+ * from the value_string list.
+ */
+ gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list))));
+
+ /*
+ * Put the list in single mode, so we don't get any selection
+ * events while we're building it (i.e., so we don't get any
+ * on a list item BEFORE WE GET TO SET THE DATA FOR THE LIST
+ * ITEM SO THAT THE HANDLER CAN HANDLE IT).
+ */
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+
+ /*
+ * Build the list.
+ */
+ if (values == NULL)
+ values = &true_false;
+ add_value_list_item(value_list, values->true_string, (gpointer) values);
+ add_value_list_item(value_list, values->false_string, NULL);
+
+ /*
+ * OK, we're done, so we can finally put it in browse mode.
+ * Select the first item, so that the user doesn't have to, under
+ * the assumption that they're most likely to test if something
+ * is true, not false.
+ */
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE);
+ gtk_tree_model_get_iter_first(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list)), &iter);
+ gtk_tree_selection_select_iter(sel, &iter);
+
+ gtk_widget_show_all(value_list_scrolled_win);
+}
+
+static void
+build_enum_values(GtkWidget *value_list_scrolled_win _U_, GtkWidget *value_list,
+ const value_string *values)
+{
+ GtkTreeSelection *sel;
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(value_list));
+ /*
+ * Clear out the items for the list, and put in the names
+ * from the value_string list.
+ */
+ gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list))));
+
+ /*
+ * Put the list in single mode, so we don't get any selection
+ * events while we're building it (i.e., so we don't get any
+ * on a list item BEFORE WE GET TO SET THE DATA FOR THE LIST
+ * ITEM SO THAT THE HANDLER CAN HANDLE IT).
+ */
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+
+ /*
+ * Build the list.
+ */
+ while (values->strptr != NULL) {
+ add_value_list_item(value_list, values->strptr, (gpointer) values);
+ values++;
+ }
+
+ /*
+ * OK, we're done, so we can finally put it in browse mode.
+ */
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE);
+}
+
+static void
+add_value_list_item(GtkWidget *value_list, const gchar *string, const gpointer data)
+{
+ GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(value_list)));
+ GtkTreeIter iter;
+
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, 0, string, 1, data, -1);
+}
+
+/*
+ * Show or hide the various values fields as appropriate for the field
+ * and currently-selected relation.
+ */
+static void
+display_value_fields(header_field_info *hfinfo, gboolean is_comparison,
+ GtkWidget *value_label, GtkWidget *value_entry,
+ GtkWidget *value_list_label,
+ GtkWidget *value_list _U_,
+ GtkWidget *value_list_scrolled_win, GtkWidget *range_label,
+ GtkWidget *range_entry)
+{
+ /* Default values */
+ gboolean show_value_label = FALSE;
+ gboolean show_value_list = FALSE;
+ gboolean show_range = FALSE;
+
+ /*
+ * Either:
+ *
+ * this is an FT_NONE variable, in which case you can
+ * only check whether it's present or absent in the
+ * protocol tree
+ *
+ * or
+ *
+ * this is a Boolean variable, in which case you
+ * can't specify a value to compare with, you can
+ * only specify whether to test for the Boolean
+ * being true or to test for it being false
+ *
+ * or
+ *
+ * this isn't a Boolean variable, in which case you
+ * can test for its presence in the protocol tree,
+ * and the relation is such a test, in
+ * which case you don't compare with a value
+ *
+ * so we hide the value entry.
+ */
+
+ switch (hfinfo->type) {
+
+ case FT_BOOLEAN:
+ if (is_comparison) {
+ show_value_label = TRUE; /* XXX: Allow value entry (contrary to the comment above) ?? */
+ show_value_list = TRUE;
+ }
+ break;
+
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ if (is_comparison) {
+ show_value_label = TRUE;
+ if ((hfinfo->strings != NULL) && !(hfinfo->display & BASE_RANGE_STRING)) {
+ /*
+ * We have a list of values to show.
+ */
+ show_value_list = TRUE;
+ }
+ }
+ break;
+
+ default:
+ /*
+ * There is no list of names for values; only show the value_label if needed.
+ */
+ if (is_comparison)
+ show_value_label = TRUE;
+ break;
+ }
+
+ gtk_widget_set_sensitive(value_label, show_value_label);
+ gtk_widget_set_sensitive(value_entry, show_value_label);
+
+ gtk_widget_set_sensitive(value_list_label, show_value_list);
+ gtk_widget_set_sensitive(value_list_scrolled_win, show_value_list);
+
+ /*
+ * Is this a comparison, and are ranges supported by this type?
+ * If both are true, show the range stuff, otherwise hide it.
+ */
+ show_range = (is_comparison && ftype_can_slice(hfinfo->type));
+ gtk_widget_set_sensitive(range_label, show_range);
+ gtk_widget_set_sensitive(range_entry, show_range);
+}
+
+static void
+value_list_sel_cb(GtkTreeSelection *sel, gpointer value_entry_arg)
+{
+ GtkWidget *value_entry = value_entry_arg;
+ GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(gtk_tree_selection_get_tree_view(sel)));
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ header_field_info *hfinfo = g_object_get_data(G_OBJECT(window),
+ E_DFILTER_EXPR_CURRENT_VAR_KEY);
+ const value_string *value = NULL;
+ gchar *value_display_string = NULL;
+
+ if (!gtk_tree_selection_get_selected(sel, &model, &iter))
+ return;
+ gtk_tree_model_get(model, &iter, 1, &value, -1);
+
+ /*
+ * This should either be a numeric type or a Boolean type.
+ */
+ if (hfinfo->type == FT_BOOLEAN) {
+ /*
+ * Boolean type; if the value key for the selected item
+ * is non-null, it's the item for "true", otherwise it's
+ * the item for "false". Compare with 1 if we're
+ * testing for "true", and compare with 0 if we're
+ * testing for "false".
+ */
+ if (value != NULL)
+ value_display_string = g_strdup("1");
+ else
+ value_display_string = g_strdup("0");
+ } else {
+ /*
+ * Numeric type; get the value corresponding to the
+ * selected item, and display it in the base for this
+ * field.
+ */
+ switch ((hfinfo->display) & BASE_DISPLAY_E_MASK) {
+
+ case BASE_NONE:
+ case BASE_DEC:
+ switch (hfinfo->type) {
+
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ value_display_string = g_strdup_printf("%u", value->value);
+ break;
+
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ value_display_string = g_strdup_printf("%d", value->value);
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+ break;
+
+ case BASE_HEX:
+ value_display_string = g_strdup_printf("0x%x", value->value);
+ break;
+
+ case BASE_OCT:
+ value_display_string = g_strdup_printf("%#o", value->value);
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+ }
+
+ gtk_entry_set_text(GTK_ENTRY(value_entry), value_display_string);
+ g_free (value_display_string);
+}
+
+static void
+dfilter_report_bad_value(const char *format, ...)
+{
+ char error_msg_buf[1024];
+ va_list args;
+
+ va_start(args, format);
+ g_vsnprintf(error_msg_buf, sizeof error_msg_buf, format, args);
+ va_end(args);
+
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_msg_buf);
+}
+
+static void
+dfilter_expr_dlg_accept_cb(GtkWidget *w, gpointer filter_te_arg)
+{
+ GtkWidget *filter_te = filter_te_arg;
+ GtkWidget *window = gtk_widget_get_toplevel(w);
+ GtkWidget *relation_list =
+ g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_RELATION_LIST_KEY);
+ GtkWidget *range_entry =
+ g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_RANGE_ENTRY_KEY);
+ GtkWidget *value_entry =
+ g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_ENTRY_KEY);
+ header_field_info *hfinfo;
+ gchar *item_str;
+ gchar *range_str, *stripped_range_str;
+ gchar *value_str, *stripped_value_str;
+ int pos;
+ gchar *chars;
+ ftenum_t ftype;
+ gboolean can_compare;
+ fvalue_t *fvalue;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gboolean quote_it;
+
+ /*
+ * Get the variable to be tested.
+ */
+ hfinfo = g_object_get_data(G_OBJECT(window), E_DFILTER_EXPR_CURRENT_VAR_KEY);
+
+ /*
+ * Get the relation operator to use.
+ */
+ if (gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list)),
+ &model, &iter)) {
+ gtk_tree_model_get(model, &iter, 0, &item_str, -1);
+ } else {
+ /* Nothing selected */
+ return;
+ }
+
+ /*
+ * Get the range to use, if any.
+ */
+ if (gtk_widget_get_sensitive(range_entry)) {
+ range_str = g_strdup(gtk_entry_get_text(GTK_ENTRY(range_entry)));
+ /*
+ * XXX - strip this even for strings?
+ * Doing so for strings means you can't match a string that has
+ * leading or trailing whitespace, but you can't see trailing
+ * whitespace in a text field, so it's not clear that it's
+ * a good idea to allow that.
+ */
+ stripped_range_str = g_strstrip(range_str);
+ if (strcmp(stripped_range_str, "") == 0) {
+ /*
+ * No range was specified.
+ */
+ g_free(range_str);
+ range_str = NULL;
+ stripped_range_str = NULL;
+ }
+
+ /*
+ * XXX - check it for validity?
+ */
+ } else {
+ range_str = NULL;
+ stripped_range_str = NULL;
+ }
+
+ /*
+ * If a range was specified, the type of the LHS of the
+ * comparison is FT_BYTES; otherwise, it's the type of the field.
+ */
+ if (range_str == NULL)
+ ftype = hfinfo->type;
+ else
+ ftype = FT_BYTES;
+
+ /*
+ * Make sure the relation is valid for the type in question.
+ * We may be offering relations that the type of the field
+ * can't support, because the field's type supports slicing,
+ * and the relation *is* supported on byte strings.
+ */
+ if (strcmp(item_str, "==") == 0)
+ can_compare = ftype_can_eq(ftype);
+ else if (strcmp(item_str, "!=") == 0)
+ can_compare = ftype_can_ne(ftype);
+ else if (strcmp(item_str, ">") == 0)
+ can_compare = ftype_can_gt(ftype);
+ else if (strcmp(item_str, "<") == 0)
+ can_compare = ftype_can_lt(ftype);
+ else if (strcmp(item_str, ">=") == 0)
+ can_compare = ftype_can_ge(ftype);
+ else if (strcmp(item_str, "<=") == 0)
+ can_compare = ftype_can_le(ftype);
+ else if (strcmp(item_str, "contains") == 0)
+ can_compare = ftype_can_contains(ftype);
+ else if (strcmp(item_str, "matches") == 0)
+ can_compare = ftype_can_matches(ftype);
+ else
+ can_compare = TRUE; /* not a comparison */
+ if (!can_compare) {
+ if (range_str == NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "That field can't be tested with \"%s\".",
+ item_str);
+ } else {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Ranges of that field can't be tested with \"%s\".",
+ item_str);
+ }
+ g_free(range_str);
+ g_free(item_str);
+ return;
+ }
+
+ /*
+ * Get the value to use, if any.
+ */
+ if (gtk_widget_get_sensitive(value_entry)) {
+ value_str = g_strdup(gtk_entry_get_text(GTK_ENTRY(value_entry)));
+ stripped_value_str = g_strstrip(value_str);
+ if (strcmp(stripped_value_str, "") == 0) {
+ /*
+ * This field takes a value, but they didn't supply
+ * one.
+ */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "That field must be compared with a value, "
+ "but you didn't specify a value with which to "
+ "compare it.");
+ g_free(range_str);
+ g_free(value_str);
+ g_free(item_str);
+ return;
+ }
+
+ /*
+ * Make sure the value is valid.
+ *
+ * If no range string was specified, it must be valid
+ * for the type of the field; if a range string was
+ * specified, must be valid for FT_BYTES.
+ */
+ if (strcmp(item_str, "contains") == 0) {
+ fvalue = fvalue_from_unparsed(ftype, stripped_value_str, TRUE,
+ dfilter_report_bad_value);
+ }
+ else {
+ fvalue = fvalue_from_unparsed(ftype, stripped_value_str, FALSE,
+ dfilter_report_bad_value);
+ }
+ if (fvalue == NULL) {
+ /*
+ * It's not valid.
+ *
+ * The dialog box was already popped up by
+ * "dfilter_report_bad_value()".
+ */
+ g_free(range_str);
+ g_free(value_str);
+ g_free(item_str);
+ return;
+ }
+ FVALUE_FREE(fvalue);
+ } else {
+ value_str = NULL;
+ stripped_value_str = NULL;
+ }
+
+ /*
+ * Insert the expression at the current cursor position.
+ * If there's a non-whitespace character to the left of it,
+ * insert a blank first; if there's a non-whitespace character
+ * to the right of it, insert a blank after it.
+ */
+ pos = gtk_editable_get_position(GTK_EDITABLE(filter_te));
+ chars = gtk_editable_get_chars(GTK_EDITABLE(filter_te), pos, pos + 1);
+ if (strcmp(chars, "") != 0 && !isspace((unsigned char)chars[0]))
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
+ g_free(chars);
+
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), hfinfo->abbrev,
+ (gint) strlen(hfinfo->abbrev), &pos);
+ if (range_str != NULL) {
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), "[", 1, &pos);
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te),
+ stripped_range_str, (gint) strlen(stripped_range_str), &pos);
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), "]", 1, &pos);
+ g_free(range_str);
+ }
+ if (item_str != NULL && !relation_is_presence_test(item_str)) {
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), item_str,
+ (gint) strlen(item_str), &pos);
+ }
+ if (value_str != NULL) {
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
+ /*
+ * XXX - we should do this by generating an appropriate display
+ * filter value string for this field; that requires us to have
+ * a "generate display filter string" method for every FT_ type.
+ */
+ switch (hfinfo->type) {
+
+ case FT_STRING:
+ case FT_STRINGZ:
+ case FT_UINT_STRING:
+ case FT_ABSOLUTE_TIME:
+ /*
+ * Always put quotes around the string.
+ */
+ quote_it = TRUE;
+ break;
+
+ default:
+ /*
+ * If the string contains white space, put quotes around it.
+ */
+ quote_it = (strpbrk(stripped_value_str, " \t") != NULL);
+ break;
+ }
+ if (quote_it) {
+ /*
+ * Put quotes around the string.
+ */
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), "\"",
+ 1, &pos);
+ }
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te),
+ stripped_value_str, (gint) strlen(stripped_value_str), &pos);
+ if (quote_it) {
+ /*
+ * Put quotes around the string.
+ */
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), "\"",
+ 1, &pos);
+ }
+ g_free(value_str);
+ }
+ chars = gtk_editable_get_chars(GTK_EDITABLE(filter_te), pos + 1, pos + 2);
+ if (strcmp(chars, "") != 0 && !isspace((unsigned char)chars[0]))
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), " ", 1, &pos);
+ g_free(chars);
+
+ /*
+ * Put the cursor after the expression we just entered into
+ * the text entry widget.
+ */
+ gtk_editable_set_position(GTK_EDITABLE(filter_te), pos);
+
+ /*
+ * We're done; destroy the dialog box (which is the top-level
+ * widget for the "Accept" button).
+ */
+ window_destroy(window);
+ g_free(item_str);
+}
+
+static void
+dfilter_expr_dlg_cancel_cb(GtkWidget *w _U_, gpointer parent_w)
+{
+ /*
+ * User pressed the cancel button; close the dialog box.
+ */
+ window_destroy(GTK_WIDGET(parent_w));
+}
+
+/* Treat this as a cancel, by calling "prefs_main_cancel_cb()" */
+static gboolean
+dfilter_expr_dlg_delete_event_cb(GtkWidget *w _U_, GdkEvent *event _U_,
+ gpointer parent_w)
+{
+ dfilter_expr_dlg_cancel_cb(NULL, parent_w);
+ return FALSE;
+}
+
+static void
+dfilter_expr_dlg_destroy_cb(GtkWidget *w, gpointer filter_te)
+{
+ /*
+ * The dialog box is being destroyed; disconnect from the
+ * "destroy" signal on the text entry box to which we're
+ * attached, as the handler for that signal is supposed
+ * to destroy us, but we're already gone.
+ */
+ g_signal_handlers_disconnect_by_func(filter_te, dfilter_expr_dlg_cancel_cb, w);
+}
+
+/*
+ * Length of string used for protocol fields.
+ */
+#define TAG_STRING_LEN 256
+
+GtkWidget *
+dfilter_expr_dlg_new(GtkWidget *filter_te)
+{
+ GtkWidget *window, *main_vb, *main_hb;
+
+ GtkWidget *field_vb, *field_tree_lb, *field_tree, *tree_scrolled_win;
+
+ GtkWidget *relation_vb, *relation_label, *relation_list, *relation_list_scrolled_win;
+/* GtkWidget *relation_present_rb, *relation_equals_rb, *relation_unequals_rb,
+ *relation_greater_rb, *relation_less_rb,
+ *relation_greaterequal_rb, *relation_lessequal_rb,
+ *relation_contains_rb, *relation_matches_rb;*/
+
+ GtkWidget *value_vb, *value_label, *value_entry;
+ GtkWidget *value_list_label, *value_list_scrolled_win, *value_list;
+ GtkWidget *range_label, *range_entry;
+
+ GtkWidget *list_bb, *ok_bt, *cancel_bt;
+ header_field_info *hfinfo;
+ int i;
+ protocol_t *protocol;
+ GtkTreeStore *store;
+ GtkTreeSelection *selection;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkListStore *l_store;
+ GtkTreeSelection *l_sel;
+
+ proto_initialize_all_prefixes();
+
+ window = dlg_conf_window_new("Wireshark: Filter Expression");
+ gtk_window_set_default_size(GTK_WINDOW(window), 500, 400);
+ gtk_container_set_border_width(GTK_CONTAINER(window), 5);
+
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(window), main_vb);
+
+ main_hb = gtk_hbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(main_hb), 5);
+ gtk_container_add(GTK_CONTAINER(main_vb), main_hb);
+
+ field_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(field_vb), 5);
+ gtk_container_add(GTK_CONTAINER(main_hb), field_vb);
+
+ field_tree_lb = gtk_label_new("Field name");
+ gtk_misc_set_alignment(GTK_MISC(field_tree_lb), 0.0f, 0.0f);
+ gtk_box_pack_start(GTK_BOX(field_vb), field_tree_lb, FALSE, FALSE, 0);
+
+ tree_scrolled_win = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tree_scrolled_win),
+ GTK_SHADOW_IN);
+ gtk_box_pack_start(GTK_BOX(field_vb), tree_scrolled_win, TRUE, TRUE, 0);
+ gtk_widget_set_size_request(tree_scrolled_win, 300, -1);
+
+
+ store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
+ field_tree = tree_view_new(GTK_TREE_MODEL(store));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(field_tree), FALSE);
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(field_tree));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Field name", renderer,
+ "text", 0, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(field_tree), column);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_column_set_sort_column_id(column, 0);
+ g_signal_connect(selection, "changed", G_CALLBACK(field_select_row_cb), field_tree);
+ gtk_container_add(GTK_CONTAINER(tree_scrolled_win), field_tree);
+
+ relation_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(relation_vb), 5);
+ gtk_container_add(GTK_CONTAINER(main_hb), relation_vb);
+
+ relation_label = gtk_label_new("Relation");
+ gtk_misc_set_alignment(GTK_MISC(relation_label), 0.0f, 0.0f);
+ gtk_box_pack_start(GTK_BOX(relation_vb), relation_label, FALSE, FALSE, 0);
+
+ relation_list_scrolled_win = scrolled_window_new(NULL, NULL);
+ /* never use a scrollbar in x direction, show the complete relation string */
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(relation_list_scrolled_win),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(relation_list_scrolled_win),
+ GTK_SHADOW_IN);
+
+ l_store = gtk_list_store_new(1, G_TYPE_STRING);
+ relation_list = tree_view_new(GTK_TREE_MODEL(l_store));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(relation_list), FALSE);
+ g_object_unref(G_OBJECT(l_store));
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("relation", renderer,
+ "text", 0, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(relation_list), column);
+ l_sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list));
+ gtk_tree_selection_set_mode(l_sel, GTK_SELECTION_BROWSE);
+ gtk_container_add(GTK_CONTAINER(relation_list_scrolled_win), relation_list);
+ gtk_box_pack_start(GTK_BOX(relation_vb), relation_list_scrolled_win, TRUE, TRUE, 0);
+
+ /*
+ * OK, show the relation label and range stuff as it would be
+ * with everything turned on, so it'll request as much space
+ * as it'll ever need, so the dialog box and widgets start out
+ * with the right sizes.
+ *
+ * XXX - this doesn't work. It *doesn't* request as much space
+ * as it'll ever need.
+ *
+ * XXX - FT_UINT8 doesn't support ranges, so even if it did work,
+ * it wouldn't work right.
+ *
+ * XXX - this no longer affects the range stuff, as that's
+ * controlled both by the type and by the relational operator
+ * selected.
+ */
+ show_relations(relation_list, FT_UINT8);
+
+ /*
+ relation_present_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "is present");
+ gtk_box_pack_start(GTK_BOX(relation_vb), relation_present_rb, FALSE, FALSE, 0);
+
+ relation_equals_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "==");
+ gtk_box_pack_start(GTK_BOX(relation_vb), relation_equals_rb, FALSE, FALSE, 0);
+
+ relation_unequals_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "!=");
+ gtk_box_pack_start(GTK_BOX(relation_vb), relation_unequals_rb, FALSE, FALSE, 0);
+
+ relation_greater_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), ">");
+ gtk_box_pack_start(GTK_BOX(relation_vb), relation_greater_rb, FALSE, FALSE, 0);
+
+ relation_less_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "<");
+ gtk_box_pack_start(GTK_BOX(relation_vb), relation_less_rb, FALSE, FALSE, 0);
+
+ relation_greaterequal_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), ">=");
+ gtk_box_pack_start(GTK_BOX(relation_vb), relation_greaterequal_rb, FALSE, FALSE, 0);
+
+ relation_lessequal_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "<=");
+ gtk_box_pack_start(GTK_BOX(relation_vb), relation_lessequal_rb, FALSE, FALSE, 0);
+
+ relation_contains_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "contains");
+ gtk_box_pack_start(GTK_BOX(relation_vb), relation_contains_rb, FALSE, FALSE, 0);
+
+ relation_matches_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(relation_present_rb), "matches");
+ gtk_box_pack_start(GTK_BOX(relation_vb), relation_matches_rb, FALSE, FALSE, 0);
+*/
+ /* value column */
+ value_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(value_vb), 5);
+ gtk_container_add(GTK_CONTAINER(main_hb), value_vb);
+
+ value_label = gtk_label_new("Value");
+ gtk_misc_set_alignment(GTK_MISC(value_label), 0.0f, 0.0f);
+ gtk_box_pack_start(GTK_BOX(value_vb), value_label, FALSE, FALSE, 0);
+
+ value_entry = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(value_vb), value_entry, FALSE, FALSE, 0);
+
+ value_list_label = gtk_label_new("Predefined values:");
+ gtk_misc_set_alignment(GTK_MISC(value_list_label), 0.0f, 0.0f);
+ gtk_box_pack_start(GTK_BOX(value_vb), value_list_label, FALSE, FALSE, 0);
+
+ value_list_scrolled_win = scrolled_window_new(NULL, NULL);
+ gtk_box_pack_start(GTK_BOX(value_vb), value_list_scrolled_win, TRUE,
+ TRUE, 0);
+
+ l_store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
+ value_list = tree_view_new(GTK_TREE_MODEL(l_store));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(value_list), FALSE);
+ g_object_unref(G_OBJECT(l_store));
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("value", renderer,
+ "text", 0, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(value_list), column);
+ g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(value_list)),
+ "changed", G_CALLBACK(value_list_sel_cb), value_entry);
+
+ /*
+ * The value stuff may be hidden or shown depending on what
+ * relation was selected; connect to the "changed" signal
+ * for the relation list, so we can make that happen.
+ */
+ g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(relation_list)),
+ "changed", G_CALLBACK(relation_list_sel_cb), NULL);
+ l_sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(value_list));
+ gtk_tree_selection_set_mode(l_sel, GTK_SELECTION_SINGLE);
+ gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(value_list_scrolled_win),
+ value_list);
+ /* This remains hidden until an enumerated field is selected */
+
+ /*
+ * Put the items in the Tree; we don't want to do that until
+ * we've constructed the value list and set the tree's
+ * E_DFILTER_EXPR_VALUE_LIST_KEY data to point to it, and
+ * constructed the "Accept" button and set the tree's
+ * E_DFILTER_EXPR_OK_BT_KEY data to point to it, so that
+ * when the list item is "helpfully" automatically selected for us
+ * we're ready to cope with the selection signal.
+ */
+
+{
+ /* GTK2 code using two levels iterator to enumerate all protocol fields */
+
+ GtkTreeIter iter, child_iter;
+ void *cookie, *cookie2;
+
+ for (i = proto_get_first_protocol(&cookie); i != -1;
+ i = proto_get_next_protocol(&cookie)) {
+ char *strp, str[TAG_STRING_LEN+1];
+
+ protocol = find_protocol_by_id(i);
+
+ if (!proto_is_protocol_enabled(protocol)) {
+ continue;
+ }
+
+ g_snprintf(str, TAG_STRING_LEN, "%s - %s",
+ proto_get_protocol_short_name(protocol),
+ proto_get_protocol_long_name(protocol));
+ strp=str;
+
+ hfinfo = proto_registrar_get_nth(i);
+
+ gtk_tree_store_append(store, &iter, NULL);
+ gtk_tree_store_set(store, &iter, 0, strp, 1, hfinfo, -1);
+
+ for (hfinfo = proto_get_first_protocol_field(i, &cookie2); hfinfo != NULL;
+ hfinfo = proto_get_next_protocol_field(&cookie2)) {
+
+ if (hfinfo->same_name_prev != NULL) /* ignore duplicate names */
+ continue;
+
+ if (hfinfo->blurb != NULL && hfinfo->blurb[0] != '\0') {
+ g_snprintf(str, TAG_STRING_LEN, "%s - %s (%s)",
+ hfinfo->abbrev, hfinfo->name, hfinfo->blurb);
+ } else {
+ g_snprintf(str, TAG_STRING_LEN, "%s - %s", hfinfo->abbrev,
+ hfinfo->name);
+ }
+ gtk_tree_store_append(store, &child_iter, &iter);
+ gtk_tree_store_set(store, &child_iter, 0, strp, 1, hfinfo, -1);
+ }
+ }
+ g_object_unref(G_OBJECT(store));
+}
+
+ range_label = gtk_label_new("Range (offset:length)");
+ gtk_misc_set_alignment(GTK_MISC(range_label), 0.0f, 0.0f);
+ gtk_box_pack_start(GTK_BOX(value_vb), range_label, FALSE, FALSE, 0);
+
+ range_entry = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(value_vb), range_entry, FALSE, FALSE, 0);
+
+
+ /* button box */
+ list_bb = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), list_bb, FALSE, FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (list_bb), 0);
+
+ ok_bt = g_object_get_data(G_OBJECT(list_bb), GTK_STOCK_OK);
+ gtk_widget_set_sensitive(ok_bt, FALSE);
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(dfilter_expr_dlg_accept_cb), filter_te);
+
+ cancel_bt = g_object_get_data(G_OBJECT(list_bb), GTK_STOCK_CANCEL);
+ window_set_cancel_button(window, cancel_bt, NULL);
+ g_signal_connect(cancel_bt, "clicked", G_CALLBACK(dfilter_expr_dlg_cancel_cb), window);
+
+ gtk_widget_grab_default(ok_bt);
+
+ /* Catch the "activate" signal on the range and value text entries,
+ so that if the user types Return there, we act as if the "Accept"
+ button had been selected, as happens if Return is typed if some
+ widget that *doesn't* handle the Return key has the input focus. */
+ dlg_set_activate(range_entry, ok_bt);
+ dlg_set_activate(value_entry, ok_bt);
+
+ g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_RELATION_LIST_KEY, relation_list);
+ g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_RANGE_LABEL_KEY, range_label);
+ g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_RANGE_ENTRY_KEY, range_entry);
+ g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LABEL_KEY, value_label);
+ g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_ENTRY_KEY, value_entry);
+ g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_KEY, value_list);
+ g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_LABEL_KEY, value_list_label);
+ g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_VALUE_LIST_SW_KEY,
+ value_list_scrolled_win);
+ g_object_set_data(G_OBJECT(window), E_DFILTER_EXPR_OK_BT_KEY, ok_bt);
+
+ g_signal_connect(window, "delete_event", G_CALLBACK(dfilter_expr_dlg_delete_event_cb), window);
+
+ /*
+ * Catch the "destroy" signal on our top-level window, and,
+ * when it's destroyed, disconnect the signal we'll be
+ * connecting below.
+ */
+ g_signal_connect(window, "destroy", G_CALLBACK(dfilter_expr_dlg_destroy_cb), filter_te);
+
+ /*
+ * Catch the "destroy" signal on the text entry widget to which
+ * we're attached; if it's destroyed, we should destroy ourselves
+ * as well.
+ */
+ g_signal_connect(filter_te, "destroy", G_CALLBACK(dfilter_expr_dlg_cancel_cb), window);
+
+ gtk_widget_show_all(window);
+ window_present(window);
+
+ return window;
+}
diff --git a/ui/gtk/dfilter_expr_dlg.h b/ui/gtk/dfilter_expr_dlg.h
new file mode 100644
index 0000000000..8975bbc857
--- /dev/null
+++ b/ui/gtk/dfilter_expr_dlg.h
@@ -0,0 +1,41 @@
+/* dfilter_expr_dlg.h
+ * Definitions for dialog boxes for display filter expression construction
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __DFILTER_EXPR_DLG_H__
+#define __DFILTER_EXPR_DLG_H__
+
+/** @file
+ * "Add Expression" dialog box.
+ * @ingroup dialog_group
+ */
+
+/** User requested the "Add Expression" dialog box by menu or toolbar.
+ *
+ * @param widget corresponding text entry widget
+ * @return the newly created dialog widget
+ */
+GtkWidget *dfilter_expr_dlg_new(GtkWidget *widget);
+
+#endif /* dfilter_expr_dlg.h */
diff --git a/ui/gtk/diameter_stat.c b/ui/gtk/diameter_stat.c
new file mode 100644
index 0000000000..3c6733f588
--- /dev/null
+++ b/ui/gtk/diameter_stat.c
@@ -0,0 +1,246 @@
+/* diameter_stat.c
+ * Diameter Service Response Time Statistics
+ * (c) 2008 Abhik Sarkar
+ *
+ * Based almost completely on gtp_stat by Kari Tiirikainen
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-diameter.h>
+
+#include "../timestats.h"
+#include "../simple_dialog.h"
+#include "../file.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/service_response_time_table.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/main.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _diameterstat_t {
+ GtkWidget *win;
+ srt_stat_table diameter_srt_table;
+} diameterstat_t;
+
+static GHashTable* cmd_str_hash;
+
+static void
+diameterstat_set_title(diameterstat_t *diameter)
+{
+ char *title;
+
+ title = g_strdup_printf("Diameter Service Response Time statistics: %s",
+ cf_get_display_name(&cfile));
+ gtk_window_set_title(GTK_WINDOW(diameter->win), title);
+ g_free(title);
+}
+
+static void
+diameterstat_reset(void *pdiameter)
+{
+ diameterstat_t *diameter=(diameterstat_t *)pdiameter;
+
+ reset_srt_table_data(&diameter->diameter_srt_table);
+ diameterstat_set_title(diameter);
+}
+
+
+static int
+diameterstat_packet(void *pdiameter, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pdi)
+{
+ const diameter_req_ans_pair_t *diameter=pdi;
+ diameterstat_t *fs=(diameterstat_t *)pdiameter;
+ int* idx = NULL;
+
+ /* Process only answers where corresponding request is found.
+ * Unpaired daimeter messages are currently not supported by statistics.
+ * Return 0, since redraw is not needed. */
+ if(!diameter || diameter->processing_request || !diameter->req_frame)
+ return 0;
+
+ idx = (int*) g_hash_table_lookup(cmd_str_hash, diameter->cmd_str);
+ if (idx == NULL) {
+ idx = g_malloc(sizeof(int));
+ *idx = (int) g_hash_table_size(cmd_str_hash);
+ g_hash_table_insert(cmd_str_hash, (gchar*) diameter->cmd_str, idx);
+ init_srt_table_row(&fs->diameter_srt_table, *idx, (const char*) diameter->cmd_str);
+ }
+
+ add_srt_table_data(&fs->diameter_srt_table, *idx, &diameter->req_time, pinfo);
+
+ return 1;
+}
+
+
+
+static void
+diameterstat_draw(void *pdiameter)
+{
+ diameterstat_t *diameter=(diameterstat_t *)pdiameter;
+
+ draw_srt_table_data(&diameter->diameter_srt_table);
+}
+
+
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ diameterstat_t *diameter=(diameterstat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(diameter);
+ unprotect_thread_critical_region();
+
+ free_srt_table_data(&diameter->diameter_srt_table);
+ g_free(diameter);
+ g_hash_table_destroy(cmd_str_hash);
+}
+
+
+static void
+gtk_diameterstat_init(const char *optarg, void *userdata _U_)
+{
+ diameterstat_t *diameter;
+ const char *filter=NULL;
+ GtkWidget *label;
+ char *filter_string;
+ GString *error_string;
+ GtkWidget *vbox;
+ GtkWidget *bbox;
+ GtkWidget *close_bt;
+ int* idx;
+
+ if(!strncmp(optarg,"diameter,",9)){
+ filter=optarg+9;
+ } else {
+ filter="diameter"; /*NULL doesn't work here like in LDAP. Too little time/lazy to find out why ?*/
+ }
+
+ diameter=g_malloc(sizeof(diameterstat_t));
+ idx = g_malloc(sizeof(int));
+ *idx = 0;
+ cmd_str_hash = g_hash_table_new(g_str_hash,g_str_equal);
+ g_hash_table_insert(cmd_str_hash, (gchar *)"Unknown", idx);
+
+ diameter->win = dlg_window_new("diameter-stat"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(diameter->win), TRUE);
+ gtk_window_set_default_size(GTK_WINDOW(diameter->win), 550, 400);
+ diameterstat_set_title(diameter);
+
+ vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(diameter->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ label=gtk_label_new("Diameter Service Response Time statistics");
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ filter_string = g_strdup_printf("Filter: %s", filter ? filter : "");
+ label=gtk_label_new(filter_string);
+ g_free(filter_string);
+ gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ label=gtk_label_new("Diameter Requests");
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ /* We must display TOP LEVEL Widget before calling init_srt_table() */
+ gtk_widget_show_all(diameter->win);
+
+ /** @todo the filter to use in stead of NULL is "diameter.cmd.code"
+ * to enable the filter popup in the service response time dalouge
+ * Note to make it work the command code must be stored rather than the
+ * index.
+ */
+ init_srt_table(&diameter->diameter_srt_table, 1, vbox, NULL);
+ init_srt_table_row(&diameter->diameter_srt_table, 0, "Unknown");
+
+ error_string=register_tap_listener("diameter", diameter, filter, 0, diameterstat_reset, diameterstat_packet, diameterstat_draw);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(diameter);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(diameter->win, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(diameter->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(diameter->win, "destroy", G_CALLBACK(win_destroy_cb), diameter);
+
+ gtk_widget_show_all(diameter->win);
+ window_present(diameter->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(diameter->win));
+}
+
+static tap_param diameter_stat_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg diameter_stat_dlg = {
+ "Diameter Service Response Time Statistics",
+ "diameter",
+ gtk_diameterstat_init,
+ -1,
+ G_N_ELEMENTS(diameter_stat_params),
+ diameter_stat_params
+};
+
+void
+register_tap_listener_gtkdiameterstat(void)
+{
+ register_dfilter_stat(&diameter_stat_dlg, "Diameter",
+ REGISTER_STAT_GROUP_RESPONSE_TIME);
+}
+
+void diameter_srt_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &diameter_stat_dlg);
+}
+
diff --git a/ui/gtk/dissector_tables_dlg.c b/ui/gtk/dissector_tables_dlg.c
new file mode 100644
index 0000000000..ce5c59a1cf
--- /dev/null
+++ b/ui/gtk/dissector_tables_dlg.c
@@ -0,0 +1,402 @@
+/* dissector_tables_dlg.c
+ * dissector_tables_dlg 2010 Anders Broman
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <glib.h>
+
+#include <epan/packet.h>
+
+#include <gtk/gtk.h>
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/dissector_tables_dlg.h"
+
+static GtkWidget *dissector_tables_dlg_w = NULL;
+
+/* The columns */
+enum
+{
+ TABLE_UI_NAME_COL,
+ TABLE_SHORT_NAME_COL,
+ N_COLUMNS
+};
+
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data _U_)
+{
+
+ if (dissector_tables_dlg_w != NULL) {
+ window_destroy(dissector_tables_dlg_w);
+ dissector_tables_dlg_w = NULL;
+ }
+
+
+}
+
+/*
+ * For a dissector table, put
+ * its short name and its
+ * descriptive name in the treeview.
+ */
+
+struct dissector_tables_tree_info {
+ GtkWidget *tree;
+ GtkTreeIter iter;
+ GtkTreeIter new_iter;
+};
+
+typedef struct dissector_tables_tree_info dissector_tables_tree_info_t;
+
+static gint
+ui_sort_func(GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ gchar *stra, *strb;
+
+ /* The col to get data from is in userdata */
+ gint data_column = GPOINTER_TO_INT(user_data);
+
+ gtk_tree_model_get(model, a, data_column, &stra, -1);
+ gtk_tree_model_get(model, b, data_column, &strb, -1);
+
+ return strcmp(stra, strb);
+}
+
+
+/*
+ * Struct to hold the pointer to the trees
+ * for dissector tables.
+ */
+struct dissector_tables_trees {
+ GtkWidget *str_tree_wgt;
+ GtkWidget *uint_tree_wgt;
+ GtkWidget *heuristic_tree_wgt;
+};
+
+typedef struct dissector_tables_trees dissector_tables_trees_t;
+
+static void
+proto_add_to_list(dissector_tables_tree_info_t *tree_info,
+ GtkTreeStore *store,
+ gchar *str,
+ const gchar *proto_name)
+{
+ gtk_tree_store_insert_with_values(store, &tree_info->new_iter, &tree_info->iter, G_MAXINT,
+ TABLE_UI_NAME_COL, str,
+ TABLE_SHORT_NAME_COL, proto_name,
+ -1);
+}
+
+static void
+decode_proto_add_to_list (const gchar *table_name _U_, ftenum_t selector_type,
+ gpointer key, gpointer value _U_, gpointer user_data)
+{
+ GtkTreeStore *store;
+ const gchar *proto_name;
+ dtbl_entry_t *dtbl_entry;
+ dissector_handle_t handle;
+ guint32 port;
+ gchar *str;
+ dissector_tables_tree_info_t *tree_info;
+
+ tree_info = user_data;
+ dtbl_entry = value;
+ handle = dtbl_entry_get_handle(dtbl_entry);
+ proto_name = dissector_handle_get_short_name(handle);
+
+ store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_info->tree)));
+
+ switch (selector_type) {
+
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ port = GPOINTER_TO_UINT(key);
+ /* Hack: Use fixed width rj str so alpha sort (strcmp) will sort field numerically */
+ str = g_strdup_printf ("%10d", port);
+ proto_add_to_list(tree_info, store, str, proto_name);
+ g_free (str);
+ break;
+
+ case FT_STRING:
+ case FT_STRINGZ:
+ str = (gchar*) key;
+ proto_add_to_list(tree_info, store, str, proto_name);
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+
+}
+
+static void
+table_name_add_to_list(dissector_tables_tree_info_t *tree_info,
+ GtkWidget *tree_view,
+ const char *table_name,
+ const char *ui_name)
+{
+ GtkTreeStore *store;
+
+ tree_info->tree = tree_view;
+ store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view))); /* Get store */
+
+ gtk_tree_store_insert_with_values(store, &tree_info->iter, NULL, G_MAXINT,
+ TABLE_UI_NAME_COL, ui_name,
+ TABLE_SHORT_NAME_COL, table_name,
+ -1);
+}
+
+static void
+display_heur_dissector_table_entries(gpointer data, gpointer user_data)
+{
+ heur_dtbl_entry_t *dtbl_entry = data;
+ dissector_tables_tree_info_t *tree_info = user_data;
+ GtkTreeStore *store;
+
+ if(dtbl_entry->protocol){
+
+ store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_info->tree))); /* Get store */
+ proto_add_to_list(tree_info, store,
+ (gchar *)proto_get_protocol_long_name(dtbl_entry->protocol),
+ proto_get_protocol_short_name(dtbl_entry->protocol));
+ }else{
+ g_warning("no protocol info");
+ }
+
+
+}
+
+static void
+display_heur_dissector_table_names(const char *table_name, gpointer table, gpointer w)
+{
+ dissector_tables_trees_t *dis_tbl_trees;
+ dissector_tables_tree_info_t *tree_info;
+ heur_dissector_list_t *list;
+
+ tree_info = g_new(dissector_tables_tree_info_t, 1);
+ dis_tbl_trees = w;
+ list = table;
+
+ table_name_add_to_list(tree_info, dis_tbl_trees->heuristic_tree_wgt, "", table_name);
+
+ if(table){
+ g_slist_foreach (*list, display_heur_dissector_table_entries, tree_info);
+ }
+
+}
+
+static void
+display_dissector_table_names(const char *table_name, const char *ui_name, gpointer w)
+{
+ dissector_tables_trees_t *dis_tbl_trees;
+ dissector_tables_tree_info_t *tree_info;
+ ftenum_t selector_type = get_dissector_table_selector_type(table_name);
+
+ tree_info = g_new(dissector_tables_tree_info_t, 1);
+ dis_tbl_trees = w;
+
+ switch (selector_type) {
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ table_name_add_to_list(tree_info, dis_tbl_trees->uint_tree_wgt, table_name, ui_name);
+ break;
+ case FT_STRING:
+ case FT_STRINGZ:
+ table_name_add_to_list(tree_info, dis_tbl_trees->str_tree_wgt, table_name, ui_name);
+ break;
+ default:
+ break;
+ }
+ dissector_table_foreach(table_name, decode_proto_add_to_list, tree_info);
+
+ g_free(tree_info);
+}
+
+static GtkWidget*
+init_table(void)
+{
+ GtkTreeStore *store;
+ GtkWidget *tree;
+ GtkTreeView *tree_view;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSortable *sortable;
+
+ /* Create the store */
+ store = gtk_tree_store_new (N_COLUMNS, /* Total number of columns */
+ G_TYPE_STRING, /* Table */
+ G_TYPE_STRING); /* Table */
+
+ /* Create a view */
+ tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
+ tree_view = GTK_TREE_VIEW(tree);
+ sortable = GTK_TREE_SORTABLE(store);
+
+ /* Speed up the list display */
+ gtk_tree_view_set_fixed_height_mode(tree_view, TRUE);
+
+ /* Setup the sortable columns */
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW (tree), FALSE);
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref (G_OBJECT (store));
+
+ /* Create the first column, associating the "text" attribute of the
+ * cell_renderer to the first column of the model */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("UI name", renderer, "text", TABLE_UI_NAME_COL, NULL);
+ gtk_tree_sortable_set_sort_func(sortable, TABLE_UI_NAME_COL,
+ ui_sort_func, GINT_TO_POINTER(TABLE_UI_NAME_COL), NULL);
+ gtk_tree_view_column_set_sort_column_id(column, TABLE_UI_NAME_COL);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_column_set_fixed_width(column, 330);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Short name", renderer, "text", TABLE_SHORT_NAME_COL, NULL);
+ gtk_tree_sortable_set_sort_func(sortable, TABLE_SHORT_NAME_COL,
+ ui_sort_func, GINT_TO_POINTER(TABLE_SHORT_NAME_COL), NULL);
+ gtk_tree_view_column_set_sort_column_id(column, TABLE_SHORT_NAME_COL);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_column_set_fixed_width(column, 100);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
+
+ return tree;
+
+}
+
+static void
+dissector_tables_dlg_init(void)
+{
+ dissector_tables_trees_t dis_tbl_trees;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *main_nb;
+ GtkWidget *scrolled_window;
+ GtkTreeSortable *sortable;
+ GtkWidget *temp_page, *tmp;
+
+ dissector_tables_dlg_w = dlg_window_new("Dissector tables"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(dissector_tables_dlg_w), TRUE);
+ gtk_window_set_default_size(GTK_WINDOW(dissector_tables_dlg_w), 700, 300);
+
+ vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(dissector_tables_dlg_w), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ main_nb = gtk_notebook_new();
+ gtk_box_pack_start(GTK_BOX(vbox), main_nb, TRUE, TRUE, 0);
+
+ /* String tables */
+ temp_page = gtk_vbox_new(FALSE, 6);
+ tmp = gtk_label_new("String tables");
+ gtk_widget_show(tmp);
+ hbox = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(hbox), tmp);
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, hbox);
+
+ scrolled_window = scrolled_window_new(NULL, NULL);
+ dis_tbl_trees.str_tree_wgt = init_table();
+ gtk_widget_show(dis_tbl_trees.str_tree_wgt);
+ gtk_container_add(GTK_CONTAINER(scrolled_window), dis_tbl_trees.str_tree_wgt);
+ gtk_box_pack_start(GTK_BOX(temp_page), scrolled_window, TRUE, TRUE, 0);
+ gtk_widget_show(scrolled_window);
+
+ /* uint tables */
+ temp_page = gtk_vbox_new(FALSE, 6);
+ tmp = gtk_label_new("Integer tables");
+ gtk_widget_show(tmp);
+ hbox = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(hbox), tmp);
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, hbox);
+
+ scrolled_window = scrolled_window_new(NULL, NULL);
+ dis_tbl_trees.uint_tree_wgt = init_table();
+ gtk_widget_show(dis_tbl_trees.uint_tree_wgt);
+ gtk_container_add(GTK_CONTAINER(scrolled_window), dis_tbl_trees.uint_tree_wgt);
+ gtk_box_pack_start(GTK_BOX(temp_page), scrolled_window, TRUE, TRUE, 0);
+ gtk_widget_show(scrolled_window);
+
+ /* heuristic tables */
+ temp_page = gtk_vbox_new(FALSE, 6);
+ tmp = gtk_label_new("Heuristic tables");
+ gtk_widget_show(tmp);
+ hbox = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(hbox), tmp);
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, hbox);
+
+ scrolled_window = scrolled_window_new(NULL, NULL);
+ dis_tbl_trees.heuristic_tree_wgt = init_table();
+ gtk_widget_show(dis_tbl_trees.heuristic_tree_wgt);
+ gtk_container_add(GTK_CONTAINER(scrolled_window), dis_tbl_trees.heuristic_tree_wgt);
+ gtk_box_pack_start(GTK_BOX(temp_page), scrolled_window, TRUE, TRUE, 0);
+ gtk_widget_show(scrolled_window);
+
+ /* We must display TOP LEVEL Widget before calling init_table() */
+ gtk_widget_show_all(dissector_tables_dlg_w);
+ g_signal_connect(dissector_tables_dlg_w, "destroy", G_CALLBACK(win_destroy_cb), NULL);
+
+ /* Fill the table with data */
+ dissector_all_tables_foreach_table(display_dissector_table_names, (gpointer)&dis_tbl_trees);
+
+ dissector_all_heur_tables_foreach_table(display_heur_dissector_table_names, (gpointer)&dis_tbl_trees);
+
+ sortable = GTK_TREE_SORTABLE(gtk_tree_view_get_model(GTK_TREE_VIEW(dis_tbl_trees.str_tree_wgt)));
+ gtk_tree_sortable_set_sort_column_id(sortable, TABLE_UI_NAME_COL, GTK_SORT_ASCENDING);
+
+ sortable = GTK_TREE_SORTABLE(gtk_tree_view_get_model(GTK_TREE_VIEW(dis_tbl_trees.uint_tree_wgt)));
+ gtk_tree_sortable_set_sort_column_id(sortable, TABLE_UI_NAME_COL, GTK_SORT_ASCENDING);
+
+ sortable = GTK_TREE_SORTABLE(gtk_tree_view_get_model(GTK_TREE_VIEW(dis_tbl_trees.heuristic_tree_wgt)));
+ gtk_tree_sortable_set_sort_column_id(sortable, TABLE_UI_NAME_COL, GTK_SORT_ASCENDING);
+
+}
+
+void
+dissector_tables_dlg_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ if (dissector_tables_dlg_w) {
+ reactivate_window(dissector_tables_dlg_w);
+ } else {
+ dissector_tables_dlg_init();
+ }
+}
diff --git a/ui/gtk/dissector_tables_dlg.h b/ui/gtk/dissector_tables_dlg.h
new file mode 100644
index 0000000000..6f21d225c3
--- /dev/null
+++ b/ui/gtk/dissector_tables_dlg.h
@@ -0,0 +1,25 @@
+/* dissector_tables_dlg.h
+ * dissector_tables_dlg 2010 Anders Broman
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+void dissector_tables_dlg_cb(GtkWidget *w, gpointer d);
diff --git a/ui/gtk/dlg_utils.c b/ui/gtk/dlg_utils.c
new file mode 100644
index 0000000000..3a77689e0c
--- /dev/null
+++ b/ui/gtk/dlg_utils.c
@@ -0,0 +1,514 @@
+/* dlg_utils.c
+ * Utilities to use when constructing dialogs
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <string.h>
+#include <stdarg.h>
+
+#include <gtk/gtk.h>
+
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/stock_icons.h"
+
+#include "epan/filesystem.h"
+
+
+static void
+dlg_activate (GtkWidget *widget, gpointer ok_button);
+
+/* create a button for the button row (helper for dlg_button_row_new) */
+static GtkWidget *
+dlg_button_new(GtkWidget *hbox, GtkWidget *button_hbox, const gchar *stock_id)
+{
+ GtkWidget *button;
+
+ button = gtk_button_new_from_stock(stock_id);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(button, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+#endif
+ g_object_set_data(G_OBJECT(hbox), stock_id, button);
+ gtk_box_pack_end(GTK_BOX(button_hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show(button);
+ return button;
+}
+
+/*
+ * Set the focus and default for the nth item in a button row, with
+ * 0 being the first item.
+ */
+#define BUTTON_HBOX_KEY "button_hbox"
+void
+dlg_button_focus_nth(GtkWidget *hbox, gint focus_item) {
+ GtkWidget *button_hbox, *button;
+ GList *children;
+ gint cur_item = 0;
+
+ if (!hbox)
+ return;
+
+ button_hbox = g_object_get_data(G_OBJECT(hbox), BUTTON_HBOX_KEY);
+ children = gtk_container_get_children(GTK_CONTAINER(button_hbox));
+
+ while (children) {
+ if (cur_item == focus_item) {
+ button = children->data;
+ gtk_widget_grab_focus(button);
+ gtk_widget_grab_default(button);
+ break;
+ }
+ children = g_list_next(children);
+ cur_item++;
+ }
+
+ g_list_free(children);
+}
+
+/* create a button row for a dialog */
+
+/* The purpose of this is, to have one place available, where all button rows
+ * from all dialogs are laid out. This will:
+ *
+ * a.) keep the button layout more consistent over the different dialogs
+ * b.) being able to switch between different button layouts, e.g.:
+ * e.g. Win32: "OK" "Apply" "Cancel"
+ * e.g. GNOME: "Apply" "Cancel" "OK"
+ */
+GtkWidget *
+dlg_button_row_new(const gchar *stock_id_first, ...)
+{
+ gint buttons = 0;
+ va_list stock_id_list;
+ const gchar *stock_id = stock_id_first;
+ GtkWidget *hbox;
+ GtkWidget *button_hbox;
+ GtkWidget *help_hbox;
+ GtkWidget *button;
+
+ const gchar *apply = NULL;
+ const gchar *cancel = NULL;
+ const gchar *cap_start = NULL;
+ const gchar *cap_stop = NULL;
+ const gchar *cap_options = NULL;
+#ifdef _WIN32
+ const gchar *cap_details = NULL;
+#endif
+ const gchar *clear = NULL;
+ const gchar *close = NULL;
+ const gchar *copy = NULL;
+ const gchar *create_stat = NULL;
+ const gchar *delete = NULL;
+ const gchar *dont_save = NULL;
+ const gchar *filter_stream= NULL;
+ const gchar *find = NULL;
+ const gchar *help = NULL;
+ const gchar *jump = NULL;
+ const gchar *no = NULL;
+ const gchar *ok = NULL;
+ const gchar *print = NULL;
+ const gchar *save = NULL;
+ const gchar *save_as = NULL;
+ const gchar *save_all = NULL;
+ const gchar *stop = NULL;
+ const gchar *yes = NULL;
+#ifdef HAVE_GEOIP
+ const gchar *map = NULL;
+#endif /* HAVE_GEOIP */
+ const gchar *follow_stream = NULL;
+
+
+ va_start(stock_id_list, stock_id_first);
+
+ /* get all buttons needed */
+ while(stock_id != NULL) {
+ if (strcmp(stock_id, GTK_STOCK_OK) == 0) {
+ ok = stock_id;
+ } else if (strcmp(stock_id, WIRESHARK_STOCK_CREATE_STAT) == 0) {
+ create_stat = stock_id;
+ } else if (strcmp(stock_id, GTK_STOCK_APPLY) == 0) {
+ apply = stock_id;
+ } else if (strcmp(stock_id, GTK_STOCK_SAVE) == 0) {
+ save = stock_id;
+ } else if (strcmp(stock_id, GTK_STOCK_SAVE_AS) == 0) {
+ save_as = stock_id;
+ } else if (strcmp(stock_id, WIRESHARK_STOCK_SAVE_ALL) == 0) {
+ save_all = stock_id;
+ } else if (strcmp(stock_id, WIRESHARK_STOCK_DONT_SAVE) == 0) {
+ dont_save = stock_id;
+ } else if (strcmp(stock_id, WIRESHARK_STOCK_QUIT_DONT_SAVE) == 0) {
+ dont_save = stock_id;
+ } else if (strcmp(stock_id, GTK_STOCK_CANCEL) == 0) {
+ cancel = stock_id;
+ } else if (strcmp(stock_id, GTK_STOCK_CLOSE) == 0) {
+ close = stock_id;
+ } else if (strcmp(stock_id, GTK_STOCK_CLEAR) == 0) {
+ clear = stock_id;
+#ifdef HAVE_LIBPCAP
+ } else if (strcmp(stock_id, WIRESHARK_STOCK_CAPTURE_START) == 0) {
+ cap_start = stock_id;
+ } else if (strcmp(stock_id, WIRESHARK_STOCK_CAPTURE_STOP) == 0) {
+ cap_stop = stock_id;
+ } else if (strcmp(stock_id, WIRESHARK_STOCK_CAPTURE_OPTIONS) == 0) {
+ cap_options = stock_id;
+#ifdef _WIN32
+ } else if (strcmp(stock_id, WIRESHARK_STOCK_CAPTURE_DETAILS) == 0) {
+ cap_details = stock_id;
+#endif
+#endif /* HAVE_LIBPCAP */
+#ifdef HAVE_GEOIP
+ } else if (strcmp(stock_id, WIRESHARK_STOCK_MAP) == 0) {
+ map = stock_id;
+#endif /* HAVE_GEOIP */
+ } else if (strcmp(stock_id, WIRESHARK_STOCK_FOLLOW_STREAM) == 0) {
+ follow_stream = stock_id;
+ } else if (strcmp(stock_id, GTK_STOCK_STOP) == 0) {
+ stop = stock_id;
+ } else if (strcmp(stock_id, GTK_STOCK_HELP) == 0) {
+ help = stock_id;
+ } else if (strcmp(stock_id, GTK_STOCK_PRINT) == 0) {
+ print = stock_id;
+ } else if (strcmp(stock_id, GTK_STOCK_FIND) == 0) {
+ find = stock_id;
+ } else if (strcmp(stock_id, GTK_STOCK_JUMP_TO) == 0) {
+ jump = stock_id;
+ } else if (strcmp(stock_id, GTK_STOCK_YES) == 0) {
+ yes = stock_id;
+ } else if (strcmp(stock_id, GTK_STOCK_NO) == 0) {
+ no = stock_id;
+ } else if (strcmp(stock_id, WIRESHARK_STOCK_FILTER_OUT_STREAM) == 0) {
+ filter_stream = stock_id;
+ } else if (strcmp(stock_id, GTK_STOCK_DELETE) == 0) {
+ delete = stock_id;
+ } else if (strcmp(stock_id, GTK_STOCK_COPY) == 0) {
+ copy = stock_id;
+ } else {
+ /* we don't know that button! */
+ g_assert_not_reached();
+ }
+ buttons++;
+ stock_id = va_arg(stock_id_list, gchar *);
+ }
+ va_end(stock_id_list);
+
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_widget_show(hbox);
+
+ button_hbox = gtk_hbutton_box_new();
+ gtk_box_pack_end(GTK_BOX(hbox), button_hbox, TRUE, TRUE, 0);
+ g_object_set_data(G_OBJECT(hbox), BUTTON_HBOX_KEY, button_hbox);
+ gtk_widget_show(button_hbox);
+ gtk_box_set_spacing(GTK_BOX(button_hbox), 5);
+
+ help_hbox = gtk_hbutton_box_new();
+ gtk_box_pack_end(GTK_BOX(hbox), help_hbox, FALSE, FALSE, 0);
+ gtk_widget_show(help_hbox);
+ gtk_box_set_spacing(GTK_BOX(help_hbox), 5);
+
+ if (buttons == 0) {
+ /* if no buttons wanted, simply do nothing */
+ return hbox;
+ }
+
+ if (buttons == 1) {
+ /* if only one button, simply put it in the middle (default) */
+ dlg_button_new(hbox, button_hbox, stock_id_first);
+ return hbox;
+ }
+
+ /* do we have a help button? -> special handling for it */
+ if (help) {
+ button = gtk_button_new_from_stock(help);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(button, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+#endif
+ g_object_set_data(G_OBJECT(hbox), help, button);
+ gtk_box_pack_start(GTK_BOX(help_hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show(button);
+ buttons--;
+ }
+
+ /* do we have a copy button? -> special handling for it */
+ if (copy) {
+ button = gtk_button_new_from_stock(copy);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(button, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+#endif
+ g_object_set_data(G_OBJECT(hbox), copy, button);
+ gtk_box_pack_start(GTK_BOX(help_hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show(button);
+ buttons--;
+ }
+
+#ifdef HAVE_GEOIP
+ /* do we have a map button? -> special handling for it */
+ if (map) {
+ button = gtk_button_new_from_stock(map);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(button, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+#endif
+ g_object_set_data(G_OBJECT(hbox), map, button);
+ gtk_box_pack_start(GTK_BOX(help_hbox), button, FALSE, FALSE, 0);
+ gtk_widget_show(button);
+ buttons--;
+ }
+#endif /* HAVE_GEOIP */
+
+ /* if more than one button, sort buttons from left to right */
+ /* (the whole button cluster will then be right aligned) */
+ gtk_button_box_set_layout (GTK_BUTTON_BOX(button_hbox), GTK_BUTTONBOX_END);
+
+#if !defined(_WIN32)
+ /* beware: sequence of buttons are important! */
+
+ /* XXX: this can be implemented more elegant of course, but it works as it should */
+ if (buttons == 2) {
+ if (ok && cancel) {
+ dlg_button_new(hbox, button_hbox, cancel);
+ dlg_button_new(hbox, button_hbox, ok);
+ return hbox;
+ }
+ if (print && cancel) {
+ dlg_button_new(hbox, button_hbox, cancel);
+ dlg_button_new(hbox, button_hbox, print);
+ return hbox;
+ }
+ if (find && cancel) {
+ dlg_button_new(hbox, button_hbox, cancel);
+ dlg_button_new(hbox, button_hbox, find);
+ return hbox;
+ }
+ if (jump && cancel) {
+ dlg_button_new(hbox, button_hbox, cancel);
+ dlg_button_new(hbox, button_hbox, jump);
+ return hbox;
+ }
+ if (save && cancel) {
+ dlg_button_new(hbox, button_hbox, cancel);
+ dlg_button_new(hbox, button_hbox, save);
+ return hbox;
+ }
+ if (ok && clear) {
+ dlg_button_new(hbox, button_hbox, clear);
+ dlg_button_new(hbox, button_hbox, ok);
+ return hbox;
+ }
+ if (save && close) {
+ dlg_button_new(hbox, button_hbox, close);
+ dlg_button_new(hbox, button_hbox, save);
+ return hbox;
+ }
+ if (create_stat && cancel) {
+ dlg_button_new(hbox, button_hbox, cancel);
+ dlg_button_new(hbox, button_hbox, create_stat);
+ return hbox;
+ }
+ if (cap_start && cancel) {
+ dlg_button_new(hbox, button_hbox, cancel);
+ dlg_button_new(hbox, button_hbox, cap_start);
+ return hbox;
+ }
+ if (cap_stop && cancel) {
+ dlg_button_new(hbox, button_hbox, cancel);
+ dlg_button_new(hbox, button_hbox, cap_stop);
+ return hbox;
+ }
+ if (delete && cancel) {
+ dlg_button_new(hbox, button_hbox, cancel);
+ dlg_button_new(hbox, button_hbox, delete);
+ return hbox;
+ }
+ }
+ if (buttons == 3) {
+ if (ok && save && close) {
+ dlg_button_new(hbox, button_hbox, save);
+ dlg_button_new(hbox, button_hbox, close);
+ dlg_button_new(hbox, button_hbox, ok);
+ return hbox;
+ }
+ if (ok && apply && cancel) {
+ dlg_button_new(hbox, button_hbox, apply);
+ dlg_button_new(hbox, button_hbox, cancel);
+ dlg_button_new(hbox, button_hbox, ok);
+ return hbox;
+ }
+ if (apply && save && close) {
+ dlg_button_new(hbox, button_hbox, save);
+ dlg_button_new(hbox, button_hbox, close);
+ dlg_button_new(hbox, button_hbox, apply);
+ return hbox;
+ }
+ if (yes && no && cancel) {
+ dlg_button_new(hbox, button_hbox, no);
+ dlg_button_new(hbox, button_hbox, cancel);
+ dlg_button_new(hbox, button_hbox, yes);
+ return hbox;
+ }
+ if (save && dont_save && cancel) {
+ dlg_button_new(hbox, button_hbox, dont_save);
+ dlg_button_new(hbox, button_hbox, cancel);
+ dlg_button_new(hbox, button_hbox, save);
+ return hbox;
+ }
+ }
+ if (buttons == 4) {
+ if (ok && apply && save && cancel) {
+ dlg_button_new(hbox, button_hbox, save);
+ dlg_button_new(hbox, button_hbox, apply);
+ dlg_button_new(hbox, button_hbox, cancel);
+ dlg_button_new(hbox, button_hbox, ok);
+ return hbox;
+ }
+ if (ok && apply && save && close) {
+ dlg_button_new(hbox, button_hbox, save);
+ dlg_button_new(hbox, button_hbox, apply);
+ dlg_button_new(hbox, button_hbox, close);
+ dlg_button_new(hbox, button_hbox, ok);
+ return hbox;
+ }
+ }
+#endif
+
+ /* beware: sequence of buttons is important! */
+ if (ok != NULL) dlg_button_new(hbox, button_hbox, ok);
+ if (delete != NULL) dlg_button_new(hbox, button_hbox, delete);
+ if (jump != NULL) dlg_button_new(hbox, button_hbox, jump);
+ if (find != NULL) dlg_button_new(hbox, button_hbox, find);
+ if (print != NULL) dlg_button_new(hbox, button_hbox, print);
+ if (create_stat != NULL) dlg_button_new(hbox, button_hbox, create_stat);
+ if (apply != NULL) dlg_button_new(hbox, button_hbox, apply);
+ if (yes != NULL) dlg_button_new(hbox, button_hbox, yes);
+ if (no != NULL) dlg_button_new(hbox, button_hbox, no);
+ if (save != NULL) dlg_button_new(hbox, button_hbox, save);
+ if (save_as != NULL) dlg_button_new(hbox, button_hbox, save_as);
+ if (save_all != NULL) dlg_button_new(hbox, button_hbox, save_all);
+ if (dont_save != NULL) dlg_button_new(hbox, button_hbox, dont_save);
+ if (cap_start != NULL) dlg_button_new(hbox, button_hbox, cap_start);
+ if (cap_stop != NULL) dlg_button_new(hbox, button_hbox, cap_stop);
+ if (cap_options != NULL) dlg_button_new(hbox, button_hbox, cap_options);
+#ifdef _WIN32
+ if (cap_details != NULL) dlg_button_new(hbox, button_hbox, cap_details);
+#endif
+ if (stop != NULL) dlg_button_new(hbox, button_hbox, stop);
+ if (clear != NULL) dlg_button_new(hbox, button_hbox, clear);
+ if (filter_stream!= NULL) dlg_button_new(hbox, button_hbox, filter_stream);
+ if (follow_stream != NULL) dlg_button_new(hbox, button_hbox, follow_stream);
+ if (close != NULL) dlg_button_new(hbox, button_hbox, close);
+ if (cancel != NULL) dlg_button_new(hbox, button_hbox, cancel);
+
+ return hbox;
+}
+
+
+/* Create a dialog box window that belongs to Wireshark's main window. */
+GtkWidget *
+dlg_window_new(const gchar *title)
+{
+ GtkWidget *win;
+
+ win = window_new(GTK_WINDOW_TOPLEVEL, title);
+
+ /*
+ * XXX - if we're running in the capture child process, we can't easily
+ * make this window transient for the main process's window. We just
+ * punt here.
+ *
+ * Perhaps the child process should only capture packets, write them to
+ * a file, and somehow notify the parent process and let *it* do all
+ * the GUI work. If we can do that efficiently (so that we don't drop
+ * more packets), perhaps we can also do so even when we're *not* doing
+ * an "Update list of packets in real time" capture. That'd let the
+ * child process run set-UID on platforms where you need that in order
+ * to capture, and might also simplify the job of having the GUI main
+ * loop wait both for user input and packet arrival.
+ */
+ /*
+ * On Windows, making the dialogs transient to top_level behaves strangely.
+ * It is not possible any more to bring the top level window to front easily.
+ * So we don't do this on Windows.
+ */
+#ifndef _WIN32
+ if (top_level) {
+ gtk_window_set_transient_for(GTK_WINDOW(win), GTK_WINDOW(top_level));
+ }
+#endif /*_WIN32*/
+
+ return win;
+}
+
+/* Create a configuration dialog box window that belongs to Wireshark's
+ * main window and add the name of the current profile name to it's title bar
+ */
+GtkWidget *
+dlg_conf_window_new(const gchar *title)
+{
+ const char *profile_name;
+ gchar *win_name;
+ GtkWidget *win;
+
+ /*
+ * Set window title to reflect which preferences profile we are
+ * working with.
+ */
+ profile_name = get_profile_name();
+
+ win_name = g_strdup_printf("%s - Profile: %s", title, profile_name);
+ win = dlg_window_new(win_name);
+
+ g_free(win_name);
+
+ return win;
+}
+
+/* Set the "activate" signal for a widget to call a routine to
+ activate the "OK" button for a dialog box.
+
+ XXX - there should be a way to specify that a GtkEntry widget
+ shouldn't itself handle the Return key, but should let it be
+ passed on to the parent, so that you don't have to do this
+ by hand for every GtkEntry widget in a dialog box, but, alas,
+ there isn't. (Does this problem exist for other widgets?
+ I.e., are there any others that seize the Return key? */
+void
+dlg_set_activate(GtkWidget *widget, GtkWidget *ok_button)
+{
+ g_signal_connect(widget, "activate", G_CALLBACK(dlg_activate), ok_button);
+}
+
+static void
+dlg_activate (GtkWidget *widget _U_, gpointer ok_button)
+{
+ gtk_widget_activate(GTK_WIDGET(ok_button));
+}
+
diff --git a/ui/gtk/dlg_utils.h b/ui/gtk/dlg_utils.h
new file mode 100644
index 0000000000..78e538895d
--- /dev/null
+++ b/ui/gtk/dlg_utils.h
@@ -0,0 +1,119 @@
+/* dlg_utils.h
+ * Declarations of utilities to use when constructing dialogs
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/** @defgroup dialog_group Dialogs
+ *
+ * Dialogs are specially created windows and are related to their parent windows (usually the main window).
+ * See: @ref howto_window_page for details.
+ *
+ * Normal dialogs are created using dlg_window_new().
+ *
+ * - "About" about_wireshark_cb()
+ * - "Capture Options" capture_prep_cb()
+ * - "Capture" capture_info_ui_create()
+ * - "Interface Options" ifopts_edit_cb()
+ * - "Coloring Rules" colorize_dialog_new()
+ * - "Edit Color Filter" edit_color_filter_dialog_new()
+ * - "Compute DCE-RPC SRT statistics" gtk_dcerpcstat_cb()
+ * - "Decode As: Show" decode_show_cb()
+ * - "Decode As" decode_as_cb()
+ * - "Filter Expression" dfilter_expr_dlg_new()
+ * - "Compute Fibre Channel Service Response Time statistics" gtk_fcstat_cb()
+ * - "Filter" (display and capture) filter_dialog_new()
+ * - "Find Packet" find_frame_cb()
+ * - "Follow TCP stream" follow_stream_cb()
+ * - "Go To Packet" goto_frame_cb()
+ * - "Compute LDAP Service Response Time statistics" gtk_ldapstat_cb()
+ * - "Preferences" tools_plugins_cmd_cb()
+ * - "Print" / "Export" open_print_dialog()
+ * - "Progress" create_progress_dlg()
+ * - "Enabled Protocols" proto_cb()
+ * - "Compute ONC-RPC SRT statistics" gtk_rpcstat_cb()
+ * - "RTP Streams" rtpstream_dlg_create()
+ * - "Simple Dialog" display_simple_dialog()
+ * - "Compute SMB SRT statistics" gtk_smbstat_cb()
+ * - "Compute ..." tap_param_dlg_cb()
+ * - "Tcp Graph" create_drawing_area()
+ * - "Tcp Graph Control" control_panel_create()
+ * - "Help for TCP graphing" callback_create_help()
+ * - "Tcp Graph Magnify" magnify_create()
+ */
+
+/** @file
+ * Utilities for dialog boxes. Depending on the window functions in
+ * gui_utils.h, see: @ref howto_window_page for details.
+ * @ingroup dialog_group
+ */
+
+#ifndef __DLG_UTILS_H__
+#define __DLG_UTILS_H__
+
+
+/** Create a dialog box window that belongs to Wireshark's main window.
+ * If you want to create a window, use window_new() instead.
+ * See window_new() for general window usage.
+ *
+ * @param title the title for the new dialog
+ * @return the newly created dialog
+ */
+extern GtkWidget *dlg_window_new(const gchar *title);
+
+/** Create a configuration dialog box window that belongs to Wireshark's
+ * main window and add the name of the current profile name to it's title bar
+ * If you want to create a window, use window_new() instead.
+ * See window_new() for general window usage.
+ *
+ * @param title the title for the new dialog
+ * @return the newly created dialog
+ */
+extern GtkWidget *dlg_conf_window_new(const gchar *title);
+
+/** Create a button row (with variable number of buttons) for a dialog.
+ * The button widgets will be available by g_object_get_data(dlg, stock_id) later.
+ *
+ * @param stock_id_first the first button (e.g. GTK_STOCK_OK)
+ * @param ... the next buttons, just like stock_id_first
+ * @return the new button row
+ * @todo move this to gui_utils.h
+ */
+extern GtkWidget *dlg_button_row_new(const gchar *stock_id_first, ...);
+
+/** Set the "activate" signal for a widget to call a routine to
+ * activate the "OK" button for a dialog box.
+ *
+ * @param widget a widget which should be connected (usually a GtkEntry)
+ * @param ok_button the button to be activated
+ * @todo move this to gui_utils.h
+ */
+extern void dlg_set_activate(GtkWidget *widget, GtkWidget *ok_button);
+
+/** Set the focus and default for the nth item in a button row.
+ *
+ * @param hbox A button row returned by dlg_button_row_new().
+ * @param focus_item The button to focus (0 is the first).
+ * @see dlg_button_row_new()
+ */
+void dlg_button_focus_nth(GtkWidget *hbox, gint focus_item);
+
+#endif
diff --git a/ui/gtk/doxygen.cfg.in b/ui/gtk/doxygen.cfg.in
new file mode 100644
index 0000000000..05b425271a
--- /dev/null
+++ b/ui/gtk/doxygen.cfg.in
@@ -0,0 +1,65 @@
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = Wireshark-GTK
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = @VERSION@
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY =
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = . ../progress_dlg.h ../simple_dialog.h
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
+# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc
+
+FILE_PATTERNS = *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+
+EXCLUDE_PATTERNS =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE = wireshark-gtk.chm
+
+@INCLUDE = ../doxygen_global.cfg
diff --git a/ui/gtk/drag_and_drop.c b/ui/gtk/drag_and_drop.c
new file mode 100644
index 0000000000..43e2fb664c
--- /dev/null
+++ b/ui/gtk/drag_and_drop.c
@@ -0,0 +1,417 @@
+/* drag_and_drop.c
+ * Drag and Drop
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <string.h>
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/prefs.h>
+
+#include "../util.h"
+#include "../file.h"
+#include "../simple_dialog.h"
+#ifdef HAVE_LIBPCAP
+#include "../capture.h"
+#endif
+
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/capture_file_dlg.h"
+#include "ui/gtk/drag_and_drop.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/menus.h"
+#ifdef HAVE_LIBPCAP
+#include "ui/gtk/capture_globals.h"
+#endif
+
+#include "ui/gtk/old-gtk-compat.h"
+
+#ifdef HAVE_GTKOSXAPPLICATION
+#include <igemacintegration/gtkosxapplication.h>
+#endif
+
+enum { DND_TARGET_STRING, DND_TARGET_ROOTWIN, DND_TARGET_URL };
+
+/* convert drag and drop URI to a local filename */
+static gchar *
+dnd_uri2filename(gchar *cf_name)
+{
+ gchar *src, *dest;
+ gint ret;
+ guint i;
+ gchar esc[3];
+
+
+ /* Remove URI header.
+ * we have to remove the prefix to get a valid filename. */
+#ifdef _WIN32
+ /*
+ * On win32 (at least WinXP), this prefix looks like (UNC):
+ * file:////servername/sharename/dir1/dir2/capture-file.cap
+ * or (local filename):
+ * file:///d:/dir1/dir2/capture-file.cap
+ */
+ if (strncmp("file:////", cf_name, 9) == 0) {
+ /* win32 UNC: now becoming: //servername/sharename/dir1/dir2/capture-file.cap */
+ cf_name += 7;
+ } else if (strncmp("file:///", cf_name, 8) == 0) {
+ /* win32 local: now becoming: d:/dir1/dir2/capture-file.cap */
+ cf_name += 8;
+ }
+#else
+ /*
+ * On UNIX (at least KDE 3.0 Konqueror), this prefix looks like:
+ * file:/dir1/dir2/capture-file.cap
+ *
+ * On UNIX (at least GNOME Nautilus 2.8.2), this prefix looks like:
+ * file:///dir1/dir2/capture-file.cap
+ */
+ if (strncmp("file:", cf_name, 5) == 0) {
+ /* now becoming: /dir1/dir2/capture-file.cap or ///dir1/dir2/capture-file.cap */
+ cf_name += 5;
+ /* shorten //////thing to /thing */
+ for(; cf_name[1] == '/'; ++cf_name);
+ }
+#endif
+
+ /*
+ * unescape the escaped URI characters (spaces, ...)
+ *
+ * we have to replace escaped chars to their equivalents,
+ * e.g. %20 (always a two digit hexstring) -> ' '
+ * the percent character '%' is escaped be a double one "%%"
+ *
+ * we do this conversation "in place" as the result is always
+ * equal or smaller in size.
+ */
+ src = cf_name;
+ dest = cf_name;
+ while (*src) {
+ if (*src == '%') {
+ src++;
+ if (*src == '%') {
+ /* this is an escaped '%' char (was: "%%") */
+ *dest = *src;
+ src++;
+ dest++;
+ } else {
+ /* convert escaped hexnumber to unscaped character */
+ esc[0] = src[0];
+ esc[1] = src[1];
+ esc[2] = '\0';
+ ret = sscanf(esc, "%x", &i);
+ if (ret == 1) {
+ src+=2;
+ *dest = (gchar) i;
+ dest++;
+ } else {
+ /* somethings wrong, just jump over that char
+ * this will result in a wrong string, but we might get
+ * user feedback and can fix it later ;-) */
+ src++;
+ }
+ }
+ } else {
+ *dest = *src;
+ src++;
+ dest++;
+ }
+ }
+ *dest = '\0';
+
+ return cf_name;
+}
+
+static void
+dnd_merge_files(int in_file_count, char **in_filenames)
+{
+ char *tmpname;
+ cf_status_t merge_status;
+ int err;
+
+ /* merge the files in chonological order */
+ tmpname = NULL;
+ merge_status = cf_merge_files(&tmpname, in_file_count, in_filenames,
+ WTAP_FILE_PCAP, FALSE);
+
+ if (merge_status != CF_OK) {
+ /* merge failed */
+ g_free(tmpname);
+ return;
+ }
+
+ cf_close(&cfile);
+
+ /* Try to open the merged capture file. */
+ if (cf_open(&cfile, tmpname, TRUE /* temporary file */, &err) != CF_OK) {
+ /* We couldn't open it; don't dismiss the open dialog box,
+ just leave it around so that the user can, after they
+ dismiss the alert box popped up for the open error,
+ try again. */
+ g_free(tmpname);
+ return;
+ }
+ g_free(tmpname);
+
+ switch (cf_read(&cfile, FALSE)) {
+
+ case CF_READ_OK:
+ case CF_READ_ERROR:
+ /* Just because we got an error, that doesn't mean we were unable
+ to read any of the file; we handle what we could get from the
+ file. */
+ break;
+
+ case CF_READ_ABORTED:
+ /* The user bailed out of re-reading the capture file; the
+ capture file has been closed - just free the capture file name
+ string and return (without changing the last containing
+ directory). */
+ return;
+ }
+}
+
+/* open/merge the dnd file */
+void
+dnd_open_file_cmd(gchar *cf_names_freeme)
+{
+ int err;
+ gchar *cf_name;
+ int in_files;
+ GString *dialog_text;
+ int files_work;
+ char **in_filenames;
+
+
+ /* DND_TARGET_URL on Win32:
+ * The cf_name_freeme is a single string, containing one or more URI's,
+ * seperated by CR/NL chars. The length of the whole field can be found
+ * in the selection_data->length field. If it contains one file, simply open it,
+ * If it contains more than one file, ask to merge these files. */
+
+ /* count the number of input files */
+ cf_name = cf_names_freeme;
+ for(in_files = 0; (cf_name = strstr(cf_name, "\r\n")) != NULL; ) {
+ cf_name += 2;
+ in_files++;
+ }
+
+ in_filenames = g_malloc(sizeof(char*) * in_files);
+
+ /* store the starts of the file entries in a gchar array */
+ cf_name = cf_names_freeme;
+ in_filenames[0] = cf_name;
+ for(files_work = 1; (cf_name = strstr(cf_name, "\r\n")) != NULL && files_work < in_files; ) {
+ cf_name += 2;
+ in_filenames[files_work] = cf_name;
+ files_work++;
+ }
+
+ /* replace trailing CR NL simply with zeroes (in place), so we get valid terminated strings */
+ cf_name = cf_names_freeme;
+ g_strdelimit(cf_name, "\r\n", '\0');
+
+ /* convert all filenames from URI to local filename (in place) */
+ for(files_work = 0; files_work < in_files; files_work++) {
+ in_filenames[files_work] = dnd_uri2filename(in_filenames[files_work]);
+ }
+
+ switch(in_files) {
+ case(0):
+ /* shouldn't happen */
+ break;
+ case(1):
+ /* open and read the capture file (this will close an existing file) */
+ if (cf_open(&cfile, in_filenames[0], FALSE, &err) == CF_OK) {
+ /* XXX - add this to the menu if the read fails? */
+ cf_read(&cfile, FALSE);
+ add_menu_recent_capture_file(in_filenames[0]);
+ } else {
+ /* the capture file couldn't be read (doesn't exist, file format unknown, ...) */
+ }
+ break;
+ default:
+ /* build and show the info dialog */
+ dialog_text = g_string_sized_new(200);
+ g_string_printf(dialog_text,
+ "%sMerging the following files:%s\n\n",
+ simple_dialog_primary_start(), simple_dialog_primary_end());
+ for(files_work = 0; files_work < in_files; files_work++) {
+ g_string_append(dialog_text, in_filenames[files_work]);
+ g_string_append(dialog_text, "\n");
+ }
+ g_string_append(dialog_text, "\nThe packets in these files will be merged chronologically into a new temporary file.");
+ simple_dialog(ESD_TYPE_CONFIRMATION,
+ ESD_BTN_OK, "%s",
+ dialog_text->str);
+ g_string_free(dialog_text, TRUE);
+
+ /* actually merge the files now */
+ dnd_merge_files(in_files, in_filenames);
+ }
+
+ g_free(in_filenames);
+ g_free(cf_names_freeme);
+}
+
+/* ask the user to save current unsaved file, before opening the dnd file */
+static void
+dnd_save_file_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
+{
+ switch(btn) {
+ case(ESD_BTN_SAVE):
+ /* save file first */
+ file_save_as_cmd(after_save_open_dnd_file, data, FALSE);
+ break;
+ case(ESD_BTN_DONT_SAVE):
+ cf_close(&cfile);
+ dnd_open_file_cmd(data);
+ break;
+ case(ESD_BTN_CANCEL):
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+
+/* we have received some drag and drop data */
+/* (as we only registered to "text/uri-list", we will only get a file list here) */
+static void
+dnd_data_received(GtkWidget *widget _U_, GdkDragContext *dc _U_, gint x _U_, gint y _U_,
+ GtkSelectionData *selection_data, guint info, guint t _U_, gpointer data _U_)
+{
+ gpointer dialog;
+ gchar *cf_names_freeme;
+ const guchar *sel_data_data;
+ gint sel_data_len;
+
+ if (info == DND_TARGET_URL) {
+ /* Usually we block incoming events by disabling the corresponding menu/toolbar items.
+ * This is the only place where an incoming event won't be blocked in such a way,
+ * so we have to take care of NOT loading a new file while a different process
+ * (e.g. capture/load/...) is still in progress. */
+
+#ifdef HAVE_LIBPCAP
+ /* if a capture is running, do nothing but warn the user */
+ if((global_capture_opts.state != CAPTURE_STOPPED)) {
+ simple_dialog(ESD_TYPE_CONFIRMATION,
+ ESD_BTN_OK,
+ "%sDrag and Drop currently not possible!%s\n\n"
+ "Dropping a file isn't possible while a capture is in progress.",
+ simple_dialog_primary_start(), simple_dialog_primary_end());
+ return;
+ }
+#endif
+
+ /* if another file read is still in progress, do nothing but warn the user */
+ if(cfile.state == FILE_READ_IN_PROGRESS) {
+ simple_dialog(ESD_TYPE_CONFIRMATION,
+ ESD_BTN_OK,
+ "%sDrag and Drop currently not possible!%s\n\n"
+ "Dropping a file isn't possible while loading another capture file.",
+ simple_dialog_primary_start(), simple_dialog_primary_end());
+ return;
+ }
+
+ /* the selection_data will soon be gone, make a copy first */
+ /* the data string is not zero terminated -> make a zero terminated "copy" of it */
+ sel_data_len = gtk_selection_data_get_length(selection_data);
+ sel_data_data = gtk_selection_data_get_data(selection_data);
+ cf_names_freeme = g_malloc(sel_data_len + 1);
+ memcpy(cf_names_freeme, sel_data_data, sel_data_len);
+ cf_names_freeme[sel_data_len] = '\0';
+
+ /* ask the user to save it's current capture file first */
+ if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
+ /* user didn't saved his current file, ask him */
+ dialog = simple_dialog(ESD_TYPE_CONFIRMATION,
+ ESD_BTNS_SAVE_DONTSAVE_CANCEL,
+ "%sSave capture file before opening a new one?%s\n\n"
+ "If you open a new capture file without saving, your current capture data will be discarded.",
+ simple_dialog_primary_start(), simple_dialog_primary_end());
+ simple_dialog_set_cb(dialog, dnd_save_file_answered_cb, cf_names_freeme );
+ } else {
+ /* unchanged file */
+ dnd_open_file_cmd( cf_names_freeme );
+ }
+ }
+}
+
+#ifdef HAVE_GTKOSXAPPLICATION
+gboolean
+gtk_osx_openFile (GtkOSXApplication *app _U_, gchar *path, gpointer user_data _U_)
+{
+ GtkSelectionData selection_data;
+ gchar *selection_path;
+ int length = strlen(path) + 3;
+
+ selection_path = g_malloc(length + 3);
+ memcpy(selection_path, path, length);
+
+ selection_path[length] = '\r';
+ selection_path[length + 1] = '\n';
+ selection_path[length + 2] = '\0';
+
+ gtk_selection_data_set_text(&selection_data, selection_path, length);
+ dnd_data_received(NULL, NULL, 0, 0, &selection_data, DND_TARGET_URL, 0, 0);
+
+ g_free(selection_path);
+
+ return TRUE;
+}
+#endif
+
+/* init the drag and drop functionality */
+void
+dnd_init(GtkWidget *w)
+{
+ /* we are only interested in the URI list containing filenames */
+ static GtkTargetEntry target_entry[] = {
+ /*{"STRING", 0, DND_TARGET_STRING},*/
+ /*{"text/plain", 0, DND_TARGET_STRING},*/
+ {"text/uri-list", 0, DND_TARGET_URL}
+ };
+
+ /* set this window as a dnd destination */
+ gtk_drag_dest_set(
+ w, GTK_DEST_DEFAULT_ALL, target_entry,
+ sizeof(target_entry) / sizeof(GtkTargetEntry),
+ (GdkDragAction)(GDK_ACTION_MOVE | GDK_ACTION_COPY) );
+
+ /* get notified, if some dnd coming in */
+ g_signal_connect(w, "drag_data_received", G_CALLBACK(dnd_data_received), NULL);
+#ifdef HAVE_GTKOSXAPPLICATION
+ g_signal_connect(g_object_new(GTK_TYPE_OSX_APPLICATION, NULL), "NSApplicationOpenFile", G_CALLBACK(gtk_osx_openFile), NULL);
+#endif
+}
+
+
diff --git a/ui/gtk/drag_and_drop.h b/ui/gtk/drag_and_drop.h
new file mode 100644
index 0000000000..0ea47eb153
--- /dev/null
+++ b/ui/gtk/drag_and_drop.h
@@ -0,0 +1,40 @@
+/* drag_and_drop.h
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __DRAG_AND_DROP_H__
+#define __DRAG_AND_DROP_H__
+
+
+/** Init the drag-n-drop functionality.
+ *
+ * @param w the target widget for this dnd operations
+ */
+extern void dnd_init(GtkWidget *w);
+
+/** Open a new file coming from drag and drop.
+ * @param cf_names_freeme the selection data reported from GTK
+ */
+extern void dnd_open_file_cmd(gchar *cf_names_freeme);
+
+
+#endif /* __DRAG_AND_DROP_H__ */
diff --git a/ui/gtk/expert_comp_dlg.c b/ui/gtk/expert_comp_dlg.c
new file mode 100644
index 0000000000..85fb78bcc4
--- /dev/null
+++ b/ui/gtk/expert_comp_dlg.c
@@ -0,0 +1,811 @@
+/* expert_comp_dlg.c
+ * expert_comp_dlg 2005 Greg Morris
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/prefs.h>
+#include <epan/tap.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/prefs.h>
+
+#include "../simple_dialog.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/expert_comp_table.h"
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/expert_comp_dlg.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/expert_indicators.h"
+#include "ui/gtk/main_proto_draw.h"
+#include "ui/gtk/old-gtk-compat.h"
+
+enum
+{
+ NO_COLUMN,
+ SEVERITY_COLUMN,
+ GROUP_COLUMN,
+ PROTOCOL_COLUMN,
+ SUMMARY_COLUMN,
+ FOREGROUND_COLOR_COL,
+ BACKGROUND_COLOR_COL,
+ N_COLUMNS
+};
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _expert_comp_dlg_t {
+ GtkWidget *win;
+ GtkWidget *chat_label;
+ GtkWidget *note_label;
+ GtkWidget *warn_label;
+ GtkWidget *error_label;
+ GtkWidget *all_label;
+ error_equiv_table chat_table;
+ error_equiv_table note_table;
+ error_equiv_table warn_table;
+ error_equiv_table error_table;
+ guint32 disp_events;
+ guint32 chat_events;
+ guint32 note_events;
+ guint32 warn_events;
+ guint32 error_events;
+} expert_comp_dlg_t;
+
+struct expert_tapdata_s {
+ GtkWidget *win;
+ GtkWidget *scrolled_window;
+ GtkTreeView *tree_view;
+ GtkWidget *label;
+ guint32 disp_events;
+ guint32 chat_events;
+ guint32 note_events;
+ guint32 warn_events;
+ guint32 error_events;
+ int severity_report_level;
+
+ GArray *ei_array; /* expert info items */
+ guint first;
+ guint last;
+ GStringChunk* text; /* summary text */
+};
+
+static GtkWidget *expert_comp_dlg_w = NULL;
+
+static void
+select_row_cb(GtkTreeSelection *selection, gpointer *user_data _U_)
+{
+ /*guint num = GPOINTER_TO_UINT(gtk_clist_get_row_data(clist, row));*/
+
+ /*cf_goto_frame(&cfile, num);*/
+
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ guint fnumber;
+
+ if (selection==NULL)
+ return;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)){
+ gtk_tree_model_get (model, &iter, NO_COLUMN, &fnumber, -1);
+ cf_goto_frame(&cfile, fnumber);
+ }
+
+}
+
+/* reset of display only, e.g. for filtering */
+static void expert_dlg_display_reset(expert_tapdata_t * etd)
+{
+ etd->disp_events = 0;
+ gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(etd->tree_view))));
+
+ gtk_window_set_title(GTK_WINDOW(etd->win), "Wireshark: ? Expert Infos");
+ if(etd->label) {
+ gtk_label_set_text(GTK_LABEL(etd->label), "Please wait ...");
+ }
+}
+
+/* complete reset, e.g. capture file closed */
+static void
+expert_dlg_reset(void *tapdata)
+{
+ expert_tapdata_t * etd = tapdata;
+
+ etd->chat_events = 0;
+ etd->note_events = 0;
+ etd->warn_events = 0;
+ etd->error_events = 0;
+ etd->last = 0;
+ etd->first = 0;
+ /* g_string_chunk_clear() is introduced in glib 2.14 */
+ g_string_chunk_free(etd->text);
+ etd->text = g_string_chunk_new(100);
+ g_array_set_size(etd->ei_array, 0);
+
+ expert_dlg_display_reset(etd);
+}
+
+static int
+expert_dlg_packet(void *tapdata, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *pointer)
+{
+ expert_info_t *ei;
+ expert_tapdata_t *etd = tapdata;
+
+ g_array_append_val(etd->ei_array, *(expert_info_t *)pointer);
+ etd->last = etd->ei_array->len;
+ ei = &g_array_index(etd->ei_array, expert_info_t, etd->last -1); /* ugly */
+ ei->protocol = g_string_chunk_insert_const(etd->text, ei->protocol);
+ ei->summary = g_string_chunk_insert_const(etd->text, ei->summary);
+
+ switch(ei->severity) {
+ case(PI_CHAT):
+ etd->chat_events++;
+ break;
+ case(PI_NOTE):
+ etd->note_events++;
+ break;
+ case(PI_WARN):
+ etd->warn_events++;
+ break;
+ case(PI_ERROR):
+ etd->error_events++;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ if(ei->severity < etd->severity_report_level) {
+ return 0; /* draw not required */
+ } else {
+ return 1; /* draw required */
+ }
+}
+static void
+error_set_title(expert_comp_dlg_t *ss)
+{
+ char *title;
+
+ title = g_strdup_printf("Expert Info: %s",
+ cf_get_display_name(&cfile));
+ gtk_window_set_title(GTK_WINDOW(ss->win), title);
+ g_free(title);
+}
+
+static void
+error_reset(void *pss)
+{
+ expert_comp_dlg_t *ss=(expert_comp_dlg_t *)pss;
+ gchar *buf;
+
+ ss->error_events = 0;
+ ss->warn_events = 0;
+ ss->note_events = 0;
+ ss->chat_events = 0;
+ ss->disp_events = 0;
+
+ reset_error_table_data(&ss->error_table);
+ buf = g_strdup_printf("Errors: %u (0)", ss->error_table.num_procs);
+ gtk_label_set_text( GTK_LABEL(ss->error_label), buf);
+ g_free(buf);
+
+ reset_error_table_data(&ss->warn_table);
+ buf = g_strdup_printf("Warnings: %u (0)", ss->warn_table.num_procs);
+ gtk_label_set_text( GTK_LABEL(ss->warn_label), buf);
+ g_free(buf);
+
+ reset_error_table_data(&ss->note_table);
+ buf = g_strdup_printf("Notes: %u (0)", ss->note_table.num_procs);
+ gtk_label_set_text( GTK_LABEL(ss->note_label), buf);
+ g_free(buf);
+
+ reset_error_table_data(&ss->chat_table);
+ buf = g_strdup_printf("Chats: %u (0)", ss->chat_table.num_procs);
+ gtk_label_set_text( GTK_LABEL(ss->chat_label), buf);
+ g_free(buf);
+
+ gtk_label_set_text( GTK_LABEL(ss->all_label), "Details: 0");
+ error_set_title(ss);
+}
+
+static gboolean
+error_packet(void *pss, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *prv)
+{
+ expert_comp_dlg_t *ss=(expert_comp_dlg_t *)pss;
+ const expert_info_t *error_pkt=prv;
+
+ /* if return value is 0 then no error */
+ if(error_pkt==NULL){
+ return FALSE;
+ }
+
+ switch (error_pkt->severity) {
+ case PI_ERROR:
+ ss->disp_events++;
+ ss->error_events++;
+ init_error_table_row(&ss->error_table, error_pkt);
+ break;
+ case PI_WARN:
+ ss->disp_events++;
+ ss->warn_events++;
+ init_error_table_row(&ss->warn_table, error_pkt);
+ break;
+ case PI_NOTE:
+ ss->disp_events++;
+ ss->note_events++;
+ init_error_table_row(&ss->note_table, error_pkt);
+ break;
+ case PI_CHAT:
+ ss->disp_events++;
+ ss->chat_events++;
+ init_error_table_row(&ss->chat_table, error_pkt);
+ break;
+ default:
+ return FALSE; /* Don't draw */
+ }
+ return TRUE; /* Draw */
+}
+
+static void
+expert_comp_draw(void *data)
+{
+ gchar *buf = NULL;
+
+ expert_comp_dlg_t *ss=(expert_comp_dlg_t *)data;
+
+ buf = g_strdup_printf("Errors: %u (%u)", ss->error_table.num_procs, ss->error_events);
+ gtk_label_set_text( GTK_LABEL(ss->error_label), buf);
+ g_free(buf);
+
+ buf = g_strdup_printf("Warnings: %u (%u)", ss->warn_table.num_procs, ss->warn_events);
+ gtk_label_set_text( GTK_LABEL(ss->warn_label), buf);
+ g_free(buf);
+
+ buf = g_strdup_printf("Notes: %u (%u)", ss->note_table.num_procs, ss->note_events);
+ gtk_label_set_text( GTK_LABEL(ss->note_label), buf);
+ g_free(buf);
+
+ buf = g_strdup_printf("Chats: %u (%u)", ss->chat_table.num_procs, ss->chat_events);
+ gtk_label_set_text( GTK_LABEL(ss->chat_label), buf);
+ g_free(buf);
+
+ buf = g_strdup_printf("Details: %u", ss->disp_events);
+ gtk_label_set_text( GTK_LABEL(ss->all_label), buf);
+ g_free(buf);
+
+}
+
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ expert_comp_dlg_t *ss=(expert_comp_dlg_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(ss);
+ unprotect_thread_critical_region();
+
+ if (expert_comp_dlg_w != NULL) {
+ window_destroy(expert_comp_dlg_w);
+ expert_comp_dlg_w = NULL;
+ }
+
+ free_error_table_data(&ss->error_table);
+ free_error_table_data(&ss->warn_table);
+ free_error_table_data(&ss->note_table);
+ free_error_table_data(&ss->chat_table);
+ g_free(ss);
+
+}
+
+static void
+expert_dlg_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ expert_tapdata_t *etd=(expert_tapdata_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(etd);
+ unprotect_thread_critical_region();
+
+ /*free_srt_table_data(&etd->afp_srt_table);*/
+ g_array_free(etd->ei_array, TRUE);
+ g_string_chunk_free(etd->text);
+ g_free(etd);
+}
+
+static expert_tapdata_t * expert_dlg_new_table(void)
+{
+ expert_tapdata_t * etd;
+ etd=g_malloc0(sizeof(expert_tapdata_t));
+
+ etd->ei_array = g_array_sized_new(FALSE, FALSE, sizeof(expert_info_t), 1000);
+ etd->text = g_string_chunk_new(100);
+ etd->severity_report_level = PI_CHAT;
+ return etd;
+}
+
+static void
+expert_dlg_init_table(expert_tapdata_t * etd, GtkWidget *vbox)
+{
+ GtkListStore *store;
+ GtkWidget *tree;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSortable *sortable;
+ GtkTreeSelection *selection;
+
+ /* Create the store */
+ store = gtk_list_store_new(N_COLUMNS, /* Total number of columns */
+ G_TYPE_UINT, /* No */
+ G_TYPE_POINTER, /* Severity */
+ G_TYPE_POINTER, /* Group */
+ G_TYPE_POINTER, /* Protocol */
+ G_TYPE_POINTER, /* Summary */
+ G_TYPE_STRING, /* forground */
+ G_TYPE_STRING); /* Background */
+
+ /* Create a view */
+ tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
+ etd->tree_view = GTK_TREE_VIEW(tree);
+ sortable = GTK_TREE_SORTABLE(store);
+
+ /* Speed up the list display */
+ gtk_tree_view_set_fixed_height_mode(etd->tree_view, TRUE);
+
+ /* Setup the sortable columns */
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW (tree), FALSE);
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref (G_OBJECT (store));
+
+ /* Let the font be the default one to have the same look as the rest of the tabs
+ * Bug https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=4388
+ * gtk_widget_modify_font(GTK_WIDGET (etd->tree_view), user_font_get_regular());
+ */
+
+ /* Create a cell renderer */
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set(renderer, "ypad", 0, NULL);
+ g_object_set(renderer, "xalign", 1.0, NULL);
+
+ /* Create the first column, associating the "text" attribute of the
+ * cell_renderer to the first column of the model */
+ /* No */
+ column = gtk_tree_view_column_new_with_attributes ("No", renderer,
+ "text", NO_COLUMN,
+ "foreground", FOREGROUND_COLOR_COL,
+ "background", BACKGROUND_COLOR_COL,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, NO_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_append_column (etd->tree_view, column);
+
+ /* Severity */
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set(renderer, "ypad", 0, NULL);
+
+ column = gtk_tree_view_column_new_with_attributes ("Severity", renderer,
+ "foreground", FOREGROUND_COLOR_COL,
+ "background", BACKGROUND_COLOR_COL,
+ NULL);
+
+ gtk_tree_view_column_set_cell_data_func(column, renderer, str_ptr_data_func,
+ GINT_TO_POINTER(SEVERITY_COLUMN), NULL);
+
+ gtk_tree_sortable_set_sort_func(sortable, SEVERITY_COLUMN, str_ptr_sort_func,
+ GINT_TO_POINTER(SEVERITY_COLUMN), NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, SEVERITY_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (etd->tree_view, column);
+
+ /* Group */
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set(renderer, "ypad", 0, NULL);
+ column = gtk_tree_view_column_new_with_attributes ("Group", renderer,
+ "foreground", FOREGROUND_COLOR_COL,
+ "background", BACKGROUND_COLOR_COL,
+ NULL);
+
+ gtk_tree_view_column_set_cell_data_func(column, renderer, str_ptr_data_func,
+ GINT_TO_POINTER(GROUP_COLUMN), NULL);
+
+ gtk_tree_sortable_set_sort_func(sortable, GROUP_COLUMN, str_ptr_sort_func,
+ GINT_TO_POINTER(GROUP_COLUMN), NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, GROUP_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (etd->tree_view, column);
+
+ /* Protocol. */
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set(renderer, "ypad", 0, NULL);
+ column = gtk_tree_view_column_new_with_attributes ("Protocol", renderer,
+ "foreground", FOREGROUND_COLOR_COL,
+ "background", BACKGROUND_COLOR_COL,
+ NULL);
+ gtk_tree_view_column_set_cell_data_func(column, renderer, str_ptr_data_func,
+ GINT_TO_POINTER(PROTOCOL_COLUMN), NULL);
+
+ gtk_tree_sortable_set_sort_func(sortable, PROTOCOL_COLUMN, str_ptr_sort_func,
+ GINT_TO_POINTER(PROTOCOL_COLUMN), NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, PROTOCOL_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_append_column (etd->tree_view, column);
+
+ /* Summary. */
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set(renderer, "ypad", 0, NULL);
+ column = gtk_tree_view_column_new_with_attributes ("Summary", renderer,
+ "foreground", FOREGROUND_COLOR_COL,
+ "background", BACKGROUND_COLOR_COL,
+ NULL);
+ gtk_tree_view_column_set_cell_data_func(column, renderer, str_ptr_data_func,
+ GINT_TO_POINTER(SUMMARY_COLUMN), NULL);
+
+ gtk_tree_sortable_set_sort_func(sortable, SUMMARY_COLUMN, str_ptr_sort_func,
+ GINT_TO_POINTER(SUMMARY_COLUMN), NULL);
+
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 90);
+ gtk_tree_view_column_set_sort_column_id(column, SUMMARY_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_append_column (etd->tree_view, column);
+
+ gtk_tree_view_set_search_column (etd->tree_view, SUMMARY_COLUMN); /* Allow searching the summary */
+ gtk_tree_view_set_reorderable (etd->tree_view, TRUE); /* Allow user to reorder data with drag n drop */
+
+ /* Now enable the sorting of each column */
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(etd->tree_view), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(etd->tree_view), TRUE);
+
+ /* Setup the selection handler */
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(etd->tree_view));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+
+ g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
+ G_CALLBACK (select_row_cb),
+ NULL);
+
+ etd->scrolled_window=scrolled_window_new(NULL, NULL);
+ gtk_container_add(GTK_CONTAINER(etd->scrolled_window), GTK_WIDGET (etd->tree_view));
+
+ gtk_box_pack_start(GTK_BOX(vbox), etd->scrolled_window, TRUE, TRUE, 0);
+}
+
+static void
+expert_dlg_draw(void *data)
+{
+ expert_tapdata_t *etd = data;
+ expert_info_t *ei;
+ gchar *title;
+ const char *entries[2]; /**< column entries */
+ GtkListStore *list_store;
+ GtkTreeIter iter;
+ gchar *color_str = NULL;
+ guint packet_no = 0;
+ const gchar *group_str;
+ const gchar *severity_str;
+
+
+ if(etd->label) {
+ if(etd->last - etd->first) {
+ title = g_strdup_printf("Adding: %u new messages",etd->last - etd->first);
+ gtk_label_set_text(GTK_LABEL(etd->label), title);
+ g_free(title);
+ }
+ }
+
+ /* append new events (remove from new list, append to displayed list and clist) */
+ while(etd->first < etd->last){
+ ei = &g_array_index(etd->ei_array, expert_info_t, etd->first);
+ etd->first++;
+
+ if(ei->severity < etd->severity_report_level) {
+ continue;
+ }
+ etd->disp_events++;
+
+ /* packet number */
+ if(ei->packet_num) {
+ packet_no = ei->packet_num;
+ }
+
+ /* match_strval return a static string or NULL
+ severity */
+ severity_str = match_strval(ei->severity, expert_severity_vals);
+ /* group */
+ group_str = match_strval(ei->group, expert_group_vals);
+
+ /* protocol */
+ if(ei->protocol) {
+ entries[0] = ei->protocol;
+ } else {
+ entries[0] = "-";
+ }
+
+ /* summary */
+ entries[1] = ei->summary;
+
+ /* set rows background color depending on severity */
+ switch(ei->severity) {
+ case(PI_CHAT):
+ color_str = expert_color_chat_str;
+ break;
+ case(PI_NOTE):
+ color_str = expert_color_note_str;
+ break;
+ case(PI_WARN):
+ color_str = expert_color_warn_str;
+ break;
+ case(PI_ERROR):
+ color_str = expert_color_error_str;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ list_store = GTK_LIST_STORE(gtk_tree_view_get_model(etd->tree_view)); /* Get store */
+
+ /* Creates a new row at position. iter will be changed to point to this new row.
+ * If position is larger than the number of rows on the list, then the new row will be appended to the list.
+ * The row will be filled with the values given to this function.
+ * :
+ * should generally be preferred when inserting rows in a sorted list store.
+ */
+ gtk_list_store_insert_with_values( list_store , &iter, G_MAXINT,
+ NO_COLUMN, packet_no,
+ SEVERITY_COLUMN, severity_str,
+ GROUP_COLUMN, group_str,
+ PROTOCOL_COLUMN, entries[0],
+ SUMMARY_COLUMN, entries[1],
+ FOREGROUND_COLOR_COL, expert_color_foreground_str,
+ BACKGROUND_COLOR_COL, color_str,
+ -1);
+ }
+
+ if(etd->label) {
+ title = g_strdup_printf("Errors: %u Warnings: %u Notes: %u Chats: %u",
+ etd->error_events, etd->warn_events,
+ etd->note_events, etd->chat_events);
+ gtk_label_set_text(GTK_LABEL(etd->label), title);
+ g_free(title);
+ }
+
+ title = g_strdup_printf("Wireshark: %u Expert Info%s",
+ etd->disp_events,
+ plurality(etd->disp_events, "", "s"));
+ gtk_window_set_title(GTK_WINDOW(etd->win), title);
+ g_free(title);
+}
+
+static void
+expert_comp_init(const char *optarg _U_, void* userdata _U_)
+{
+ expert_comp_dlg_t *ss;
+ const char *filter=NULL;
+ GString *error_string;
+ GtkWidget *temp_page;
+ GtkWidget *main_nb;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *image;
+ GtkWidget *bbox;
+ GtkWidget *close_bt;
+ GtkWidget *help_bt;
+ expert_tapdata_t *etd;
+
+ ss=g_malloc(sizeof(expert_comp_dlg_t));
+
+ ss->disp_events = 0;
+ ss->chat_events = 0;
+ ss->note_events = 0;
+ ss->warn_events = 0;
+ ss->error_events = 0;
+
+ expert_comp_dlg_w = ss->win=dlg_window_new("err"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(ss->win), TRUE);
+ gtk_window_set_default_size(GTK_WINDOW(ss->win), 700, 300);
+
+ error_set_title(ss);
+
+ vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(ss->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ main_nb = gtk_notebook_new();
+ gtk_box_pack_start(GTK_BOX(vbox), main_nb, TRUE, TRUE, 0);
+
+ /* We must display TOP LEVEL Widget before calling init_table() */
+ gtk_widget_show_all(ss->win);
+
+ /* Errors */
+ temp_page = gtk_vbox_new(FALSE, 6);
+ ss->error_label = gtk_label_new("Errors: 0/y");
+ gtk_widget_show(ss->error_label);
+ hbox = gtk_hbox_new(FALSE, 3);
+ if ( prefs.gui_expert_composite_eyecandy ) {
+ image = pixbuf_to_widget(expert_error_pb_data);
+ gtk_widget_show(image);
+ gtk_container_add(GTK_CONTAINER(hbox), image);
+ }
+ gtk_container_add(GTK_CONTAINER(hbox), ss->error_label);
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, hbox);
+ init_error_table(&ss->error_table, 0, temp_page);
+
+ /* Warnings */
+ temp_page = gtk_vbox_new(FALSE, 6);
+ ss->warn_label = gtk_label_new("Warnings: 0/y");
+ gtk_widget_show(ss->warn_label);
+ hbox = gtk_hbox_new(FALSE, 3);
+ if ( prefs.gui_expert_composite_eyecandy ) {
+ image = pixbuf_to_widget(expert_warn_pb_data);
+ gtk_widget_show(image);
+ gtk_container_add(GTK_CONTAINER(hbox), image);
+ }
+ gtk_container_add(GTK_CONTAINER(hbox), ss->warn_label);
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, hbox);
+ init_error_table(&ss->warn_table, 0, temp_page);
+
+ /* Notes */
+ temp_page = gtk_vbox_new(FALSE, 6);
+ ss->note_label = gtk_label_new("Notes: 0/y");
+ gtk_widget_show(ss->note_label);
+ hbox = gtk_hbox_new(FALSE, 3);
+ if ( prefs.gui_expert_composite_eyecandy ) {
+ image = pixbuf_to_widget(expert_note_pb_data);
+ gtk_widget_show(image);
+ gtk_container_add(GTK_CONTAINER(hbox), image);
+ }
+ gtk_container_add(GTK_CONTAINER(hbox), ss->note_label);
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, hbox);
+ init_error_table(&ss->note_table, 0, temp_page);
+
+ /* Chat */
+ temp_page = gtk_vbox_new(FALSE, 6);
+ ss->chat_label = gtk_label_new("Chats: 0/y");
+ gtk_widget_show(ss->chat_label);
+ hbox = gtk_hbox_new(FALSE, 3);
+ if ( prefs.gui_expert_composite_eyecandy ) {
+ image = pixbuf_to_widget(expert_chat_pb_data);
+ gtk_widget_show(image);
+ gtk_container_add(GTK_CONTAINER(hbox), image);
+ }
+ gtk_container_add(GTK_CONTAINER(hbox), ss->chat_label);
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, hbox);
+ init_error_table(&ss->chat_table, 0, temp_page);
+
+ /* Details */
+ temp_page = gtk_vbox_new(FALSE, 6);
+ ss->all_label = gtk_label_new("Details: 0");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, ss->all_label);
+
+ etd = expert_dlg_new_table();
+ etd->label=gtk_label_new("Please wait ...");
+ gtk_misc_set_alignment(GTK_MISC(etd->label), 0.0f, 0.5f);
+
+ etd->win=ss->win;
+ expert_dlg_init_table(etd, temp_page);
+
+ /* Add tap listener functions for expert details, From expert_dlg.c*/
+ error_string=register_tap_listener("expert", etd, NULL /* fstring */,
+ TL_REQUIRES_PROTO_TREE,
+ expert_dlg_reset,
+ expert_dlg_packet,
+ expert_dlg_draw);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(etd);
+ return;
+ }
+
+ g_signal_connect(etd->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(etd->win, "destroy", G_CALLBACK(expert_dlg_destroy_cb), etd);
+
+ /* Register the tap listener */
+
+ error_string=register_tap_listener("expert", ss, filter, 0, error_reset, error_packet, expert_comp_draw);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(ss);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(ss->win, close_bt, window_cancel_button_cb);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_EXPERT_INFO_DIALOG);
+ gtk_widget_set_tooltip_text (help_bt, "Show topic specific help");
+
+ g_signal_connect(ss->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(ss->win, "destroy", G_CALLBACK(win_destroy_cb), ss);
+
+ gtk_widget_show_all(ss->win);
+ window_present(ss->win);
+
+ /*
+ * At least at present, the only information the tap listener appears
+ * to care about is available regardless of whether the protocol tree
+ * is being built, so we don't appear to need to have the protocol
+ * tree built.
+ *
+ * This means we can use cf_retap_packets(), even though it will only
+ * build the protocol tree if at least one tap has a filter in place.
+ * cf_retap_packets() is faster than cf_redissect_packets(), as it
+ * assumes we didn't change anything that would cause any packets to
+ * dissect differently, and thus doesn't redo the packet display.
+ */
+ cf_retap_packets(&cfile);
+
+ /* This will bring up the progress bar
+ * Put our window back in front
+ */
+ gdk_window_raise(gtk_widget_get_window(ss->win));
+ /* Set the lable text */
+ expert_comp_draw(ss);
+}
+
+void
+expert_comp_dlg_launch(void)
+{
+ if (expert_comp_dlg_w) {
+ reactivate_window(expert_comp_dlg_w);
+ } else {
+ expert_comp_init("", NULL);
+ }
+}
+
+void
+register_tap_listener_expert_comp(void)
+{
+ register_stat_cmd_arg("expert_comp", expert_comp_init,NULL);
+}
+
diff --git a/ui/gtk/expert_comp_dlg.h b/ui/gtk/expert_comp_dlg.h
new file mode 100644
index 0000000000..2e54607bd0
--- /dev/null
+++ b/ui/gtk/expert_comp_dlg.h
@@ -0,0 +1,30 @@
+/* expert_comp_dlg.h
+ * expert_comp_dlg 2005 Greg Morris
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EXPERT_COMP_DLG_H__
+#define __EXPERT_COMP_DLG_H__
+
+void expert_comp_dlg_launch(void);
+
+#endif /* __EXPERT_COMP_DLG_H__ */
diff --git a/ui/gtk/expert_comp_table.c b/ui/gtk/expert_comp_table.c
new file mode 100644
index 0000000000..153ff83b6c
--- /dev/null
+++ b/ui/gtk/expert_comp_table.c
@@ -0,0 +1,854 @@
+/* expert_comp_table.c
+ * expert_comp_table 2005 Greg Morris
+ * Portions copied from service_response_time_table.c by Ronnie Sahlberg
+ * Helper routines common to all composite expert statistics
+ * tap.
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/packet_info.h"
+#include "epan/strutil.h"
+
+#include <epan/expert.h>
+
+#include "../simple_dialog.h"
+
+#include "ui/gtk/expert_comp_table.h"
+#include "ui/gtk/filter_utils.h"
+#include "ui/gtk/find_dlg.h"
+#include "ui/gtk/color_dlg.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/webbrowser.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/utf8_entities.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+const char *packet = "Packet:";
+
+enum
+{
+ GROUP_COLUMN,
+ PROTOCOL_COLUMN,
+ SUMMARY_COLUMN,
+ COUNT_COLUMN,
+ N_COLUMNS
+};
+
+static void
+proto_data_func (GtkTreeViewColumn *column _U_,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gchar *str = NULL;
+ gchar *grp = NULL; /* type pointer, don't free */
+
+ /* The col to get data from is in userdata */
+ gint data_column = GPOINTER_TO_INT(user_data);
+
+ gtk_tree_model_get(model, iter, data_column, &str, -1);
+ gtk_tree_model_get(model, iter, GROUP_COLUMN, &grp, -1);
+ /* XXX should we check that str is non NULL and print a warning or do assert? */
+
+ g_object_set(renderer, "text", str, NULL);
+ if (grp == packet) {
+ /* it's a number right align */
+ g_object_set(renderer, "xalign", 1.0, NULL);
+ }
+ else {
+ g_object_set(renderer, "xalign", 0.0, NULL);
+ }
+ g_free(str);
+}
+
+static gint
+proto_sort_func(GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ gchar *str_a = NULL;
+ gchar *str_b = NULL;
+ gchar *grp = NULL; /* type pointer, don't free */
+ gint ret = 0;
+
+ /* The col to get data from is in userdata */
+ gint data_column = GPOINTER_TO_INT(user_data);
+
+ gtk_tree_model_get(model, a, data_column, &str_a, -1);
+ gtk_tree_model_get(model, b, data_column, &str_b, -1);
+ gtk_tree_model_get(model, a, GROUP_COLUMN, &grp, -1);
+
+ if (str_a == str_b) {
+ ret = 0;
+ }
+ else if (str_a == NULL || str_b == NULL) {
+ ret = (str_a == NULL) ? -1 : 1;
+ }
+ else {
+ if (grp == packet) {
+ gint int_a = atoi(str_a);
+ gint int_b = atoi(str_b);
+ if (int_a == int_b)
+ ret = 0;
+ else if (int_a < int_b)
+ ret = -1;
+ else
+ ret = 1;
+ }
+ else
+ ret = g_ascii_strcasecmp(str_a,str_b);
+ }
+ g_free(str_a);
+ g_free(str_b);
+ return ret;
+}
+
+static gint find_summary_data(error_equiv_table *err, const expert_info_t *expert_data)
+{
+ guint i;
+ error_procedure_t *procedure;
+
+ /* First time thru values will be 0 */
+ if (err->num_procs==0) {
+ return -1;
+ }
+ for (i=0;i<err->num_procs;i++) {
+ procedure = &g_array_index(err->procs_array, error_procedure_t, i);
+ if (strcmp(procedure->entries[0], expert_data->protocol) == 0 &&
+ strcmp(procedure->entries[1], expert_data->summary) == 0) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static void
+error_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callback_action)
+{
+ int action, type, selection;
+ error_equiv_table *err = (error_equiv_table *)callback_data;
+ char str[512];
+ const char *current_filter;
+ error_procedure_t *procedure;
+
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ expert_info_t expert_data;
+ gchar *grp;
+
+ action=FILTER_ACTION(callback_action);
+ type=FILTER_ACTYPE(callback_action);
+
+
+ if(!gtk_tree_selection_get_selected(err->select, &model, &iter)){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No selection made or the table is empty");
+ return;
+ }
+
+ gtk_tree_model_get (model, &iter,
+ GROUP_COLUMN, &grp,
+ PROTOCOL_COLUMN, &expert_data.protocol,
+ SUMMARY_COLUMN, &expert_data.summary,
+ -1);
+
+ if (strcmp(grp, packet)==0) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "You cannot filter or search for packet number. Click on a valid item header.");
+ g_free(expert_data.summary);
+ return;
+ }
+
+ /* XXX: find_summary_data doesn't (currently) reference expert_data.group. */
+ /* If "group" is required, then the message from GROUP_COLUMN will need */
+ /* to be translated to the group number (or the actual group number */
+ /* will also need to be stored in the TreeModel). */
+ selection = find_summary_data(err, &expert_data);
+ /* g_free(expert_data.protocol); - const */
+ g_free(expert_data.summary);
+
+ if(selection>=(int)err->num_procs){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No items are selected");
+ return;
+ }
+ current_filter=gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
+
+ /* Some expert data doesn't pass an expert item. Without this we cannot create a filter */
+ /* But allow for searching of internet for error string */
+ procedure = &g_array_index(err->procs_array, error_procedure_t, selection);
+
+ if (action != ACTION_WEB_LOOKUP && action != ACTION_COPY) {
+ char *msg;
+ if (0 /*procedure->fvalue_value==NULL*/) {
+ if (action != ACTION_FIND_FRAME && action != ACTION_FIND_NEXT && action != ACTION_FIND_PREVIOUS) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Wireshark cannot create a filter on this item - %s, try using find instead.",
+ procedure->entries[1]);
+ return;
+ }
+ }
+ msg = g_malloc(escape_string_len(procedure->entries[1]));
+ escape_string(msg, procedure->entries[1]);
+ switch(type){
+ case ACTYPE_SELECTED:
+ /* if no expert item was passed */
+ if (procedure->fvalue_value==NULL) {
+ g_snprintf(str, sizeof(str), "expert.message==%s", msg);
+ }
+ else
+ {
+ /* expert item exists. Use it. */
+ g_strlcpy(str, procedure->fvalue_value, sizeof(str));
+ }
+ break;
+ case ACTYPE_NOT_SELECTED:
+ /* if no expert item was passed */
+ if (procedure->fvalue_value==NULL) {
+ g_snprintf(str, sizeof(str), "!(expert.message==%s)", msg);
+ }
+ else
+ {
+ /* expert item exists. Use it. */
+ g_snprintf(str, sizeof(str), "!(%s)", procedure->fvalue_value);
+ }
+ break;
+ /* the remaining cases will only exist if the expert item exists so no need to check */
+ case ACTYPE_AND_SELECTED:
+ if ((!current_filter) || (0 == strlen(current_filter)))
+ g_snprintf(str, sizeof(str), "expert.message==%s", msg);
+ else
+ g_snprintf(str, sizeof(str), "(%s) && (expert.message==%s)", current_filter, msg);
+ break;
+ case ACTYPE_OR_SELECTED:
+ if ((!current_filter) || (0 == strlen(current_filter)))
+ g_snprintf(str, sizeof(str), "expert.message==%s", msg);
+ else
+ g_snprintf(str, sizeof(str), "(%s) || (expert.message==%s)", current_filter, msg);
+ break;
+ case ACTYPE_AND_NOT_SELECTED:
+ if ((!current_filter) || (0 == strlen(current_filter)))
+ g_snprintf(str, sizeof(str), "!(expert.message==%s)", msg);
+ else
+ g_snprintf(str, sizeof(str), "(%s) && !(expert.message==%s)", current_filter, msg);
+ break;
+ case ACTYPE_OR_NOT_SELECTED:
+ if ((!current_filter) || (0 == strlen(current_filter)))
+ g_snprintf(str, sizeof(str), "!(expert.message==%s)", msg);
+ else
+ g_snprintf(str, sizeof(str), "(%s) || !(expert.message==%s)", current_filter, msg);
+ break;
+ default:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Can't find menu type - %u", type);
+ }
+ g_free(msg);
+ }
+
+ switch(action){
+ case ACTION_MATCH:
+ gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), str);
+ main_filter_packets(&cfile, str, FALSE);
+ gdk_window_raise(gtk_widget_get_window(top_level));
+ break;
+ case ACTION_PREPARE:
+ gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), str);
+ break;
+ case ACTION_FIND_FRAME:
+ /* When trying to perform a find without expert item, we must pass
+ * the expert string to the find window. The user might need to modify
+ * the string and click on the text search to locate the packet in question.
+ * So regardless of the type we will just bring up the find window and allow
+ * the user to modify the search criteria and options.
+ */
+ find_frame_with_filter(str);
+ break;
+ case ACTION_FIND_NEXT:
+ /* In the case of find next, if there was no expert item, then most likely the expert
+ * string was modified to locate the text inside the message. So we can't just perform
+ * a find with the expert string or we will not really be performing a find next.
+ * In an effort to allow the user to modify the string and/or continue searching, we
+ * will just present the user with the find window again with the default expert string.
+ * A better aproach would be to attempt in capturing the last find string and utilize this
+ * with a find next/previous. Also a better approach might be to just send a <Ctl-N> keystroke.
+ */
+ /* Fall trough */
+ case ACTION_FIND_PREVIOUS:
+ /* In the case of find previous, if there was no expert item, then most likely the expert
+ * string was modified to locate the text inside the message. So we can't just perform
+ * a find with the expert string or we will not really be performing a find previous.
+ * In an effort to allow the user to modify the string and/or continue searching, we
+ * will just present the user with the find window again with the default expert string.
+ * A better aproach would be to attempt in capturing the last find string and utilize this
+ * with a find next/previous. Also a better approach might be to just send a <Ctl-B> keystroke.
+ */
+ if (procedure->fvalue_value==NULL) {
+ find_frame_with_filter(str);
+ }
+ else
+ {
+ /* We have an expert item so just continue search without find dialog. */
+ cf_find_packet_dfilter_string(&cfile, str, SD_FORWARD);
+ }
+ break;
+ case ACTION_COLORIZE:
+ color_display_with_filter(str);
+ break;
+ case ACTION_WEB_LOOKUP:
+ /* Lookup expert string on internet. Default search via www.google.com */
+ g_snprintf(str, sizeof(str), "http://www.google.com/search?hl=en&q=%s+'%s'", procedure->entries[0], procedure->entries[1]);
+ browser_open_url(str);
+ break;
+ case ACTION_COPY:
+ {
+ GString *copyString = g_string_sized_new(0);
+ g_string_printf(copyString, "%s: %s",
+ procedure->entries[0], procedure->entries[1]);
+ copy_to_clipboard(copyString);
+ g_string_free(copyString, TRUE);
+ }
+ break;
+
+ default:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Can't find menu action - %u", action);
+ }
+}
+
+static gboolean
+error_show_popup_menu_cb(void *widg _U_, GdkEvent *event, gpointer user_data)
+{
+ error_equiv_table *err = user_data;
+ GdkEventButton *bevent = (GdkEventButton *)event;
+
+ if(event->type==GDK_BUTTON_PRESS && bevent->button==3){
+ gtk_menu_popup(GTK_MENU(err->menu), NULL, NULL, NULL, NULL,
+ bevent->button, bevent->time);
+ }
+
+ return FALSE;
+}
+
+static void
+apply_as_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_SELECTED, 0));
+}
+static void
+apply_as_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_NOT_SELECTED, 0));
+}
+static void
+apply_as_and_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_SELECTED, 0));
+}
+static void
+apply_as_or_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_SELECTED, 0));
+}
+static void
+apply_as_and_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, 0));
+}
+static void
+apply_as_or_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, 0));
+}
+
+static void
+prep_as_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_SELECTED, 0));
+}
+static void
+prep_as_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, 0));
+}
+static void
+prep_as_and_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_SELECTED, 0));
+}
+static void
+prep_as_or_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_SELECTED, 0));
+}
+static void
+prep_as_and_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, 0));
+}
+static void
+prep_as_or_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, 0));
+}
+
+static void
+find_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_FIND_FRAME(ACTYPE_SELECTED, 0));
+}
+static void
+find_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_FIND_FRAME(ACTYPE_NOT_SELECTED, 0));
+}
+static void
+find_prev_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, 0));
+}
+static void
+find_prev_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_NOT_SELECTED, 0));
+}
+static void
+find_next_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_FIND_NEXT(ACTYPE_SELECTED, 0));
+}
+static void
+find_next_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_FIND_NEXT(ACTYPE_NOT_SELECTED, 0));
+}
+static void
+color_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, 0));
+}
+static void
+color_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, 0));
+}
+static void
+internet_search_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_WEB_LOOKUP);
+}
+static void
+copy_cb(GtkWidget *widget, gpointer user_data)
+{
+ error_select_filter_cb( widget , user_data, CALLBACK_COPY);
+}
+
+static const char *ui_desc_expert_filter_popup =
+"<ui>\n"
+" <popup name='ExpertFilterPopup'>\n"
+" <menu action='/Apply as Filter'>\n"
+" <menuitem action='/Apply as Filter/Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " not Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected'/>\n"
+" </menu>\n"
+" <menu action='/Prepare a Filter'>\n"
+" <menuitem action='/Prepare a Filter/Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " not Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected'/>\n"
+" </menu>\n"
+" <menu action='/Find Frame'>\n"
+" <menu action='/Find Frame/Find Frame'>\n"
+" <menuitem action='/Find Frame/Selected'/>\n"
+" <menuitem action='/Find Frame/Not Selected'/>\n"
+" </menu>\n"
+" <menu action='/Find Frame/Find Next'>\n"
+" <menuitem action='/Find Next/Selected'/>\n"
+" <menuitem action='/Find Next/Not Selected'/>\n"
+" </menu>\n"
+" <menu action='/Find Frame/Find Previous'>\n"
+" <menuitem action='/Find Previous/Selected'/>\n"
+" <menuitem action='/Find Previous/Not Selected'/>\n"
+" </menu>\n"
+" </menu>\n"
+" <menu action='/Colorize Procedure'>\n"
+" <menuitem action='/Colorize Procedure/Selected'/>\n"
+" <menuitem action='/Colorize Procedure/Not Selected'/>\n"
+" </menu>\n"
+" <menu action='/Internet Search'>\n"
+" <menuitem action='/For Info Text'/>\n"
+" </menu>\n"
+" <menu action='/Copy'>\n"
+" <menuitem action='/Copy/Protocol Plus Summary'/>\n"
+" </menu>\n"
+" </popup>\n"
+"</ui>\n";
+
+
+/*
+ * GtkActionEntry
+ * typedef struct {
+ * const gchar *name;
+ * const gchar *stock_id;
+ * const gchar *label;
+ * const gchar *accelerator;
+ * const gchar *tooltip;
+ * GCallback callback;
+ * } GtkActionEntry;
+ * const gchar *name; The name of the action.
+ * const gchar *stock_id; The stock id for the action, or the name of an icon from the icon theme.
+ * const gchar *label; The label for the action. This field should typically be marked for translation,
+ * see gtk_action_group_set_translation_domain().
+ * If label is NULL, the label of the stock item with id stock_id is used.
+ * const gchar *accelerator; The accelerator for the action, in the format understood by gtk_accelerator_parse().
+ * const gchar *tooltip; The tooltip for the action. This field should typically be marked for translation,
+ * see gtk_action_group_set_translation_domain().
+ * GCallback callback; The function to call when the action is activated.
+ *
+ */
+static const GtkActionEntry expert_popup_entries[] = {
+ { "/Apply as Filter", NULL, "Apply as Filter", NULL, NULL, NULL },
+ { "/Prepare a Filter", NULL, "Prepare a Filter", NULL, NULL, NULL },
+ { "/Find Frame", NULL, "Find Frame", NULL, NULL, NULL },
+ { "/Find Frame/Find Frame", NULL, "Find Frame", NULL, NULL, NULL },
+ { "/Find Frame/Find Next", NULL, "Find Next" , NULL, NULL, NULL },
+ { "/Find Frame/Find Previous", NULL, "Find Previous", NULL, NULL, NULL },
+ { "/Colorize Procedure", NULL, "Colorize Procedure", NULL, NULL, NULL },
+ { "/Apply as Filter/Selected", NULL, "Selected", NULL, "Selected", G_CALLBACK(apply_as_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " not Selected", G_CALLBACK(apply_as_not_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", G_CALLBACK(apply_as_and_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", G_CALLBACK(apply_as_or_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", G_CALLBACK(apply_as_and_not_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", G_CALLBACK(apply_as_or_not_selected_cb) },
+ { "/Prepare a Filter/Selected", NULL, "Selected", NULL, "selcted", G_CALLBACK(prep_as_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " not Selected", G_CALLBACK(prep_as_not_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", G_CALLBACK(prep_as_and_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", G_CALLBACK(prep_as_or_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", G_CALLBACK(prep_as_and_not_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", G_CALLBACK(prep_as_or_not_selected_cb) },
+ { "/Find Frame/Selected", NULL, "Selected", NULL, "Selected", G_CALLBACK(find_selected_cb) },
+ { "/Find Frame/Not Selected", NULL, "Not Selected", NULL, "Not Selected", G_CALLBACK(find_not_selected_cb) },
+ { "/Find Previous/Selected", NULL, "Selected", NULL, "Selected", G_CALLBACK(find_prev_selected_cb) },
+ { "/Find Previous/Not Selected", NULL, "Not Selected", NULL, "Not Selected", G_CALLBACK(find_prev_not_selected_cb) },
+ { "/Find Next/Selected", NULL, "Selected", NULL, "Selected", G_CALLBACK(find_next_selected_cb) },
+ { "/Find Next/Not Selected", NULL, "Not Selected", NULL, "Not Selected", G_CALLBACK(find_next_not_selected_cb) },
+ { "/Colorize Procedure/Selected", NULL, "Selected", NULL, "Selected", G_CALLBACK(color_selected_cb) },
+ { "/Colorize Procedure/Not Selected", NULL, "Not Selected", NULL, "Not Selected", G_CALLBACK(color_not_selected_cb) },
+ { "/Internet Search", WIRESHARK_STOCK_INTERNET, "Internet Search", NULL, "Internet Search", NULL },
+ { "/For Info Text", NULL, "For Info Text", NULL, "For Info Text", G_CALLBACK(internet_search_cb) },
+ { "/Copy", NULL, "Copy", NULL, "Copy", NULL },
+ { "/Copy/Protocol Plus Summary", NULL, "Protocol Plus Summary", NULL, "Protocol Plus Summary", G_CALLBACK(copy_cb) },
+};
+
+static void
+expert_goto_pkt_cb (GtkTreeSelection *selection, gpointer data _U_)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ gchar *pkt;
+ gchar *grp;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ gtk_tree_model_get (model, &iter,
+ PROTOCOL_COLUMN, &pkt,
+ GROUP_COLUMN, &grp,
+ -1);
+
+ if (strcmp(grp, packet)==0) {
+ cf_goto_frame(&cfile, atoi(pkt));
+ }
+ g_free (pkt);
+ }
+}
+
+static void
+error_create_popup_menu(error_equiv_table *err)
+{
+ GtkUIManager *ui_manager;
+ GtkActionGroup *action_group;
+ GError *error = NULL;
+
+ err->select = gtk_tree_view_get_selection (GTK_TREE_VIEW (err->tree_view));
+ gtk_tree_selection_set_mode (err->select, GTK_SELECTION_SINGLE);
+ g_signal_connect (G_OBJECT (err->select), "changed", G_CALLBACK(expert_goto_pkt_cb), NULL);
+
+ action_group = gtk_action_group_new ("ExpertFilterPopupActionGroup");
+ gtk_action_group_add_actions (action_group, /* the action group */
+ (gpointer)expert_popup_entries, /* an array of action descriptions */
+ G_N_ELEMENTS(expert_popup_entries), /* the number of entries */
+ err); /* data to pass to the action callbacks */
+
+ ui_manager = gtk_ui_manager_new ();
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ gtk_ui_manager_add_ui_from_string (ui_manager,ui_desc_expert_filter_popup, -1, &error);
+ if (error != NULL)
+ {
+ fprintf (stderr, "Warning: building expert filter popup failed: %s\n",
+ error->message);
+ g_error_free (error);
+ error = NULL;
+ }
+ err->menu = gtk_ui_manager_get_widget(ui_manager, "/ExpertFilterPopup");
+ g_signal_connect(err->tree_view, "button_press_event", G_CALLBACK(error_show_popup_menu_cb), err);
+}
+
+void
+init_error_table(error_equiv_table *err, guint num_procs, GtkWidget *vbox)
+{
+ GtkTreeStore *store;
+ GtkWidget *tree;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSortable *sortable;
+
+ /* Create the store */
+ store = gtk_tree_store_new (4, /* Total number of columns */
+ G_TYPE_POINTER, /* Group */
+ G_TYPE_STRING, /* Protocol */
+ G_TYPE_STRING, /* Summary */
+ G_TYPE_INT); /* Count */
+
+ /* Create a view */
+ tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
+ err->tree_view = GTK_TREE_VIEW(tree);
+ sortable = GTK_TREE_SORTABLE(store);
+
+ /* Speed up the list display */
+ gtk_tree_view_set_fixed_height_mode(err->tree_view, TRUE);
+
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW (tree), FALSE);
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref (G_OBJECT (store));
+
+ /* Create a cell renderer */
+ renderer = gtk_cell_renderer_text_new ();
+
+ /* Create the first column, associating the "text" attribute of the
+ * cell_renderer to the first column of the model */
+ column = gtk_tree_view_column_new_with_attributes ("Group", renderer, NULL);
+ gtk_tree_view_column_set_sort_column_id(column, GROUP_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_cell_data_func(column, renderer, str_ptr_data_func,
+ GINT_TO_POINTER(GROUP_COLUMN), NULL);
+
+ gtk_tree_sortable_set_sort_func(sortable, GROUP_COLUMN, str_ptr_sort_func,
+ GINT_TO_POINTER(GROUP_COLUMN), NULL);
+
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_column_set_fixed_width(column, 80);
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (GTK_TREE_VIEW (err->tree_view), column);
+
+ /* Second column.. Protocol. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Protocol", renderer, "text", PROTOCOL_COLUMN, NULL);
+ gtk_tree_view_column_set_sort_column_id(column, PROTOCOL_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_cell_data_func(column, renderer, proto_data_func,
+ GINT_TO_POINTER(PROTOCOL_COLUMN), NULL);
+
+ gtk_tree_sortable_set_sort_func(sortable, PROTOCOL_COLUMN, proto_sort_func,
+ GINT_TO_POINTER(PROTOCOL_COLUMN), NULL);
+
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_column_set_fixed_width(column, 100);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (err->tree_view), column);
+
+ /* Third column.. Summary. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Summary", renderer, "text", SUMMARY_COLUMN, NULL);
+ gtk_tree_view_column_set_sort_column_id(column, SUMMARY_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 300);
+ gtk_tree_view_column_set_fixed_width(column,
+ 700 /* window size */ -
+ (80 /* group */ + 100 /* protocol */ + 80 /* count */ +
+ 24 /* border */ + 22 /* vbar */));
+ gtk_tree_view_append_column (GTK_TREE_VIEW (err->tree_view), column);
+
+ /* Last column.. Count. */
+ renderer = gtk_cell_renderer_text_new ();
+ /* right align */
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+ column = gtk_tree_view_column_new_with_attributes ("Count", renderer, "text", COUNT_COLUMN, NULL);
+ gtk_tree_view_column_set_sort_column_id(column, COUNT_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (err->tree_view), column);
+
+ err->scrolled_window=scrolled_window_new(NULL, NULL);
+
+ gtk_container_add(GTK_CONTAINER(err->scrolled_window), GTK_WIDGET (err->tree_view));
+
+ gtk_box_pack_start(GTK_BOX(vbox), err->scrolled_window, TRUE, TRUE, 0);
+
+ gtk_tree_view_set_search_column (err->tree_view, SUMMARY_COLUMN); /* Allow searching the summary */
+ gtk_tree_view_set_reorderable (err->tree_view, TRUE); /* Allow user to reorder data with drag n drop */
+
+ /* Now enable the sorting of each column */
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(err->tree_view), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(err->tree_view), TRUE);
+
+ gtk_widget_show(err->scrolled_window);
+
+ err->num_procs=num_procs;
+
+ err->text = g_string_chunk_new(100);
+ err->procs_array = g_array_sized_new(FALSE, FALSE, sizeof(error_procedure_t), num_procs);
+
+ /* create popup menu for this table */
+ error_create_popup_menu(err);
+}
+
+void
+init_error_table_row(error_equiv_table *err, const expert_info_t *expert_data)
+{
+ guint old_num_procs=err->num_procs;
+ gint row=0;
+ error_procedure_t *procedure;
+ GtkTreeStore *store;
+ GtkTreeIter new_iter;
+ gchar num[10];
+
+ /* we have discovered a new procedure. Extend the table accordingly */
+ row = find_summary_data(err, expert_data);
+ if(row==-1){
+ error_procedure_t new_procedure;
+ /* First time we have seen this event so initialize memory table */
+ row = old_num_procs; /* Number of expert events since this is a new event */
+
+ new_procedure.count=0; /* count of events for this item */
+ new_procedure.fvalue_value = NULL; /* Filter string value */
+
+ g_array_append_val(err->procs_array, new_procedure);
+ procedure = &g_array_index(err->procs_array, error_procedure_t, row);
+
+ /* Create the item in our memory table */
+ procedure->entries[0]=(char *)g_string_chunk_insert_const(err->text, expert_data->protocol); /* Protocol */
+ procedure->entries[1]=(char *)g_string_chunk_insert_const(err->text, expert_data->summary); /* Summary */
+
+ /* Create a new item in our tree view */
+ store = GTK_TREE_STORE(gtk_tree_view_get_model(err->tree_view)); /* Get store */
+ gtk_tree_store_append (store, &procedure->iter, NULL); /* Acquire an iterator */
+
+ /* match_strval return a static constant or null */
+ gtk_tree_store_set (store, &procedure->iter,
+ GROUP_COLUMN, match_strval(expert_data->group, expert_group_vals),
+ PROTOCOL_COLUMN, procedure->entries[0],
+ SUMMARY_COLUMN, procedure->entries[1], -1);
+
+ /* If an expert item was passed then build the filter string */
+ if (expert_data->pitem) {
+ char *filter;
+
+ g_assert(PITEM_FINFO(expert_data->pitem));
+ filter = proto_construct_match_selected_string(PITEM_FINFO(expert_data->pitem), NULL);
+ if (filter != NULL)
+ procedure->fvalue_value = g_string_chunk_insert_const(err->text, filter);
+ }
+ /* Store the updated count of events */
+ err->num_procs = ++old_num_procs;
+ }
+
+ /* Update our memory table with event data */
+ procedure = &g_array_index(err->procs_array, error_procedure_t, row);
+ procedure->count++; /* increment the count of events for this item */
+
+ /* Update the tree with new count for this event */
+ store = GTK_TREE_STORE(gtk_tree_view_get_model(err->tree_view));
+ gtk_tree_store_set(store, &procedure->iter,
+ COUNT_COLUMN, procedure->count,
+ -1);
+
+ g_snprintf(num, sizeof(num), "%d", expert_data->packet_num);
+#if 0
+ This does not have a big performance improvment :(
+ gtk_tree_store_insert_with_values (store,
+ &new_iter, /* *iter */
+ &procedure->iter, /* *parent*/
+ G_MAXINT, /* position */
+
+#else
+
+ /* FIXME gtk is plagued with slow algorithms
+ gtk_tree_store_append call new_path and its nice recursive linear search....
+ */
+ if (procedure->count > 1000) {
+ /* If there's more than 1000 sub rows give up and prepend new rows, at least
+ it will end in a reasonable time. Anyway with so many rows it's not
+ very useful and if sorted the right order is restored.
+ */
+ gtk_tree_store_prepend(store, &new_iter, &procedure->iter);
+ }
+ else {
+ gtk_tree_store_append(store, &new_iter, &procedure->iter);
+ }
+ gtk_tree_store_set(store, &new_iter,
+#endif
+ GROUP_COLUMN, packet,
+ PROTOCOL_COLUMN, num,
+ COUNT_COLUMN, 1,
+ -1);
+}
+
+void
+reset_error_table_data(error_equiv_table *err)
+{
+ GtkTreeStore *store;
+
+ store = GTK_TREE_STORE(gtk_tree_view_get_model(err->tree_view));
+ gtk_tree_store_clear(store);
+ err->num_procs = 0;
+ /* g_string_chunk_clear() is introduced in glib 2.14 */
+ g_string_chunk_free(err->text);
+ err->text = g_string_chunk_new(100);
+
+ g_array_set_size(err->procs_array, 0);
+}
+
+void
+free_error_table_data(error_equiv_table *err)
+{
+ err->num_procs=0;
+ g_string_chunk_free(err->text);
+ g_array_free(err->procs_array, TRUE);
+}
diff --git a/ui/gtk/expert_comp_table.h b/ui/gtk/expert_comp_table.h
new file mode 100644
index 0000000000..c8a099e2c0
--- /dev/null
+++ b/ui/gtk/expert_comp_table.h
@@ -0,0 +1,93 @@
+/* expert_comp_table.h
+ * expert_comp_table 2005 Greg Morris
+ * Portions copied from service_response_time_table.h by Ronnie Sahlberg
+ * Helper routines common to all composite expert statistics
+ * tap.
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EXPERT_COMP_TABLE_H__
+#define __EXPERT_COMP_TABLE_H__
+
+#include <gtk/gtk.h>
+#include <epan/expert.h>
+
+typedef struct expert_tapdata_s expert_tapdata_t;
+
+/** @file
+ * Helper routines common to all error statistics tap.
+ */
+
+/** Procedure data */
+typedef struct _error_procedure_t {
+ char *entries[2]; /**< column entries */
+ char *fvalue_value; /**< filter value */
+ GtkTreeIter iter;
+ guint count; /**< number of expert items encountered
+ for this entry */
+} error_procedure_t;
+
+/** Statistics table */
+typedef struct _error_equiv_table {
+ GtkWidget *scrolled_window; /**< window widget */
+ GtkTreeSelection *select; /**< item selected */
+ GtkTreeView *tree_view; /**< Tree view */
+ GtkWidget *menu; /**< context menu */
+ guint num_procs; /**< number of elements on procedures array */
+ GArray *procs_array; /**< the procedures array error_procedure_t *procedures */
+ GStringChunk* text;
+}error_equiv_table;
+
+/** Init an err table data structure.
+ *
+ * @param err the err table to init
+ * @param num_procs number of procedures
+ * @param vbox the corresponding GtkVBox to fill in
+ */
+void init_error_table(error_equiv_table *err, guint num_procs, GtkWidget *vbox);
+
+/** Init an err table row data structure.
+ *
+ * @param err the err table
+ * @param expert_data data
+ */
+void init_error_table_row(error_equiv_table *err, const expert_info_t *expert_data);
+
+/** Draw the err table data.
+ *
+ * @param err the err table
+ */
+void draw_error_table_data(error_equiv_table *err);
+
+/** Reset the err table data.
+ *
+ * @param err the err table
+ */
+void reset_error_table_data(error_equiv_table *err);
+
+/** Free the err table data.
+ *
+ * @param err the err table
+ */
+void free_error_table_data(error_equiv_table *err);
+
+#endif /* __EXPERT_COMP_TABLE_H__ */
diff --git a/ui/gtk/expert_indicators.h b/ui/gtk/expert_indicators.h
new file mode 100644
index 0000000000..b5a66e1207
--- /dev/null
+++ b/ui/gtk/expert_indicators.h
@@ -0,0 +1,316 @@
+/* This file was automatically generated. DO NOT EDIT. */
+
+/* GdkPixbuf RGBA C-Source image dump */
+
+#ifdef __SUNPRO_C
+#pragma align 4 (expert_chat_pb_data)
+#endif
+#ifdef __GNUC__
+static const guint8 expert_chat_pb_data[] __attribute__ ((__aligned__ (4))) =
+#else
+static const guint8 expert_chat_pb_data[] =
+#endif
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (784) */
+ "\0\0\3("
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (56) */
+ "\0\0\0""8"
+ /* width (14) */
+ "\0\0\0\16"
+ /* height (14) */
+ "\0\0\0\16"
+ /* pixel_data: */
+ "\377\377\377\0\377\377\377\0\377\377\377\0\0\0\0\15\1\11\22\206\1\13"
+ "\27\314\1\14\31\345\2\15\32\345\1\13\27\314\1\11\22\206\0\0\0\15\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\1"
+ "\3\6G\10\22\35\344X~\252\374\177\252\334\377\240\303\353\377\243\305"
+ "\353\377\177\252\334\377U|\250\373\11\23\35\344\1\3\6G\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\1\3\6G\26'<\357~\251\334\377\227\275\351"
+ "\377\226\274\351\377\226\274\351\377\226\274\351\377\226\274\351\377"
+ "\226\274\351\377}\251\334\377\26'<\357\1\3\6G\377\377\377\0\0\0\0\15"
+ "\4\15\30\343`\226\324\377\201\260\346\377\201\260\346\377\201\260\346"
+ "\377\201\260\346\377\201\260\346\377\201\260\346\377\201\260\346\377"
+ "\201\260\346\377n\237\330\377\4\15\30\343\0\0\0\15\4\13\23\206\37Q\212"
+ "\372e\237\341\377n\244\343\377n\244\343\377n\244\343\377n\244\343\377"
+ "n\244\343\377n\244\343\377i\242\342\377g\240\342\377n\244\343\377*Z\220"
+ "\372\4\13\23\206\7\17\31\3146z\313\377:\204\333\377\77\207\334\377G\214"
+ "\335\377H\215\336\377H\215\336\377G\214\335\377A\210\334\377:\204\333"
+ "\377:\204\333\377=\206\334\3776z\313\377\7\17\31\314\10\21\33\345C\213"
+ "\337\377C\213\337\377C\213\337\377C\213\337\377C\213\337\377C\213\337"
+ "\377C\213\337\377C\213\337\377C\213\337\377C\213\337\377C\213\337\377"
+ "C\213\337\377\10\21\33\345\11\22\34\345L\221\342\377L\221\342\377L\221"
+ "\342\377L\221\342\377L\221\342\377L\221\342\377L\221\342\377L\221\342"
+ "\377L\221\342\377L\221\342\377L\221\342\377L\221\342\377\11\22\34\345"
+ "\12\22\33\314O\215\325\377U\230\346\377U\230\346\377U\230\346\377U\230"
+ "\346\377U\230\346\377U\230\346\377U\230\346\377U\230\346\377U\230\346"
+ "\377U\230\346\377O\215\325\377\12\22\33\314\10\16\25\206<f\225\372^\237"
+ "\351\377^\237\351\377^\237\351\377^\237\351\377^\237\351\377^\237\351"
+ "\377^\237\351\377^\237\351\377^\237\351\377^\237\351\377<f\225\372\10"
+ "\16\25\206\0\0\0\15\14\23\33\343_\231\333\377g\245\355\377g\245\355\377"
+ "g\245\355\377g\245\355\377g\245\355\377g\245\355\377g\245\355\377g\245"
+ "\355\377_\231\333\377\14\23\33\343\0\0\0\15\377\377\377\0\3\5\7G\32'"
+ "6\356i\237\337\377q\254\361\377q\254\361\377q\254\361\377q\254\361\377"
+ "q\254\361\377q\254\361\377i\237\337\377\32'6\356\3\5\7G\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\4\5\10G\16\24\34\343Nr\234\372q\245\342"
+ "\377z\262\364\377z\262\364\377q\245\342\377Nr\234\372\16\24\34\343\4"
+ "\5\10G\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\0\0\0\15\13\20\26\206\17\25\35\314\20\26\36\345\20\26\36\345\17"
+ "\25\35\314\13\20\26\206\0\0\0\15\377\377\377\0\377\377\377\0\377\377"
+ "\377\0"};
+
+
+/* GdkPixbuf RGBA C-Source image dump */
+
+#ifdef __SUNPRO_C
+#pragma align 4 (expert_error_pb_data)
+#endif
+#ifdef __GNUC__
+static const guint8 expert_error_pb_data[] __attribute__ ((__aligned__ (4))) =
+#else
+static const guint8 expert_error_pb_data[] =
+#endif
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (784) */
+ "\0\0\3("
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (56) */
+ "\0\0\0""8"
+ /* width (14) */
+ "\0\0\0\16"
+ /* height (14) */
+ "\0\0\0\16"
+ /* pixel_data: */
+ "\377\377\377\0\377\377\377\0\377\377\377\0\0\0\0\15\20\1\1\206\25\1\1"
+ "\314\26\1\1\345\27\2\2\345\25\1\1\314\20\1\1\206\0\0\0\15\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\6\0\0G\33"
+ "\7\7\343\241UU\373\321{{\376\343\235\235\376\344\241\241\376\321{{\376"
+ "\237RR\373\34\10\10\343\6\0\0G\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\6\1\1G8\23\23\356\323yy\375\342\223\223\376\341\222\222\376\341\222"
+ "\222\376\341\222\222\376\341\222\222\376\341\222\222\376\323xx\3758\23"
+ "\23\356\6\1\1G\377\377\377\0\0\0\0\15\26\3\3\342\312YY\374\335{{\375"
+ "\335{{\375\335{{\375\335{{\375\335{{\375\335{{\375\335{{\375\335{{\375"
+ "\317hh\374\26\3\3\342\0\0\0\15\22\3\3\206\201\25\25\367\331[[\373\333"
+ "dd\373\333dd\373\333dd\373\333dd\373\333dd\373\333dd\373\332__\373\331"
+ "]]\373\333dd\373\207!!\367\22\3\3\206\30\5\5\313\301%%\371\321((\370"
+ "\322--\370\32466\370\32488\370\32488\370\32477\370\32300\370\321((\370"
+ "\321((\370\322,,\370\301%%\371\30\5\5\313\32\6\6\344\327//\367\327//"
+ "\367\327//\367\327//\367\327//\367\327//\367\327//\367\327//\367\327"
+ "//\367\327//\367\327//\367\327//\367\32\6\6\344\32\6\6\344\33566\366"
+ "\33566\366\33566\366\33566\366\33566\366\33566\366\33566\366\33566\366"
+ "\33566\366\33566\366\33566\366\33566\366\32\6\6\344\31\7\7\313\32177"
+ "\366\343<<\365\343<<\365\343<<\365\343<<\365\343<<\365\343<<\365\343"
+ "<<\365\343<<\365\343<<\365\343<<\365\32177\366\31\7\7\313\24\6\6\205"
+ "\223**\363\351CC\364\351CC\364\351CC\364\351CC\364\351CC\364\351CC\364"
+ "\351CC\364\351CC\364\351CC\364\351CC\364\223**\363\24\6\6\205\0\0\0\15"
+ "\32\10\10\341\334DD\364\357JJ\363\357JJ\363\357JJ\363\357JJ\363\357J"
+ "J\363\357JJ\363\357JJ\363\357JJ\363\334DD\364\32\10\10\341\0\0\0\15\377"
+ "\377\377\0\10\2\2G5\21\21\353\342KK\362\365QQ\361\365QQ\361\365QQ\361"
+ "\365QQ\361\365QQ\361\365QQ\361\342KK\3625\21\21\353\10\2\2G\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\10\3\3G\33\11\11\341\23566\361\347PP\361"
+ "\373WW\360\373WW\360\347PP\361\23566\361\33\11\11\341\10\3\3G\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\0\0\0"
+ "\15\26\10\10\205\34\12\12\312\36\13\13\343\36\13\13\343\34\12\12\312"
+ "\26\10\10\205\0\0\0\15\377\377\377\0\377\377\377\0\377\377\377\0"};
+
+
+/* GdkPixbuf RGBA C-Source image dump */
+
+#ifdef __SUNPRO_C
+#pragma align 4 (expert_none_pb_data)
+#endif
+#ifdef __GNUC__
+static const guint8 expert_none_pb_data[] __attribute__ ((__aligned__ (4))) =
+#else
+static const guint8 expert_none_pb_data[] =
+#endif
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (784) */
+ "\0\0\3("
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (56) */
+ "\0\0\0""8"
+ /* width (14) */
+ "\0\0\0\16"
+ /* height (14) */
+ "\0\0\0\16"
+ /* pixel_data: */
+ "\377\377\377\0\377\377\377\0\377\377\377\0\0\0\0\15\17\17\17\206\24\24"
+ "\24\314\25\25\25\345\26\26\26\345\24\24\24\314\17\17\17\206\0\0\0\15"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\5\5\5G\32\32\32\344\235\235\235\374\315\315\315\377\337\337\337\377"
+ "\340\340\340\377\315\315\315\377\233\233\233\373\33\33\33\344\5\5\5G"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\6\6\6G555\357\314\314\314"
+ "\377\334\334\334\377\334\334\334\377\334\334\334\377\334\334\334\377"
+ "\334\334\334\377\334\334\334\377\314\314\314\377555\357\6\6\6G\377\377"
+ "\377\0\0\0\0\15\25\25\25\343\300\300\300\377\324\324\324\377\324\324"
+ "\324\377\324\324\324\377\324\324\324\377\324\324\324\377\324\324\324"
+ "\377\324\324\324\377\324\324\324\377\306\306\306\377\25\25\25\343\0\0"
+ "\0\15\21\21\21\206www\372\313\313\313\377\316\316\316\377\316\316\316"
+ "\377\316\316\316\377\316\316\316\377\316\316\316\377\316\316\316\377"
+ "\314\314\314\377\314\314\314\377\316\316\316\377~~~\372\21\21\21\206"
+ "\26\26\26\314\256\256\256\377\274\274\274\377\276\276\276\377\300\300"
+ "\300\377\301\301\301\377\301\301\301\377\301\301\301\377\276\276\276"
+ "\377\274\274\274\377\274\274\274\377\275\275\275\377\256\256\256\377"
+ "\26\26\26\314\27\27\27\345\277\277\277\377\277\277\277\377\277\277\277"
+ "\377\277\277\277\377\277\277\277\377\277\277\277\377\277\277\277\377"
+ "\277\277\277\377\277\277\277\377\277\277\277\377\277\277\277\377\277"
+ "\277\277\377\27\27\27\345\30\30\30\345\302\302\302\377\302\302\302\377"
+ "\302\302\302\377\302\302\302\377\302\302\302\377\302\302\302\377\302"
+ "\302\302\377\302\302\302\377\302\302\302\377\302\302\302\377\302\302"
+ "\302\377\302\302\302\377\30\30\30\345\27\27\27\314\266\266\266\377\305"
+ "\305\305\377\305\305\305\377\305\305\305\377\305\305\305\377\305\305"
+ "\305\377\305\305\305\377\305\305\305\377\305\305\305\377\305\305\305"
+ "\377\305\305\305\377\266\266\266\377\27\27\27\314\22\22\22\206\200\200"
+ "\200\372\310\310\310\377\310\310\310\377\310\310\310\377\310\310\310"
+ "\377\310\310\310\377\310\310\310\377\310\310\310\377\310\310\310\377"
+ "\310\310\310\377\310\310\310\377\200\200\200\372\22\22\22\206\0\0\0\15"
+ "\27\27\27\343\274\274\274\377\313\313\313\377\313\313\313\377\313\313"
+ "\313\377\313\313\313\377\313\313\313\377\313\313\313\377\313\313\313"
+ "\377\313\313\313\377\274\274\274\377\27\27\27\343\0\0\0\15\377\377\377"
+ "\0\6\6\6G///\356\277\277\277\377\316\316\316\377\316\316\316\377\316"
+ "\316\316\377\316\316\316\377\316\316\316\377\316\316\316\377\277\277"
+ "\277\377///\356\6\6\6G\377\377\377\0\377\377\377\0\377\377\377\0\6\6"
+ "\6G\30\30\30\343\206\206\206\372\301\301\301\377\321\321\321\377\321"
+ "\321\321\377\301\301\301\377\206\206\206\372\30\30\30\343\6\6\6G\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\0"
+ "\0\0\15\23\23\23\206\30\30\30\314\32\32\32\345\32\32\32\345\30\30\30"
+ "\314\23\23\23\206\0\0\0\15\377\377\377\0\377\377\377\0\377\377\377\0"};
+
+
+/* GdkPixbuf RGBA C-Source image dump */
+
+#ifdef __SUNPRO_C
+#pragma align 4 (expert_note_pb_data)
+#endif
+#ifdef __GNUC__
+static const guint8 expert_note_pb_data[] __attribute__ ((__aligned__ (4))) =
+#else
+static const guint8 expert_note_pb_data[] =
+#endif
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (784) */
+ "\0\0\3("
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (56) */
+ "\0\0\0""8"
+ /* width (14) */
+ "\0\0\0\16"
+ /* height (14) */
+ "\0\0\0\16"
+ /* pixel_data: */
+ "\377\377\377\0\377\377\377\0\377\377\377\0\0\0\0\15\1\16\20\206\1\22"
+ "\25\314\1\23\26\345\2\24\27\345\1\22\25\314\1\16\20\206\0\0\0\15\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\1"
+ "\5\6G\11\30\33\344Z\226\242\374\200\305\323\377\241\330\344\377\244\332"
+ "\345\377\200\305\323\377V\224\240\373\12\31\33\344\1\5\6G\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\1\5\6G\27""39\357\200\307\324\377\231"
+ "\327\343\377\230\327\343\377\230\327\343\377\230\327\343\377\230\327"
+ "\343\377\230\327\343\377\177\306\324\377\27""39\357\1\5\6G\377\377\377"
+ "\0\0\0\0\15\5\24\27\343e\275\314\377\205\322\336\377\205\322\336\377"
+ "\205\322\336\377\205\322\336\377\205\322\336\377\205\322\336\377\205"
+ "\322\336\377\205\322\336\377s\303\320\377\5\24\27\343\0\0\0\15\5\21\22"
+ "\206%w\203\372m\313\332\377u\316\334\377u\316\334\377u\316\334\377u\316"
+ "\334\377u\316\334\377u\316\334\377p\314\333\377n\314\332\377u\316\334"
+ "\3770~\211\372\5\21\22\206\10\26\30\314A\263\303\377F\301\323\377K\303"
+ "\324\377R\305\326\377S\305\326\377S\305\326\377R\305\326\377M\303\325"
+ "\377F\301\323\377F\301\323\377I\302\324\377A\263\303\377\10\26\30\314"
+ "\12\31\33\345Q\311\331\377Q\311\331\377Q\311\331\377Q\311\331\377Q\311"
+ "\331\377Q\311\331\377Q\311\331\377Q\311\331\377Q\311\331\377Q\311\331"
+ "\377Q\311\331\377Q\311\331\377\12\31\33\345\13\32\33\345]\321\336\377"
+ "]\321\336\377]\321\336\377]\321\336\377]\321\336\377]\321\336\377]\321"
+ "\336\377]\321\336\377]\321\336\377]\321\336\377]\321\336\377]\321\336"
+ "\377\13\32\33\345\14\31\32\314a\311\323\377i\331\344\377i\331\344\377"
+ "i\331\344\377i\331\344\377i\331\344\377i\331\344\377i\331\344\377i\331"
+ "\344\377i\331\344\377i\331\344\377a\311\323\377\14\31\32\314\12\24\25"
+ "\206K\220\226\372u\341\352\377u\341\352\377u\341\352\377u\341\352\377"
+ "u\341\352\377u\341\352\377u\341\352\377u\341\352\377u\341\352\377u\341"
+ "\352\377K\220\226\372\12\24\25\206\0\0\0\15\17\32\33\343w\330\336\377"
+ "\201\351\360\377\201\351\360\377\201\351\360\377\201\351\360\377\201"
+ "\351\360\377\201\351\360\377\201\351\360\377\201\351\360\377w\330\336"
+ "\377\17\32\33\343\0\0\0\15\377\377\377\0\4\7\10G\40""77\356\202\340\343"
+ "\377\215\362\365\377\215\362\365\377\215\362\365\377\215\362\365\377"
+ "\215\362\365\377\215\362\365\377\202\340\343\377\40""77\356\4\7\10G\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\5\10\10G\21\34\34\343a\240\241"
+ "\372\215\347\350\377\230\372\373\377\230\372\373\377\215\347\350\377"
+ "a\240\241\372\21\34\34\343\5\10\10G\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\0\0\0\15\16\27\27\206\22\35\35\314"
+ "\24\37\37\345\24\37\37\345\22\35\35\314\16\27\27\206\0\0\0\15\377\377"
+ "\377\0\377\377\377\0\377\377\377\0"};
+
+
+/* GdkPixbuf RGBA C-Source image dump */
+
+#ifdef __SUNPRO_C
+#pragma align 4 (expert_warn_pb_data)
+#endif
+#ifdef __GNUC__
+static const guint8 expert_warn_pb_data[] __attribute__ ((__aligned__ (4))) =
+#else
+static const guint8 expert_warn_pb_data[] =
+#endif
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (784) */
+ "\0\0\3("
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (56) */
+ "\0\0\0""8"
+ /* width (14) */
+ "\0\0\0\16"
+ /* height (14) */
+ "\0\0\0\16"
+ /* pixel_data: */
+ "\377\377\377\0\377\377\377\0\377\377\377\0\0\0\0\15\21\21\1\206\26\26"
+ "\1\314\27\30\1\345\30\31\2\345\26\26\1\314\21\21\1\206\0\0\0\15\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\6\6\0"
+ "G\34\34\10\344\245\246V\374\326\327}\377\346\347\236\377\347\350\241"
+ "\377\326\327}\377\243\244S\373\34\34\11\344\6\6\0G\377\377\377\0\377"
+ "\377\377\0\377\377\377\0\6\6\1G::\24\357\327\327y\377\345\346\223\377"
+ "\345\345\222\377\345\345\222\377\345\345\222\377\345\345\222\377\345"
+ "\345\222\377\326\327y\377::\24\357\6\6\1G\377\377\377\0\0\0\0\15\27\27"
+ "\3\343\316\317X\377\341\341z\377\341\341z\377\341\341z\377\341\341z\377"
+ "\341\341z\377\341\341z\377\341\341z\377\341\341z\377\323\323g\377\27"
+ "\27\3\343\0\0\0\15\23\23\3\206\205\205\25\372\334\334Z\377\336\336c\377"
+ "\336\336c\377\336\336c\377\336\336c\377\336\336c\377\336\336c\377\335"
+ "\335^\377\334\334\\\377\336\336c\377\213\213!\372\23\23\3\206\31\31\5"
+ "\314\304\304$\377\324\324'\377\325\325,\377\327\3275\377\327\3276\377"
+ "\327\3276\377\327\3276\377\326\326/\377\324\324'\377\324\324'\377\325"
+ "\325+\377\304\304$\377\31\31\5\314\33\32\6\345\331\327-\377\331\327-"
+ "\377\331\327-\377\331\327-\377\331\327-\377\331\327-\377\331\327-\377"
+ "\331\327-\377\331\327-\377\331\327-\377\331\327-\377\331\327-\377\33"
+ "\32\6\345\33\33\6\345\335\3332\377\335\3332\377\335\3332\377\335\333"
+ "2\377\335\3332\377\335\3332\377\335\3332\377\335\3332\377\335\3332\377"
+ "\335\3332\377\335\3332\377\335\3332\377\33\33\6\345\32\32\6\314\321\316"
+ "4\377\342\3378\377\342\3378\377\342\3378\377\342\3378\377\342\3378\377"
+ "\342\3378\377\342\3378\377\342\3378\377\342\3378\377\342\3378\377\321"
+ "\3164\377\32\32\6\314\25\24\6\206\223\221(\372\346\343>\377\346\343>"
+ "\377\346\343>\377\346\343>\377\346\343>\377\346\343>\377\346\343>\377"
+ "\346\343>\377\346\343>\377\346\343>\377\223\221(\372\25\24\6\206\0\0"
+ "\0\15\33\32\10\343\331\326\77\377\353\347D\377\353\347D\377\353\347D"
+ "\377\353\347D\377\353\347D\377\353\347D\377\353\347D\377\353\347D\377"
+ "\331\326\77\377\33\32\10\343\0\0\0\15\377\377\377\0\7\7\2G65\21\356\335"
+ "\331D\377\357\353I\377\357\353I\377\357\353I\377\357\353I\377\357\353"
+ "I\377\357\353I\377\335\331D\37765\21\356\7\7\2G\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\10\7\2G\34\33\11\343\234\2313\372\342\335I\377\364"
+ "\357O\377\364\357O\377\342\335I\377\234\2313\372\34\33\11\343\10\7\2"
+ "G\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\0\0\0\15\26\26\7\206\35\34\12\314\36\36\12\345\36\36\12\345\35\34"
+ "\12\314\26\26\7\206\0\0\0\15\377\377\377\0\377\377\377\0\377\377\377"
+ "\0"};
+
+
diff --git a/ui/gtk/export_object.c b/ui/gtk/export_object.c
new file mode 100644
index 0000000000..7d34fbbcf5
--- /dev/null
+++ b/ui/gtk/export_object.c
@@ -0,0 +1,567 @@
+/* export_object.c
+ * Common routines for tracking & saving objects found in streams of data
+ * Copyright 2007, Stephen Fisher (see AUTHORS file)
+ *
+ * $Id$
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <epan/packet_info.h>
+#include <epan/prefs.h>
+#include <epan/tap.h>
+
+#include <../alert_box.h>
+#include <../simple_dialog.h>
+#include <wsutil/file_util.h>
+
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/file_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/export_object.h"
+#include <string.h>
+
+enum {
+ EO_PKT_NUM_COLUMN,
+ EO_HOSTNAME_COLUMN,
+ EO_CONTENT_TYPE_COLUMN,
+ EO_BYTES_COLUMN,
+ EO_FILENAME_COLUMN,
+ EO_NUM_COLUMNS /* must be last */
+};
+
+static eo_protocoldata_reset_cb eo_protocoldata_reset = NULL;
+
+
+static void
+eo_remember_this_row(GtkTreeModel *model _U_, GtkTreePath *path,
+ GtkTreeIter *iter _U_, gpointer arg)
+{
+ export_object_list_t *object_list = arg;
+ export_object_entry_t *entry;
+
+ gint *path_index;
+
+ if((path_index = gtk_tree_path_get_indices(path)) == NULL)
+ /* Row not found in tree - shouldn't happen */
+ return;
+
+ object_list->row_selected = path_index[0];
+
+ /* Select the corresponding packet in the packet list */
+ entry = g_slist_nth_data(object_list->entries,
+ object_list->row_selected);
+ cf_goto_frame(&cfile, entry->pkt_num);
+}
+
+static void
+eo_remember_row_num(GtkTreeSelection *sel, gpointer data)
+{
+ gtk_tree_selection_selected_foreach(sel, eo_remember_this_row, data);
+}
+
+
+/* Called when the Export Object window is closed in any way */
+static void
+eo_win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ export_object_list_t *object_list = data;
+ export_object_entry_t *entry;
+ GSList *slist = object_list->entries;
+
+ protect_thread_critical_region();
+ remove_tap_listener(object_list);
+ unprotect_thread_critical_region();
+
+ /* Free the GSList attributes */
+ while(slist) {
+ entry = slist->data;
+
+ g_free(entry->hostname);
+ g_free(entry->content_type);
+ g_free(entry->filename);
+ g_free(entry->payload_data);
+
+ slist = slist->next;
+ g_free(entry);
+ }
+
+ /* Free the GSList elements */
+ g_slist_free(object_list->entries);
+ g_free(object_list);
+
+ /* Free the private export_object_xxx data */
+ if (eo_protocoldata_reset != NULL) eo_protocoldata_reset();
+}
+
+static gboolean
+eo_save_entry(gchar *save_as_filename, export_object_entry_t *entry, gboolean show_err)
+{
+ int to_fd;
+ gint64 bytes_left;
+ int bytes_to_write;
+ ssize_t bytes_written;
+ guint8 *ptr;
+ int err;
+
+ to_fd = ws_open(save_as_filename, O_WRONLY | O_CREAT | O_EXCL |
+ O_BINARY, 0644);
+ if(to_fd == -1) { /* An error occurred */
+ if (show_err)
+ open_failure_alert_box(save_as_filename, errno, TRUE);
+ g_free(save_as_filename);
+ return FALSE;
+ }
+
+ /*
+ * The third argument to _write() on Windows is an unsigned int,
+ * so, on Windows, that's the size of the third argument to
+ * ws_write().
+ *
+ * The third argument to write() on UN*X is a size_t, although
+ * the return value is an ssize_t, so one probably shouldn't
+ * write more than the max value of an ssize_t.
+ *
+ * In either case, there's no guarantee that a gint64 such as
+ * payload_len can be passed to ws_write(), so we write in
+ * chunks of, at most 2^31 bytes.
+ */
+ ptr = entry->payload_data;
+ bytes_left = entry->payload_len;
+ while (bytes_left != 0) {
+ if (bytes_left > 0x40000000)
+ bytes_to_write = 0x40000000;
+ else
+ bytes_to_write = (int)bytes_left;
+ bytes_written = ws_write(to_fd, ptr, bytes_to_write);
+ if(bytes_written <= 0) {
+ if (bytes_written < 0)
+ err = errno;
+ else
+ err = WTAP_ERR_SHORT_WRITE;
+ if (show_err)
+ write_failure_alert_box(save_as_filename, err);
+ ws_close(to_fd);
+ g_free(save_as_filename);
+ return FALSE;
+ }
+ bytes_left -= bytes_written;
+ ptr += bytes_written;
+ }
+ if (ws_close(to_fd) < 0) {
+ if (show_err)
+ write_failure_alert_box(save_as_filename, errno);
+ g_free(save_as_filename);
+ return FALSE;
+ }
+
+ g_free(save_as_filename);
+ return TRUE;
+}
+
+
+static void
+eo_save_clicked_cb(GtkWidget *widget _U_, gpointer arg)
+{
+ GtkWidget *save_as_w;
+ export_object_list_t *object_list = arg;
+ export_object_entry_t *entry = NULL;
+
+ entry = g_slist_nth_data(object_list->entries,
+ object_list->row_selected);
+
+ if(!entry) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK, "No object was selected for saving. Please click on an object and click save again.");
+ return;
+ }
+
+ save_as_w = file_selection_new("Wireshark: Save Object As ...",
+ FILE_SELECTION_SAVE);
+
+ gtk_window_set_transient_for(GTK_WINDOW(save_as_w),
+ GTK_WINDOW(object_list->dlg));
+
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_as_w),
+ entry->filename);
+
+ if(gtk_dialog_run(GTK_DIALOG(save_as_w)) == GTK_RESPONSE_ACCEPT)
+ eo_save_entry(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(save_as_w)), entry, TRUE);
+
+ window_destroy(save_as_w);
+}
+
+#define HINIBBLE(x) (((x) >> 4) & 0xf)
+#define LONIBBLE(x) ((x) & 0xf)
+#define HEXTOASCII(x) (((x) < 10) ? ((x) + '0') : ((x) - 10 + 'a'))
+#define MAXFILELEN 255
+
+static GString *eo_rename(GString *gstr, int dup)
+{
+ GString *gstr_tmp;
+ gchar *tmp_ptr;
+ GString *ext_str;
+
+ gstr_tmp = g_string_new("(");
+ g_string_append_printf (gstr_tmp, "%d)", dup);
+ if ( (tmp_ptr = strrchr(gstr->str, '.')) != NULL ) {
+ /* Retain the extension */
+ ext_str = g_string_new(tmp_ptr);
+ gstr = g_string_truncate(gstr, gstr->len - ext_str->len);
+ if ( gstr->len >= (MAXFILELEN - (strlen(gstr_tmp->str) + ext_str->len)) )
+ gstr = g_string_truncate(gstr, MAXFILELEN - (strlen(gstr_tmp->str) + ext_str->len));
+ gstr = g_string_append(gstr, gstr_tmp->str);
+ gstr = g_string_append(gstr, ext_str->str);
+ g_string_free(ext_str, TRUE);
+ }
+ else {
+ if ( gstr->len >= (MAXFILELEN - strlen(gstr_tmp->str)) )
+ gstr = g_string_truncate(gstr, MAXFILELEN - strlen(gstr_tmp->str));
+ gstr = g_string_append(gstr, gstr_tmp->str);
+ }
+ g_string_free(gstr_tmp, TRUE);
+ return gstr;
+}
+
+static GString *
+eo_massage_str(const gchar *in_str, gsize maxlen, int dup)
+{
+ gchar *tmp_ptr;
+ /* The characters in "reject" come from:
+ * http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx.
+ * Add to the list as necessary for other OS's.
+ */
+ const gchar *reject = "<>:\"/\\|?*"
+ "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a"
+ "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
+ "\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f";
+ GString *out_str;
+ GString *ext_str;
+
+ out_str = g_string_new("");
+
+ /* Find all disallowed characters/bytes and replace them with %xx */
+ while ( (tmp_ptr = strpbrk(in_str, reject)) != NULL ) {
+ out_str = g_string_append_len(out_str, in_str, tmp_ptr - in_str);
+ out_str = g_string_append_c(out_str, '%');
+ out_str = g_string_append_c(out_str, HEXTOASCII(HINIBBLE(*tmp_ptr)));
+ out_str = g_string_append_c(out_str, HEXTOASCII(LONIBBLE(*tmp_ptr)));
+ in_str = tmp_ptr + 1;
+ }
+ out_str = g_string_append(out_str, in_str);
+ if ( out_str->len > maxlen ) {
+ if ( (tmp_ptr = strrchr(out_str->str, '.')) != NULL ) {
+ /* Retain the extension */
+ ext_str = g_string_new(tmp_ptr);
+ out_str = g_string_truncate(out_str, maxlen - ext_str->len);
+ out_str = g_string_append(out_str, ext_str->str);
+ g_string_free(ext_str, TRUE);
+ }
+ else
+ out_str = g_string_truncate(out_str, maxlen);
+ }
+ if ( dup != 0 )
+ out_str = eo_rename(out_str, dup);
+ return out_str;
+}
+
+static const char *
+ct2ext(const char *content_type)
+{
+ /* TODO: Map the content type string to an extension string. If no match,
+ * return NULL. */
+ return content_type;
+}
+
+static void
+eo_save_all_clicked_cb(GtkWidget *widget _U_, gpointer arg)
+{
+ gchar *save_as_fullpath;
+ export_object_list_t *object_list = arg;
+ export_object_entry_t *entry;
+ GtkWidget *save_in_w;
+ GSList *slist = object_list->entries;
+ gboolean all_saved = TRUE;
+ gchar *save_in_path;
+ GString *safe_filename;
+ int count = 0;
+
+ save_in_w = file_selection_new("Wireshark: Save All Objects In ...",
+ FILE_SELECTION_CREATE_FOLDER);
+
+ gtk_window_set_transient_for(GTK_WINDOW(save_in_w),
+ GTK_WINDOW(object_list->dlg));
+
+ if (gtk_dialog_run(GTK_DIALOG(save_in_w)) == GTK_RESPONSE_ACCEPT) {
+ while (slist) {
+ entry = slist->data;
+
+ save_in_path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(save_in_w));
+ if ((strlen(save_in_path) < MAXFILELEN)) {
+ do {
+ if (entry->filename)
+ safe_filename = eo_massage_str(entry->filename,
+ MAXFILELEN - strlen(save_in_path), count);
+ else {
+ char generic_name[256];
+ const char *ext;
+ ext = ct2ext(entry->content_type);
+ g_snprintf(generic_name, sizeof(generic_name),
+ "object%u%s%s", entry->pkt_num, ext ? "." : "",
+ ext ? ext : "");
+ safe_filename = eo_massage_str(generic_name,
+ MAXFILELEN - strlen(save_in_path), count);
+ }
+ save_as_fullpath = g_build_filename(
+ save_in_path, safe_filename->str, NULL);
+ g_string_free(safe_filename, TRUE);
+ } while (g_file_test(save_as_fullpath, G_FILE_TEST_EXISTS) && ++count < 1000);
+ count = 0;
+ if (!eo_save_entry(save_as_fullpath, entry, FALSE))
+ all_saved = FALSE;
+ }
+ else
+ all_saved = FALSE;
+
+ slist = slist->next;
+ }
+ }
+
+ if (!all_saved)
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Some files could not be saved.");
+
+ window_destroy(save_in_w);
+}
+
+/* Runs at the beginning of tapping only */
+static void
+eo_reset(void *tapdata)
+{
+ export_object_list_t *object_list = tapdata;
+
+ object_list->entries = NULL;
+ object_list->iter = NULL;
+ object_list->row_selected = -1;
+
+ if (eo_protocoldata_reset != NULL) eo_protocoldata_reset();
+}
+
+static void
+eo_draw(void *tapdata)
+{
+ export_object_list_t *object_list = tapdata;
+ export_object_entry_t *eo_entry;
+
+ GSList *slist = object_list->entries;
+ GtkTreeIter new_iter;
+
+ /* Free the tree first, since we may get called more than once for the same capture
+ Not doing so caused duplicate entries and clicking them caused crashes.
+ */
+
+ gtk_tree_store_clear(object_list->store);
+
+ while(slist) {
+ eo_entry = slist->data;
+
+ gtk_tree_store_append(object_list->store, &new_iter,
+ object_list->iter);
+
+ gtk_tree_store_set(object_list->store, &new_iter,
+ EO_PKT_NUM_COLUMN, eo_entry->pkt_num,
+ EO_HOSTNAME_COLUMN, eo_entry->hostname,
+ EO_CONTENT_TYPE_COLUMN, eo_entry->content_type,
+ EO_BYTES_COLUMN, eo_entry->payload_len,
+ EO_FILENAME_COLUMN, eo_entry->filename,
+ -1);
+
+ slist = slist->next;
+ }
+}
+
+void
+export_object_window(const gchar *tapname, const gchar *name, tap_packet_cb tap_packet, eo_protocoldata_reset_cb eo_protocoldata_resetfn)
+{
+ GtkWidget *sw;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *selection;
+ GtkWidget *vbox, *bbox, *help_bt, *cancel_bt, *save_bt, *save_all_bt;
+ GString *error_msg;
+ export_object_list_t *object_list;
+ gchar *window_title;
+
+ /* Initialize the pointer to the private data clearing function */
+ eo_protocoldata_reset = eo_protocoldata_resetfn;
+
+ /* Initialize our object list structure */
+ object_list = g_malloc0(sizeof(export_object_list_t));
+
+ /* Data will be gathered via a tap callback */
+ error_msg = register_tap_listener(tapname, object_list, NULL, 0,
+ eo_reset,
+ tap_packet,
+ eo_draw);
+
+ if (error_msg) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't register %s tap: %s\n", name, error_msg->str);
+ g_string_free(error_msg, TRUE);
+ g_free(object_list);
+ return;
+ }
+
+ /* Setup our GUI window */
+ window_title = g_strdup_printf("Wireshark: %s object list", name);
+ object_list->dlg = dlg_window_new(window_title);
+ g_free(window_title);
+
+ gtk_window_set_default_size(GTK_WINDOW(object_list->dlg),
+ DEF_WIDTH, DEF_HEIGHT);
+
+ vbox = gtk_vbox_new(FALSE, 5);
+
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
+ gtk_container_add(GTK_CONTAINER(object_list->dlg), vbox);
+
+ sw = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
+ GTK_SHADOW_IN);
+
+ gtk_container_add(GTK_CONTAINER(vbox), sw);
+
+ object_list->store = gtk_tree_store_new(EO_NUM_COLUMNS,
+ G_TYPE_INT, G_TYPE_STRING,
+ /* we need a UINT64
+ (was G_TYPE_STRING, G_TYPE_INT,) */
+ G_TYPE_STRING, G_TYPE_INT64,
+ G_TYPE_STRING);
+
+ object_list->tree = tree_view_new(GTK_TREE_MODEL(object_list->store));
+ g_object_unref(G_OBJECT(object_list->store));
+
+ object_list->tree_view = GTK_TREE_VIEW(object_list->tree);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Packet num",
+ renderer,
+ "text",
+ EO_PKT_NUM_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(object_list->tree_view, column);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Hostname",
+ renderer,
+ "text",
+ EO_HOSTNAME_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(object_list->tree_view, column);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Content Type",
+ renderer,
+ "text",
+ EO_CONTENT_TYPE_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(object_list->tree_view, column);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Bytes",
+ renderer,
+ "text",
+ EO_BYTES_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(object_list->tree_view, column);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Filename",
+ renderer,
+ "text",
+ EO_FILENAME_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(object_list->tree_view, column);
+
+ gtk_container_add(GTK_CONTAINER(sw), object_list->tree);
+
+ selection = gtk_tree_view_get_selection(object_list->tree_view);
+ g_signal_connect(selection, "changed", G_CALLBACK(eo_remember_row_num), object_list);
+
+ bbox = dlg_button_row_new(GTK_STOCK_HELP, WIRESHARK_STOCK_SAVE_ALL, GTK_STOCK_SAVE_AS, GTK_STOCK_CANCEL, NULL);
+
+ /* Help button */
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_EXPORT_OBJECT_LIST);
+ gtk_widget_set_tooltip_text(help_bt, "Show help for this dialog.");
+
+ /* Save All button */
+ save_all_bt = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_SAVE_ALL);
+ g_signal_connect(save_all_bt, "clicked", G_CALLBACK(eo_save_all_clicked_cb),
+ object_list);
+ gtk_widget_set_tooltip_text(save_all_bt, "Save all listed objects with their displayed filenames.");
+
+ /* Save As button */
+ save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE_AS);
+ g_signal_connect(save_bt, "clicked", G_CALLBACK(eo_save_clicked_cb), object_list);
+ gtk_widget_set_tooltip_text(save_bt, "Saves the currently selected content to a file.");
+
+ /* Cancel button */
+ cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ gtk_widget_set_tooltip_text(cancel_bt, "Cancel this dialog.");
+
+ /* Pack the buttons into the "button box" */
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+
+ /* Setup cancel/delete/destroy signal handlers */
+ g_signal_connect(object_list->dlg, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(object_list->dlg, "destroy",
+ G_CALLBACK(eo_win_destroy_cb), object_list);
+ window_set_cancel_button(object_list->dlg, cancel_bt,
+ window_cancel_button_cb);
+
+ /* Show the window */
+ gtk_widget_show_all(object_list->dlg);
+ window_present(object_list->dlg);
+
+ cf_retap_packets(&cfile);
+}
diff --git a/ui/gtk/export_object.h b/ui/gtk/export_object.h
new file mode 100644
index 0000000000..7ecdf36b19
--- /dev/null
+++ b/ui/gtk/export_object.h
@@ -0,0 +1,68 @@
+/* export_object.h
+ * Common routines for tracking & saving objects found in streams of data
+ * Copyright 2007, Stephen Fisher (see AUTHORS file)
+ *
+ * $Id$
+ *
+ * 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 __EXPORT_OBJECT_H__
+#define __EXPORT_OBJECT_H__
+
+/* Common between protocols */
+typedef struct _export_object_list_t {
+ GSList *entries;
+ GtkWidget *tree, *dlg;
+ GtkTreeView *tree_view;
+ GtkTreeIter *iter;
+ GtkTreeStore *store;
+ gint row_selected;
+} export_object_list_t;
+
+typedef struct _export_object_entry_t {
+ guint32 pkt_num;
+ gchar *hostname;
+ gchar *content_type;
+ gchar *filename;
+ /* We need to store a 64 bit integer to hold a file length
+ (was guint payload_len;) */
+ gint64 payload_len;
+ guint8 *payload_data;
+} export_object_entry_t;
+
+/* When a protocol needs intermediate data structures to construct the
+export objects, then it must specifiy a function that cleans up all
+those data structures. This function is passed to export_object_window
+and called when tap reset or windows closes occurs. If no function is needed
+a NULL value should be passed instead */
+typedef void (*eo_protocoldata_reset_cb)(void);
+
+
+void export_object_window(const gchar *tapname, const gchar *name,
+ tap_packet_cb tap_packet,
+ eo_protocoldata_reset_cb eo_protocoldata_resetfn);
+
+/* Protocol specific */
+void eo_http_cb(GtkWidget *widget _U_, gpointer data _U_);
+void eo_dicom_cb(GtkWidget *widget _U_, gpointer data _U_);
+void eo_smb_cb(GtkWidget *widget _U_, gpointer data _U_);
+
+#endif /* __EXPORT_OBJECT_H__ */
diff --git a/ui/gtk/export_object_dicom.c b/ui/gtk/export_object_dicom.c
new file mode 100644
index 0000000000..3c7d0f4f46
--- /dev/null
+++ b/ui/gtk/export_object_dicom.c
@@ -0,0 +1,78 @@
+/* export_object_dicom.c
+ * $Id$
+ * Routines for tracking & saving objects found in DICOM streams
+ * See also: export_object.c / export_object.h for common code
+ * Copyright 2008, David Aggeler <david_aggeler@hispeed.ch>
+ *
+ * $Id$
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include <epan/dissectors/packet-dcm.h>
+
+#include <epan/packet.h>
+#include <epan/tap.h>
+
+#include "ui/gtk/export_object.h"
+
+static int
+eo_dicom_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_,
+ const void *data)
+{
+ export_object_list_t *object_list = tapdata;
+ const dicom_eo_t *eo_info = data;
+ export_object_entry_t *entry;
+
+ if (eo_info) { /* We have data waiting for us */
+ /*
+ Don't copy any data. dcm_export_create_object() is already g_malloc() the items
+ Still, the values will be freed when the export Object window is closed.
+ Therefore, strings and buffers must be copied
+ */
+ entry = g_malloc(sizeof(export_object_entry_t));
+
+ entry->pkt_num = pinfo->fd->num;
+ entry->hostname = eo_info->hostname;
+ entry->content_type = eo_info->content_type;
+ entry->filename = g_strdup(g_path_get_basename(eo_info->filename));
+ entry->payload_len = eo_info->payload_len;
+ entry->payload_data = eo_info->payload_data;
+
+ object_list->entries = g_slist_append(object_list->entries, entry);
+
+ return 1; /* State changed - window should be redrawn */
+ } else {
+ return 0; /* State unchanged - no window updates needed */
+ }
+}
+
+void
+eo_dicom_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ export_object_window("dicom_eo", "DICOM", eo_dicom_packet, NULL);
+}
diff --git a/ui/gtk/export_object_http.c b/ui/gtk/export_object_http.c
new file mode 100644
index 0000000000..5159b59f49
--- /dev/null
+++ b/ui/gtk/export_object_http.c
@@ -0,0 +1,75 @@
+/* export_object_http.c
+ * Routines for tracking & saving objects found in HTTP streams
+ * See also: export_object.c / export_object.h for common code
+ * Copyright 2007, Stephen Fisher (see AUTHORS file)
+ *
+ * $Id$
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include <epan/dissectors/packet-http.h>
+#include <epan/tap.h>
+
+#include "ui/gtk/export_object.h"
+
+
+static int
+eo_http_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_,
+ const void *data)
+{
+ export_object_list_t *object_list = tapdata;
+ const http_eo_t *eo_info = data;
+ export_object_entry_t *entry;
+
+ if(eo_info) { /* We have data waiting for us */
+ /* These values will be freed when the Export Object window
+ * is closed. */
+ entry = g_malloc(sizeof(export_object_entry_t));
+
+ entry->pkt_num = pinfo->fd->num;
+ entry->hostname = g_strdup(eo_info->hostname);
+ entry->content_type = g_strdup(eo_info->content_type);
+ entry->filename = g_strdup(g_path_get_basename(eo_info->filename));
+ entry->payload_len = eo_info->payload_len;
+ entry->payload_data = g_memdup(eo_info->payload_data,
+ eo_info->payload_len);
+
+ object_list->entries =
+ g_slist_append(object_list->entries, entry);
+
+ return 1; /* State changed - window should be redrawn */
+ } else {
+ return 0; /* State unchanged - no window updates needed */
+ }
+}
+
+void
+eo_http_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ export_object_window("http_eo", "HTTP", eo_http_packet, NULL);
+}
diff --git a/ui/gtk/export_object_smb.c b/ui/gtk/export_object_smb.c
new file mode 100644
index 0000000000..e23411e56f
--- /dev/null
+++ b/ui/gtk/export_object_smb.c
@@ -0,0 +1,423 @@
+/* export_object_smb.c
+ * Routines for tracking & saving objects (files) found in SMB streams
+ * See also: export_object.c / export_object.h for common code
+ * Initial file, prototypes and general structure initially copied
+ * from export_object_http.c
+ *
+ * Copyright 2010, David Perez & Jose Pico from TADDONG S.L.
+ *
+ * $Id$
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/dissectors/packet-smb.h>
+#include <epan/tap.h>
+
+#include "ui/gtk/export_object.h"
+
+
+/* These flags show what kind of data the object contains
+ (designed to be or'ed) */
+#define SMB_EO_CONTAINS_NOTHING 0x00
+#define SMB_EO_CONTAINS_READS 0x01
+#define SMB_EO_CONTAINS_WRITES 0x02
+#define SMB_EO_CONTAINS_READSANDWRITES 0x03
+#define LEGAL_FILENAME_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ1234567890_."
+
+static const value_string smb_eo_contains_string[]={
+ {SMB_EO_CONTAINS_NOTHING, ""},
+ {SMB_EO_CONTAINS_READS, "R"},
+ {SMB_EO_CONTAINS_WRITES, "W"},
+ {SMB_EO_CONTAINS_READSANDWRITES, "R&W"},
+ {0, NULL}
+ };
+
+
+/* This struct contains the relationship between
+ the row# in the export_object window and the file being captured;
+ the row# in this GSList will match the row# in the entry list */
+
+typedef struct _active_file {
+ guint16 tid,uid,fid;
+ guint64 file_length; /* The last free reported offset */
+ /* We treat it as the file length */
+ guint64 data_gathered; /* The actual total of data gathered */
+ guint8 flag_contains; /* What kind of data it contains */
+ GSList *free_chunk_list;
+ /* A list of virtual "holes" in the */
+ /* file stream stored in memory */
+ gboolean is_out_of_memory;
+ /* TRUE if we cannot allocate memory */
+ /* memory for this file */
+ } active_file ;
+
+/* This is the GSList that will contain all the files that we are tracking */
+static GSList *GSL_active_files = NULL;
+
+/* We define a free chunk in a file as an start offset and end offset
+ Consider a free chunk as a "hole" in a file that we are capturing */
+typedef struct _free_chunk {
+ guint64 start_offset;
+ guint64 end_offset;
+} free_chunk;
+
+/* insert_chunk function will recalculate the free_chunk_list, the data_size,
+ the end_of_file, and the data_gathered as appropriate.
+ It will also insert the data chunk that is coming in the right
+ place of the file in memory.
+ HINTS:
+ file->data_gathered contains the real data gathered independently
+ from the file length
+ file->file_length contains the length of the file in memory, i.e.,
+ the last offset captured. In most cases, the real
+ file length would be different.
+*/
+static void
+insert_chunk(active_file *file, export_object_entry_t *entry, const smb_eo_t *eo_info)
+{
+ guint nfreechunks = g_slist_length(file->free_chunk_list);
+ guint i;
+ free_chunk *current_free_chunk;
+ free_chunk *new_free_chunk;
+ guint64 chunk_offset=eo_info->smb_file_offset;
+ guint64 chunk_length=eo_info->payload_len;
+ guint64 chunk_end_offset = chunk_offset+chunk_length-1;
+ /* Size of file in memory */
+ guint64 calculated_size = chunk_offset+chunk_length;
+ gpointer dest_memory_addr;
+
+ /* Let's recalculate the file length and data gathered */
+ if (file->data_gathered==0 && nfreechunks==0) {
+ /* If this is the first entry for this file, we first
+ create an initial free chunk */
+ new_free_chunk=g_malloc(sizeof(free_chunk));
+ new_free_chunk->start_offset=0;
+ new_free_chunk->end_offset=MAX(file->file_length,chunk_end_offset+1)-1;
+ file->free_chunk_list=NULL;
+ file->free_chunk_list=g_slist_append(file->free_chunk_list,new_free_chunk);
+ nfreechunks+=1;
+ } else {
+ if (chunk_end_offset > file->file_length-1) {
+ new_free_chunk=g_malloc(sizeof(free_chunk));
+ new_free_chunk->start_offset=file->file_length;
+ new_free_chunk->end_offset=chunk_end_offset;
+ file->free_chunk_list=g_slist_append(file->free_chunk_list,new_free_chunk);
+ nfreechunks+=1;
+ }
+ }
+ file->file_length = MAX(file->file_length,chunk_end_offset+1);
+
+ for (i=0;i<nfreechunks;i++) {
+ current_free_chunk = g_slist_nth_data(file->free_chunk_list,i);
+ if (chunk_offset <= current_free_chunk->start_offset ) {
+ if (chunk_end_offset >= current_free_chunk->start_offset) {
+ if (chunk_end_offset < current_free_chunk->end_offset) {
+ file->data_gathered+=
+ (chunk_end_offset-current_free_chunk->start_offset+1);
+ current_free_chunk->start_offset=chunk_end_offset+1;
+ } else {
+ file->data_gathered+=
+ (current_free_chunk->end_offset-current_free_chunk->start_offset+1);
+ file->free_chunk_list =
+ g_slist_remove(file->free_chunk_list,current_free_chunk);
+ nfreechunks-=1;
+ if (nfreechunks==0) { /* The free chunk list is empty */
+ g_slist_free(file->free_chunk_list);
+ file->free_chunk_list=NULL;
+ break;
+ }
+ }
+ } else {
+ break;
+ }
+ } else {
+ if (chunk_offset <= current_free_chunk->end_offset) {
+ if (chunk_end_offset < current_free_chunk->end_offset) {
+ new_free_chunk=g_malloc(sizeof(free_chunk));
+ new_free_chunk->start_offset=chunk_end_offset+1;
+ new_free_chunk->end_offset=current_free_chunk->end_offset;
+ current_free_chunk->end_offset=chunk_offset-1;
+ file->free_chunk_list =
+ g_slist_insert(file->free_chunk_list,new_free_chunk,i+1);
+ file->data_gathered+=chunk_length;
+ } else {
+ file->data_gathered+=current_free_chunk->end_offset-chunk_offset+1;
+ current_free_chunk->end_offset=chunk_offset-1;
+ }
+ }
+ }
+ }
+
+ /* Now, let's insert the data chunk into memory
+ ...first, we shall be able to allocate the memory */
+ if (!entry->payload_data) {
+ /* This is a New file */
+ if (calculated_size > G_MAXSIZE) {
+ /*
+ * The argument to g_try_malloc() is
+ * a gsize, the maximum value of which is
+ * G_MAXSIZE. If the calculated size is
+ * bigger than that, we just say the attempt
+ * to allocate memory failed.
+ */
+ entry->payload_data=NULL;
+ } else {
+ entry->payload_data = g_try_malloc((gsize)calculated_size);
+ entry->payload_len=calculated_size;
+ }
+ if (!entry->payload_data) {
+ /* Memory error */
+ file->is_out_of_memory=TRUE;
+ }
+ } else {
+ /* This is an existing file in memory */
+ if (calculated_size > (guint64) entry->payload_len &&
+ !file->is_out_of_memory) {
+ /* We need more memory */
+ if (calculated_size > G_MAXSIZE) {
+ /*
+ * As for g_try_malloc(), so for
+ * g_try_realloc().
+ */
+ dest_memory_addr=NULL;
+ } else {
+ dest_memory_addr=g_try_realloc(
+ entry->payload_data,
+ (gsize)calculated_size);
+ }
+ if(!dest_memory_addr) {
+ /* Memory error */
+ file->is_out_of_memory=TRUE;
+ /* We don't have memory for this file.
+ Free the current file content from memory */
+ g_free(entry->payload_data);
+ entry->payload_data=NULL;
+ entry->payload_len=0;
+ } else {
+ entry->payload_data=dest_memory_addr;
+ entry->payload_len=calculated_size;
+ }
+ }
+ }
+ /* ...then, put the chunk of the file in the right place */
+ if(!file->is_out_of_memory) {
+ dest_memory_addr=entry->payload_data+chunk_offset;
+ g_memmove(dest_memory_addr,eo_info->payload_data,eo_info->payload_len);
+ }
+}
+
+/* We use this function to obtain the index in the GSL of a given file */
+static int
+find_incoming_file(GSList *GSL_active_files,active_file *incoming_file)
+{
+ int i,row,last;
+ active_file *in_list_file;
+
+ row=-1;
+ last=g_slist_length(GSL_active_files)-1;
+
+ /* We lookup in reverse order because it is more likely that the file
+ is one of the latest */
+ for (i=last;i>=0;i--) {
+ in_list_file=g_slist_nth_data(GSL_active_files, i);
+ /* The best-working criteria of two identical files is that the file
+ that is the same of the file that we are analyzing is the last one
+ in the list that has the same tid and the same fid */
+ /* note that we have excluded in_list_file->uid == incoming_file->uid
+ from the comparison, because a file can be opened by different
+ SMB users and it is still the same file */
+ if (in_list_file->tid == incoming_file->tid &&
+ in_list_file->fid == incoming_file->fid) {
+ row=i;
+ break;
+ }
+ }
+
+ return row;
+}
+
+/* This is the function answering to the registered tap listener call */
+static gboolean
+eo_smb_packet(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *data)
+{
+ export_object_list_t *object_list = tapdata;
+ const smb_eo_t *eo_info = data;
+
+ export_object_entry_t *entry;
+ export_object_entry_t *current_entry;
+ active_file incoming_file;
+ gint active_row;
+ active_file *new_file;
+ active_file *current_file;
+ guint8 contains;
+ gboolean is_supported_filetype;
+ gfloat percent;
+
+ gchar **aux_string_v;
+
+ /* Is this an eo_smb supported file_type? (right now we only support FILE */
+ is_supported_filetype = (eo_info->fid_type==SMB_FID_TYPE_FILE);
+
+ /* What kind of data this packet contains? */
+ switch(eo_info->cmd) {
+ case SMB_COM_READ_ANDX:
+ contains=SMB_EO_CONTAINS_READS;
+ break;
+ case SMB_COM_WRITE_ANDX:
+ contains=SMB_EO_CONTAINS_WRITES;
+ break;
+ default:
+ contains=SMB_EO_CONTAINS_NOTHING;
+ break;
+ }
+
+ /* Is this data from an already tracked file or not? */
+ incoming_file.tid=eo_info->tid;
+ incoming_file.uid=eo_info->uid;
+ incoming_file.fid=eo_info->fid;
+ active_row=find_incoming_file(GSL_active_files, &incoming_file);
+
+ if (active_row==-1) { /* This is a new-tracked file */
+ /* Construct the entry in the list of active files */
+ entry = g_malloc(sizeof(export_object_entry_t));
+ entry->payload_data=NULL;
+ entry->payload_len=0;
+ new_file = g_malloc(sizeof(active_file));
+ new_file->tid=incoming_file.tid;
+ new_file->uid=incoming_file.uid;
+ new_file->fid=incoming_file.fid;
+ new_file->file_length = eo_info->end_of_file;
+ new_file->flag_contains=contains;
+ new_file->free_chunk_list=NULL;
+ new_file->data_gathered=0;
+ new_file->is_out_of_memory=FALSE;
+ entry->pkt_num = pinfo->fd->num;
+ entry->hostname = g_strdup(eo_info->hostname);
+ if (g_str_has_prefix(eo_info->filename,"\\")) {
+ aux_string_v = g_strsplit(eo_info->filename, "\\", -1);
+ entry->filename = g_strdup(aux_string_v[g_strv_length(aux_string_v)-1]);
+ g_strfreev(aux_string_v);
+ } else {
+ entry->filename = g_strdup(eo_info->filename);
+ }
+
+ /* Insert the first chunk in the chunk list of this file */
+ if (is_supported_filetype) {
+ insert_chunk(new_file, entry, eo_info);
+ }
+
+ if(new_file->is_out_of_memory) {
+ entry->content_type =
+ g_strdup_printf("%s (%"G_GUINT64_FORMAT"?/%"G_GUINT64_FORMAT") %s [mem!!]",
+ match_strval(eo_info->fid_type, smb_fid_types),
+ new_file->data_gathered,
+ new_file->file_length,
+ match_strval(contains, smb_eo_contains_string));
+ } else {
+ if (new_file->file_length > 0) {
+ percent=(gfloat) 100*new_file->data_gathered/new_file->file_length;
+ } else {
+ percent = 0.0;
+ }
+
+ entry->content_type =
+ g_strdup_printf("%s (%"G_GUINT64_FORMAT"/%"G_GUINT64_FORMAT") %s [%5.2f%%]",
+ match_strval(eo_info->fid_type, smb_fid_types),
+ new_file->data_gathered,
+ new_file->file_length,
+ match_strval(contains, smb_eo_contains_string),
+ percent);
+ }
+
+ object_list->entries =
+ g_slist_append(object_list->entries, entry);
+ GSL_active_files =
+ g_slist_append(GSL_active_files, new_file);
+ }
+ else if (is_supported_filetype) {
+ current_file=g_slist_nth_data(GSL_active_files,active_row);
+ /* Recalculate the current file flags */
+ current_file->flag_contains=current_file->flag_contains|contains;
+ current_entry=g_slist_nth_data(object_list->entries,active_row);
+
+ insert_chunk(current_file, current_entry, eo_info);
+
+ /* Modify the current_entry object_type string */
+ if(current_file->is_out_of_memory) {
+ current_entry->content_type =
+ g_strdup_printf("%s (%"G_GUINT64_FORMAT"?/%"G_GUINT64_FORMAT") %s [mem!!]",
+ match_strval(eo_info->fid_type, smb_fid_types),
+ current_file->data_gathered,
+ current_file->file_length,
+ match_strval(current_file->flag_contains, smb_eo_contains_string));
+ } else {
+ percent=(gfloat) 100*current_file->data_gathered/current_file->file_length;
+ current_entry->content_type =
+ g_strdup_printf("%s (%"G_GUINT64_FORMAT"/%"G_GUINT64_FORMAT") %s [%5.2f%%]",
+ match_strval(eo_info->fid_type, smb_fid_types),
+ current_file->data_gathered,
+ current_file->file_length,
+ match_strval(current_file->flag_contains, smb_eo_contains_string),
+ percent);
+ }
+ }
+
+ return TRUE; /* State changed - window should be redrawn */
+}
+/* This is the eo_protocoldata_reset function that is used in the export_object module
+ to cleanup any previous private data of the export object functionality before perform
+ the eo_reset function or when the window closes */
+static void
+eo_smb_cleanup(void)
+{
+ int i,last;
+ active_file *in_list_file;
+
+ /* Free any previous data structures used in previous invocation to the
+ export_object_smb function */
+ last=g_slist_length(GSL_active_files);
+ if (GSL_active_files) {
+ for (i=last-1;i>=0;i--) {
+ in_list_file=g_slist_nth_data(GSL_active_files, i);
+ if (in_list_file->free_chunk_list) {
+ g_slist_free(in_list_file->free_chunk_list);
+ in_list_file->free_chunk_list=NULL;
+ }
+ g_free(in_list_file);
+ }
+ g_slist_free(GSL_active_files);
+ GSL_active_files=NULL;
+ }
+}
+
+void
+eo_smb_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ /* Call the export_object window */
+ export_object_window("smb_eo", "SMB", eo_smb_packet, eo_smb_cleanup);
+}
diff --git a/ui/gtk/export_sslkeys.c b/ui/gtk/export_sslkeys.c
new file mode 100644
index 0000000000..f31b6f6d63
--- /dev/null
+++ b/ui/gtk/export_sslkeys.c
@@ -0,0 +1,276 @@
+/* export_sslkeys.c
+ *
+ * $Id$
+ *
+ * Export SSL Session Keys dialog
+ * by Sake Blok <sake@euronet.nl> (20110526)
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <ctype.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#if GTK_CHECK_VERSION(3,0,0)
+# include <gdk/gdkkeysyms-compat.h>
+#endif
+#include <wsutil/file_util.h>
+
+#include <string.h>
+
+
+#include <epan/filesystem.h>
+#include <epan/packet.h>
+#include <epan/epan_dissect.h>
+#include <epan/charsets.h>
+#include <epan/prefs.h>
+#include <epan/dissectors/packet-ssl.h>
+#include <epan/dissectors/packet-ssl-utils.h>
+
+#include "../simple_dialog.h"
+#include "../isprint.h"
+#include "../alert_box.h"
+#include "../progress_dlg.h"
+#include "../ui_util.h"
+
+#include "ui/gtk/keys.h"
+#include "ui/gtk/color_utils.h"
+#include "ui/gtk/capture_file_dlg.h"
+#include "ui/gtk/packet_win.h"
+#include "ui/gtk/file_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/font_utils.h"
+#include "ui/gtk/webbrowser.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/menus.h"
+#include "ui/gtk/recent.h"
+#include "ui/gtk/export_sslkeys.h"
+
+#ifdef _WIN32
+#include <gdk/gdkwin32.h>
+#include <windows.h>
+#include "win32/file_dlg_win32.h"
+#endif
+
+static void
+ssl_export_sessions_func(gpointer key, gpointer value, gpointer user_data)
+{
+ guint i;
+ size_t offset;
+ StringInfo* sslid = (StringInfo*)key;
+ StringInfo* mastersecret = (StringInfo*)value;
+ StringInfo* keylist = (StringInfo*)user_data;
+
+ offset = strlen(keylist->data);
+
+ /*
+ * XXX - should this be a string that grows as necessary to hold
+ * everything in it?
+ */
+ g_snprintf(keylist->data+offset,(gulong)(keylist->data_len-offset),"RSA Session-ID:");
+ offset += 15;
+
+ for( i=0; i<sslid->data_len; i++) {
+ g_snprintf(keylist->data+offset,(gulong)(keylist->data_len-offset),"%.2x",sslid->data[i]&255);
+ offset += 2;
+ }
+
+ g_snprintf(keylist->data+offset,(gulong)(keylist->data_len-offset)," Master-Key:");
+ offset += 12;
+
+ for( i=0; i<mastersecret->data_len; i++) {
+ g_snprintf(keylist->data+offset,(gulong)(keylist->data_len-offset),"%.2x",mastersecret->data[i]&255);
+ offset += 2;
+ }
+
+ g_snprintf(keylist->data+offset,(gulong)(keylist->data_len-offset),"\n");
+}
+
+StringInfo*
+ssl_export_sessions(GHashTable *session_hash)
+{
+ StringInfo* keylist;
+
+ /* Output format is:
+ * "RSA Session-ID:xxxx Master-Key:yyyy\n"
+ * Where xxxx is the session ID in hex (max 64 chars)
+ * Where yyyy is the Master Key in hex (always 96 chars)
+ * So in total max 3+1+11+64+1+11+96+2 = 189 chars
+ */
+ keylist = g_malloc0(sizeof(StringInfo)+189*g_hash_table_size (session_hash));
+ keylist->data = ((guchar*)keylist+sizeof(StringInfo));
+ keylist->data_len = sizeof(StringInfo)+189*g_hash_table_size (session_hash);
+
+ g_hash_table_foreach(session_hash, ssl_export_sessions_func, (gpointer)keylist);
+
+ return keylist;
+}
+static GtkWidget *savesslkeys_dlg=NULL;
+
+static void
+savesslkeys_dlg_destroy_cb(GtkWidget *w _U_, gpointer user_data _U_)
+{
+ savesslkeys_dlg = NULL;
+}
+
+/* save the SSL Session Keys */
+static gboolean
+savesslkeys_save_clicked_cb(GtkWidget * w _U_, gpointer data _U_)
+{
+ int fd;
+ char *file;
+ StringInfo *keylist;
+
+ file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(savesslkeys_dlg));
+
+ if (test_for_directory(file) == EISDIR) {
+ /* It's a directory - set the file selection box to display that
+ directory, and leave the selection box displayed. */
+ set_last_open_dir(file);
+ g_free(file);
+ file_selection_set_current_folder(savesslkeys_dlg, get_last_open_dir());
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(savesslkeys_dlg), "");
+ return FALSE; /* do gtk_dialog_run again */
+ }
+
+ /* XXX: Must check if file name exists first */
+
+ /*
+ * Retrieve the info we need
+ */
+ keylist = ssl_export_sessions(ssl_session_hash);
+
+ if (keylist->data_len == 0 ) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "No SSL Session Keys to export!");
+ g_free(keylist);
+ g_free(file);
+ return TRUE;
+ }
+
+ fd = ws_open(file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
+ if (fd == -1) {
+ open_failure_alert_box(file, errno, TRUE);
+ g_free(keylist);
+ g_free(file);
+ return TRUE;
+ }
+ /*
+ * Thanks, Microsoft, for not using size_t for the third argument to
+ * _write(). Presumably this string will be <= 4GiB long....
+ */
+ if (ws_write(fd, keylist->data, (unsigned int)strlen(keylist->data)) < 0) {
+ write_failure_alert_box(file, errno);
+ ws_close(fd);
+ g_free(keylist);
+ g_free(file);
+ return TRUE;
+ }
+ if (ws_close(fd) < 0) {
+ write_failure_alert_box(file, errno);
+ g_free(keylist);
+ g_free(file);
+ return TRUE;
+ }
+
+ /* Get rid of the dialog box */
+ g_free(keylist);
+ g_free(file);
+ return TRUE;
+}
+
+
+/* Launch the dialog box to put up the file selection box etc */
+#ifdef _WIN32
+void
+savesslkeys_cb(GtkWidget * w _U_, gpointer data _U_)
+{
+ win32_export_sslkeys_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)));
+ return;
+}
+#else
+void
+savesslkeys_cb(GtkWidget * w _U_, gpointer data _U_)
+{
+ gchar *label;
+ GtkWidget *dlg_lb;
+ guint keylist_len;
+
+ keylist_len = g_hash_table_size(ssl_session_hash);
+ /* don't show up the dialog, if no data has to be saved */
+ if (keylist_len==0) {
+ /* shouldn't happen as the menu item should have been greyed out */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "There are no SSL Session Keys to save!");
+ return;
+ }
+
+
+ /*
+ * Build the dialog box we need.
+ */
+ savesslkeys_dlg = file_selection_new("Wireshark: Export SSL Session Keys", FILE_SELECTION_SAVE);
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(savesslkeys_dlg), TRUE);
+
+ /* label */
+ label = g_strdup_printf("Will save %u SSL Session %s to specified file.",
+ keylist_len, plurality(keylist_len, "key", "keys"));
+ dlg_lb = gtk_label_new(label);
+ g_free(label);
+ file_selection_set_extra_widget(savesslkeys_dlg, dlg_lb);
+ gtk_widget_show(dlg_lb);
+
+ g_signal_connect(savesslkeys_dlg, "destroy", G_CALLBACK(savesslkeys_dlg_destroy_cb), NULL);
+
+ /* "Run" the GtkFileChooserDialog. */
+ /* Upon exit: If "Accept" run the OK callback. */
+ /* If the OK callback returns with a FALSE status, re-run the dialog.*/
+ /* If not accept (ie: cancel) destroy the window. */
+ /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
+ /* return with a TRUE status so that the dialog window will be destroyed. */
+ /* Trying to re-run the dialog after popping up an alert box will not work */
+ /* since the user will not be able to dismiss the alert box. */
+ /* The (somewhat unfriendly) effect: the user must re-invoke the */
+ /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
+ /* */
+ /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
+ /* GtkFileChooserDialog. */
+ while (gtk_dialog_run(GTK_DIALOG(savesslkeys_dlg)) == GTK_RESPONSE_ACCEPT) {
+ if (savesslkeys_save_clicked_cb(NULL, savesslkeys_dlg)) {
+ break; /* we're done */
+ }
+ }
+ window_destroy(savesslkeys_dlg);
+}
+#endif
diff --git a/ui/gtk/export_sslkeys.h b/ui/gtk/export_sslkeys.h
new file mode 100644
index 0000000000..355f0046e4
--- /dev/null
+++ b/ui/gtk/export_sslkeys.h
@@ -0,0 +1,43 @@
+/* export_sslkeys.h
+ *
+ * $Id$
+ *
+ * Export SSL Session Keys dialog
+ * by Sake Blok <sake@euronet.nl> (20110526)
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EXPORT_SSLKEYS_H__
+#define __EXPORT_SSLKEYS_H__
+
+/** Callback for "Export SSL Session Keys" operation.
+ *
+ * @param w unused
+ * @param data unused
+ */
+extern void savesslkeys_cb(GtkWidget * w, gpointer data);
+
+/** Dump the SSL Session Keys to a StringInfo string
+ *
+ * @param session_hash contains all the SSL Session Keys
+ */
+extern StringInfo* ssl_export_sessions(GHashTable *session_hash);
+
+#endif /* __MAIN_PROTO_DRAW_H__ */
diff --git a/ui/gtk/fc_stat.c b/ui/gtk/fc_stat.c
new file mode 100644
index 0000000000..362b0e41cf
--- /dev/null
+++ b/ui/gtk/fc_stat.c
@@ -0,0 +1,231 @@
+/* fc_stat.c
+ * fc_stat 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/value_string.h>
+#include <epan/tap.h>
+#include <epan/conversation.h>
+#include <epan/dissectors/packet-scsi.h>
+#include <epan/dissectors/packet-fc.h>
+
+#include "../timestats.h"
+#include "../simple_dialog.h"
+#include "../file.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/service_response_time_table.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/main.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _fcstat_t {
+ GtkWidget *win;
+ srt_stat_table fc_srt_table;
+} fcstat_t;
+
+static void
+fcstat_set_title(fcstat_t *fc)
+{
+ char *title;
+
+ title = g_strdup_printf("Fibre Channel Service Response Time statistics: %s",
+ cf_get_display_name(&cfile));
+ gtk_window_set_title(GTK_WINDOW(fc->win), title);
+ g_free(title);
+}
+
+static void
+fcstat_reset(void *pfc)
+{
+ fcstat_t *fc=(fcstat_t *)pfc;
+
+ reset_srt_table_data(&fc->fc_srt_table);
+ fcstat_set_title(fc);
+}
+
+static int
+fcstat_packet(void *pfc, packet_info *pinfo, epan_dissect_t *edt _U_, const void *psi)
+{
+ const fc_hdr *fc=psi;
+ fcstat_t *fs=(fcstat_t *)pfc;
+
+ /* we are only interested in reply packets */
+ if(!(fc->fctl&FC_FCTL_EXCHANGE_RESPONDER)){
+ return 0;
+ }
+ /* if we havnt seen the request, just ignore it */
+ if( (!fc->itlq) || (fc->itlq->first_exchange_frame==0) ){
+ return 0;
+ }
+
+ add_srt_table_data(&fs->fc_srt_table, fc->type, &fc->itlq->fc_time, pinfo);
+
+ return 1;
+}
+
+
+
+static void
+fcstat_draw(void *pfc)
+{
+ fcstat_t *fc=(fcstat_t *)pfc;
+
+ draw_srt_table_data(&fc->fc_srt_table);
+}
+
+
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ fcstat_t *fc=(fcstat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(fc);
+ unprotect_thread_critical_region();
+
+ free_srt_table_data(&fc->fc_srt_table);
+ g_free(fc);
+}
+
+
+static void
+gtk_fcstat_init(const char *optarg, void *userdata _U_)
+{
+ fcstat_t *fc;
+ const char *filter=NULL;
+ GtkWidget *label;
+ char *filter_string;
+ GString *error_string;
+ int i;
+ GtkWidget *vbox;
+ GtkWidget *bbox;
+ GtkWidget *close_bt;
+
+ if(!strncmp(optarg,"fc,srt,",7)){
+ filter=optarg+7;
+ } else {
+ filter=NULL;
+ }
+
+ fc=g_malloc(sizeof(fcstat_t));
+
+ fc->win = dlg_window_new("fc-stat"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(fc->win), TRUE);
+
+ gtk_window_set_default_size(GTK_WINDOW(fc->win), 550, 400);
+ fcstat_set_title(fc);
+
+ vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(fc->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ label=gtk_label_new("Fibre Channel Service Response Time statistics");
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ filter_string = g_strdup_printf("Filter: %s", filter ? filter : "");
+ label=gtk_label_new(filter_string);
+ g_free(filter_string);
+ gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ label=gtk_label_new("Fibre Channel Types");
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ /* We must display TOP LEVEL Widget before calling init_srt_table() */
+ gtk_widget_show_all(fc->win);
+
+ init_srt_table(&fc->fc_srt_table, 256, vbox, NULL);
+ for(i=0;i<256;i++){
+ init_srt_table_row(&fc->fc_srt_table, i, val_to_str(i, fc_fc4_val, "Unknown(0x%02x)"));
+ }
+
+
+ error_string=register_tap_listener("fc", fc, filter, 0, fcstat_reset, fcstat_packet, fcstat_draw);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(fc);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(fc->win, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(fc->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(fc->win, "destroy", G_CALLBACK(win_destroy_cb), fc);
+
+ gtk_widget_show_all(fc->win);
+ window_present(fc->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(fc->win));
+}
+
+static tap_param fc_stat_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg fc_stat_dlg = {
+ "Fibre Channel Service Response Time statistics",
+ "fc,srt",
+ gtk_fcstat_init,
+ -1,
+ G_N_ELEMENTS(fc_stat_params),
+ fc_stat_params
+};
+
+void
+register_tap_listener_gtkfcstat(void)
+{
+ register_dfilter_stat(&fc_stat_dlg, "Fibre Channel",
+ REGISTER_STAT_GROUP_RESPONSE_TIME);
+}
+
+void fc_srt_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &fc_stat_dlg);
+}
+
diff --git a/ui/gtk/file_dlg.c b/ui/gtk/file_dlg.c
new file mode 100644
index 0000000000..0d57ddff60
--- /dev/null
+++ b/ui/gtk/file_dlg.c
@@ -0,0 +1,258 @@
+/* file_dlg.c
+ * Utilities to use when constructing file selection dialogs
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <string.h>
+#include <errno.h>
+
+#include <gtk/gtk.h>
+
+#include <wsutil/file_util.h>
+
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/file_dlg.h"
+#include "ui/gtk/keys.h"
+
+
+static gchar *last_open_dir = NULL;
+static gboolean updated_last_open_dir = FALSE;
+
+static void file_selection_browse_destroy_cb(GtkWidget *win, GtkWidget* file_te);
+
+/* Keys ... */
+#define E_FS_CALLER_PTR_KEY "fs_caller_ptr"
+
+/* Create a file selection dialog box window that belongs to Wireshark's
+ main window. */
+GtkWidget *
+file_selection_new(const gchar *title, file_selection_action_t action)
+{
+ GtkWidget *win;
+ GtkFileChooserAction gtk_action;
+#ifdef _WIN32
+ char *u3devicedocumentpath;
+#endif
+ const gchar *ok_button_text;
+
+ switch (action) {
+
+ case FILE_SELECTION_OPEN:
+ gtk_action = GTK_FILE_CHOOSER_ACTION_OPEN;
+ ok_button_text = GTK_STOCK_OPEN;
+ break;
+
+ case FILE_SELECTION_READ_BROWSE:
+ gtk_action = GTK_FILE_CHOOSER_ACTION_OPEN;
+ ok_button_text = GTK_STOCK_OK;
+ break;
+
+ case FILE_SELECTION_SAVE:
+ gtk_action = GTK_FILE_CHOOSER_ACTION_SAVE;
+ ok_button_text = GTK_STOCK_SAVE;
+ break;
+
+ case FILE_SELECTION_WRITE_BROWSE:
+ gtk_action = GTK_FILE_CHOOSER_ACTION_SAVE;
+ ok_button_text = GTK_STOCK_OK;
+ break;
+
+ case FILE_SELECTION_CREATE_FOLDER:
+ gtk_action = GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER;
+ ok_button_text = GTK_STOCK_OK;
+ break;
+
+ default:
+ g_assert_not_reached();
+ gtk_action = -1;
+ ok_button_text = NULL;
+ break;
+ }
+ win = gtk_file_chooser_dialog_new(title, GTK_WINDOW(top_level), gtk_action,
+#ifndef _WIN32
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ ok_button_text, GTK_RESPONSE_ACCEPT,
+#else
+ ok_button_text, GTK_RESPONSE_ACCEPT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+#endif
+ NULL);
+
+ /* If we've opened a file before, start out by showing the files in the directory
+ in which that file resided. */
+ if (last_open_dir)
+ file_selection_set_current_folder(win, last_open_dir);
+#ifdef _WIN32
+ else {
+ u3devicedocumentpath = getenv_utf8("U3_DEVICE_DOCUMENT_PATH");
+ if(u3devicedocumentpath != NULL)
+ file_selection_set_current_folder(win, u3devicedocumentpath);
+
+ }
+#endif
+ return win;
+}
+
+/* Set the current folder for a file selection dialog. */
+gboolean
+file_selection_set_current_folder(GtkWidget *fs, const gchar *filename)
+{
+ gboolean ret;
+ size_t filename_len = strlen(filename);
+ gchar *new_filename;
+
+ /* trim filename, so gtk_file_chooser_set_current_folder() likes it, see below */
+ if (filename[filename_len -1] == G_DIR_SEPARATOR
+#ifdef _WIN32
+ && filename_len > 3) /* e.g. "D:\" */
+#else
+ && filename_len > 1) /* e.g. "/" */
+#endif
+ {
+ new_filename = g_strdup(filename);
+ new_filename[filename_len-1] = '\0';
+ } else {
+ new_filename = g_strdup(filename);
+ }
+
+ /* this function is very pedantic about it's filename parameter */
+ /* no trailing '\' allowed, unless a win32 root dir "D:\" */
+ ret = gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fs), new_filename);
+ g_free(new_filename);
+ return ret;
+}
+
+/* Set the "extra" widget for a file selection dialog, with user-supplied
+ options. */
+void
+file_selection_set_extra_widget(GtkWidget *fs, GtkWidget *extra)
+{
+ gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(fs), extra);
+}
+
+
+/*
+ * A generic select_file routine that is intended to be connected to
+ * a Browse button on other dialog boxes. This allows the user to browse
+ * for a file and select it. We fill in the text_entry that is given to us.
+ *
+ * We display the window label specified in our args.
+ */
+void
+file_selection_browse(GtkWidget *file_bt, GtkWidget *file_te, const char *label, file_selection_action_t action)
+{
+ GtkWidget *caller = gtk_widget_get_toplevel(file_bt);
+ GtkWidget *fs;
+ gchar *f_name;
+
+ /* Has a file selection dialog box already been opened for that top-level
+ widget? */
+ fs = g_object_get_data(G_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY);
+ if (fs != NULL) {
+ /* Yes. Just re-activate that dialog box. */
+ reactivate_window(fs);
+ return;
+ }
+
+ fs = file_selection_new(label, action);
+
+ g_object_set_data(G_OBJECT(fs), PRINT_FILE_TE_KEY, file_te);
+
+ /* Set the E_FS_CALLER_PTR_KEY for the new dialog to point to our caller. */
+ g_object_set_data(G_OBJECT(fs), E_FS_CALLER_PTR_KEY, caller);
+
+ /* Set the E_FILE_SEL_DIALOG_PTR_KEY for the caller to point to us */
+ g_object_set_data(G_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY, fs);
+
+ /* Call a handler when the file selection box is destroyed, so we can inform
+ our caller, if any, that it's been destroyed. */
+ g_signal_connect(fs, "destroy", G_CALLBACK(file_selection_browse_destroy_cb),
+ file_te);
+
+ if (gtk_dialog_run(GTK_DIALOG(fs)) == GTK_RESPONSE_ACCEPT)
+ {
+ f_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
+ gtk_entry_set_text(GTK_ENTRY(file_te), f_name);
+ g_free(f_name);
+ }
+ window_destroy(fs);
+}
+
+
+static void
+file_selection_browse_destroy_cb(GtkWidget *win, GtkWidget* parent_te)
+{
+ GtkWidget *caller;
+
+ /* Get the widget that requested that we be popped up.
+ (It should arrange to destroy us if it's destroyed, so
+ that we don't get a pointer to a non-existent window here.) */
+ caller = g_object_get_data(G_OBJECT(win), E_FS_CALLER_PTR_KEY);
+
+ /* Tell it we no longer exist. */
+ g_object_set_data(G_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY, NULL);
+
+ /* Give the focus to the file text entry widget so the user can just press
+ Return to print to the file. */
+ gtk_widget_grab_focus(parent_te);
+}
+
+
+void
+set_last_open_dir(const char *dirname)
+{
+ size_t len;
+ gchar *new_last_open_dir;
+
+ if (dirname) {
+ len = strlen(dirname);
+ if (dirname[len-1] == G_DIR_SEPARATOR) {
+ new_last_open_dir = g_strconcat(dirname, NULL);
+ }
+ else {
+ new_last_open_dir = g_strconcat(dirname,
+ G_DIR_SEPARATOR_S, NULL);
+ }
+
+ if (last_open_dir == NULL ||
+ strcmp(last_open_dir, new_last_open_dir) != 0)
+ updated_last_open_dir = TRUE;
+ }
+ else {
+ new_last_open_dir = NULL;
+ if (last_open_dir != NULL)
+ updated_last_open_dir = TRUE;
+ }
+
+ g_free(last_open_dir);
+ last_open_dir = new_last_open_dir;
+}
+
+char *
+get_last_open_dir(void)
+{
+ return last_open_dir;
+}
diff --git a/ui/gtk/file_dlg.h b/ui/gtk/file_dlg.h
new file mode 100644
index 0000000000..87dd8d4d8d
--- /dev/null
+++ b/ui/gtk/file_dlg.h
@@ -0,0 +1,115 @@
+/* file_dlg.h
+ * Declarations of utilities to use when constructing file selection dialogs
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/** @defgroup filesel_dialog_group File Selection Dialogs
+ *
+ * Dialogs are specially created windows and are related to their parent windows (usually the main window).
+ * See: @ref howto_window_page for details.
+ *
+ * File selection dialogs are created using file_selection_new().
+ *
+ * - "Browse" file_selection_browse()
+ * - "Open Capture File" file_open_cmd()
+ * - "Save Capture File As" file_save_as_cmd()
+ * - "Import Color Filters" file_color_import_cmd_cb()
+ * - "Export Color Filters" file_color_export_cmd_cb()
+ * - "Save TCP Follow Stream As" follow_save_as_cmd_cb()
+ * - "Export Selected Packet Bytes" savehex_cb()
+ * - "Save Data As CSV" save_csv_as_cb()
+ * - "Save Payload As ..." on_save_bt_clicked()
+ * - "Save selected stream in rtpdump" rtpstream_on_save()
+ *
+ */
+
+/** @file
+ * Utilities for file selection dialog boxes. Depending on the window
+ * functions in gui_utils.h, see: @ref howto_window_page for details.
+ * @ingroup filesel_dialog_group
+ */
+
+#ifndef __FILE_DLG_H__
+#define __FILE_DLG_H__
+
+/** the action a file selection is designed for */
+typedef enum {
+ FILE_SELECTION_OPEN, /**< open a file */
+ FILE_SELECTION_READ_BROWSE, /**< browse for a file to read */
+ FILE_SELECTION_SAVE, /**< save/export a file */
+ FILE_SELECTION_WRITE_BROWSE, /**< browse for a file to write to */
+ FILE_SELECTION_CREATE_FOLDER /**< browse for a dir. to save in */
+} file_selection_action_t;
+
+/** Create a file selection dialog box window that belongs to Wireshark's
+ * main window. See window_new() for usage.
+ *
+ * @param title the title for the new file selection dialog
+ * @param action the desired action
+ * @return the newly created file selection dialog
+ */
+extern GtkWidget *file_selection_new(const gchar *title, file_selection_action_t action);
+
+/** Set the current folder for a file selection dialog.
+ *
+ * @param fs the file selection dialog from file_selection_new()
+ * @param filename the folder to set
+ * @return TRUE if the folder could be changed successfully
+ */
+extern gboolean file_selection_set_current_folder(GtkWidget *fs, const gchar *filename);
+
+/** Set the "extra" widget for a file selection dialog. This is needed to support
+ * user-supplied options.
+ *
+ * @param fs the file selection dialog from file_selection_new()
+ * @param extra the widget to set
+ */
+extern void file_selection_set_extra_widget(GtkWidget *fs, GtkWidget *extra);
+
+/** The function file_selection_browse() will g_object_set_data() itself on it's parent window.
+ * When destroying the parent window, it can close the corresponding file selection. */
+#define E_FILE_SEL_DIALOG_PTR_KEY "file_sel_dialog_ptr"
+
+/** Browse the files and fill in the associated text entry.
+ *
+ * @param file_bt the button that called us (to get the toplevel widget)
+ * @param file_te the GtkEntry the dialog will have to fill in the filename
+ * @param title the title for the file selection dialog
+ * @param action the desired action
+ */
+extern void
+file_selection_browse(GtkWidget *file_bt, GtkWidget *file_te, const char *title, file_selection_action_t action);
+
+/** Get the latest opened directory.
+ *
+ * @return the dirname
+ */
+extern char *get_last_open_dir(void);
+
+/** Set the latest opened directory.
+ * Will already be done when using file_selection_new().
+ *
+ * @param dirname the dirname
+ */
+extern void set_last_open_dir(const char *dirname);
+
+#endif
diff --git a/ui/gtk/file_import_dlg.c b/ui/gtk/file_import_dlg.c
new file mode 100644
index 0000000000..b5a77358a6
--- /dev/null
+++ b/ui/gtk/file_import_dlg.c
@@ -0,0 +1,1202 @@
+/* file_import_dlg.c
+ * Dialog to setup for import of a text file, like text2pcap
+ * November 2010, Jaap Keuter <jaap.keuter@xs4all.nl>
+ *
+ * $Id$
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <ctype.h>
+#include <string.h>
+
+#include "globals.h"
+#include "wtap.h"
+#include "pcap-encap.h"
+
+#include <epan/prefs.h>
+
+#include <gtk/gtk.h>
+
+#include "filters.h"
+#include "simple_dialog.h"
+#include "alert_box.h"
+
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/file_dlg.h"
+#include "ui/gtk/capture_file_dlg.h"
+#include "ui/gtk/help_dlg.h"
+
+#include "ui/gtk/file_import_dlg.h"
+#include "ui/gtk/text_import.h"
+#include "ui/gtk/text_import_scanner.h"
+
+#include "file.h"
+#include "wsutil/file_util.h"
+#include "tempfile.h"
+
+#define INPUT_FRM_KEY "input_frame"
+
+#define INPUT_FILENAME_TE_KEY "input_filename_text"
+
+#define INPUT_OFFSET_HEX_RB_KEY "input_offset_hex_radio"
+#define INPUT_OFFSET_OCT_RB_KEY "input_offset_oct_radio"
+#define INPUT_OFFSET_DEC_RB_KEY "input_offset_dec_radio"
+
+#define INPUT_DATETIME_CB_KEY "input_datetime_checkbox"
+#define INPUT_TIMEFMT_LBL_KEY "input_timeformat_label"
+#define INPUT_TIMEFMT_TE_KEY "input_timeformat_entry"
+
+#define IMPORT_FRM_KEY "import_frame"
+#define IMPORT_ENCAP_CO_KEY "import_encap_combo"
+
+#define IMPORT_HEADER_FRM_KEY "import_header_frame"
+#define IMPORT_HEADER_CB_KEY "import_header_checkbox"
+#define IMPORT_HEADER_ETH_RB_KEY "import_header_ethernet_radio"
+#define IMPORT_HEADER_ETYPE_LBL_KEY "import_header_etype_label"
+#define IMPORT_HEADER_ETYPE_TE_KEY "import_header_etype_text"
+#define IMPORT_HEADER_IPV4_RB_KEY "import_header_ipv4_radio"
+#define IMPORT_HEADER_PROT_LBL_KEY "import_header_prot_label"
+#define IMPORT_HEADER_PROT_TE_KEY "import_header_prot_text"
+#define IMPORT_HEADER_UDP_RB_KEY "import_header_udp_radio"
+#define IMPORT_HEADER_SRC_PORT_LBL_KEY "import_header_src_port_label"
+#define IMPORT_HEADER_SRC_PORT_TE_KEY "import_header_src_port_text"
+#define IMPORT_HEADER_TCP_RB_KEY "import_header_tcp_radio"
+#define IMPORT_HEADER_DST_PORT_LBL_KEY "import_header_dst_port_label"
+#define IMPORT_HEADER_DST_PORT_TE_KEY "import_header_dst_port_text"
+#define IMPORT_HEADER_SCTP_RB_KEY "import_header_sctp_radio"
+#define IMPORT_HEADER_TAG_LBL_KEY "import_header_tag_label"
+#define IMPORT_HEADER_TAG_TE_KEY "import_header_tag_text"
+#define IMPORT_HEADER_SCTP_D_RB_KEY "import_header_sctp_data_radio"
+#define IMPORT_HEADER_PPI_LBL_KEY "import_header_ppi_label"
+#define IMPORT_HEADER_PPI_TE_KEY "import_header_ppi_text"
+
+#define IMPORT_FRAME_LENGTH_TE_KEY "import_frame_length_text"
+
+static GtkWidget *file_import_dlg_w = NULL;
+static GtkListStore *encap_list_store = NULL;
+
+/*****************************************************************************/
+
+static void
+file_import_dlg_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+ file_import_dlg_w = NULL;
+}
+
+/*****************************************************************************/
+
+static void
+browse_file_cb(GtkWidget *browse_bt, GtkWidget *filename_te)
+{
+ file_selection_browse(browse_bt, filename_te, "Wireshark: Import from Text",
+ FILE_SELECTION_READ_BROWSE);
+}
+
+static void
+timefmt_cb_toggle(GtkWidget *widget, gpointer data _U_)
+{
+ GtkWidget *timefmt_lbl, *timefmt_te;
+ gboolean apply_fmt;
+
+ timefmt_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(widget), INPUT_TIMEFMT_LBL_KEY));
+ timefmt_te = GTK_WIDGET(g_object_get_data(G_OBJECT(widget), INPUT_TIMEFMT_TE_KEY));
+
+ apply_fmt = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+ gtk_widget_set_sensitive(timefmt_lbl, apply_fmt);
+ gtk_widget_set_sensitive(timefmt_te, apply_fmt);
+}
+
+/*****************************************************************************/
+static void
+create_encap_list_store(void)
+{
+ GtkTreeIter iter;
+ gint encap;
+ const gchar *name;
+
+ encap_list_store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_UINT);
+
+ /* Scan all Wiretap encapsulation types */
+ for (encap = 1; encap < wtap_get_num_encap_types(); encap++)
+ {
+ /* Check if we can write it to a PCAP file */
+ if ((wtap_wtap_encap_to_pcap_encap(encap) > 0) &&
+ /*
+ * Exclude wtap encapsulations that require a pseudo header,
+ * because we won't setup one from the text we import and
+ * wiretap doesn't allow us to write 'raw' frames
+ */
+ !((encap == WTAP_ENCAP_ATM_PDUS) ||
+ (encap == WTAP_ENCAP_IRDA) ||
+ (encap == WTAP_ENCAP_MTP2_WITH_PHDR) ||
+ (encap == WTAP_ENCAP_LINUX_LAPD) ||
+ (encap == WTAP_ENCAP_SITA) ||
+ (encap == WTAP_ENCAP_ERF) ||
+ (encap == WTAP_ENCAP_I2C) ||
+ (encap == WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR) ||
+ (encap == WTAP_ENCAP_PPP_WITH_PHDR)
+ ) )
+ {
+ /* If it has got a name */
+ if ((name = wtap_encap_string(encap)))
+ {
+ gtk_list_store_append(encap_list_store, &iter);
+ gtk_list_store_set(encap_list_store, &iter, 0, name, 1, encap, -1);
+ }
+ }
+ }
+
+}
+
+static GtkWidget *
+fill_encap_combo(void)
+{
+ GtkWidget *encap_co = NULL;
+ GtkCellRenderer *cell;
+
+ encap_co = gtk_combo_box_new_with_model(GTK_TREE_MODEL(encap_list_store));
+ cell = gtk_cell_renderer_text_new();
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(encap_co), cell, TRUE);
+ gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(encap_co), cell, "text", 0, NULL);
+
+ return encap_co;
+}
+
+static void header_frm_child_set(GtkWidget *widget, gpointer data);
+
+static void
+encap_co_changed(GtkComboBox *widget, gpointer data)
+{
+ GtkTreeIter iter;
+ gboolean result;
+ GtkWidget *header_cb;
+
+ result = gtk_combo_box_get_active_iter(widget, &iter);
+
+ if (result)
+ {
+ guint encap;
+ GtkTreeModel *model = gtk_combo_box_get_model(widget);
+ gtk_tree_model_get(model, &iter, 1, &encap, -1);
+
+ if (encap != WTAP_ENCAP_ETHERNET)
+ result = FALSE;
+ }
+
+ if (result)
+ {
+ header_cb = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_CB_KEY));
+ g_signal_emit_by_name(G_OBJECT(header_cb), "toggled", data);
+ } else {
+ gtk_container_foreach(GTK_CONTAINER(data), header_frm_child_set, GUINT_TO_POINTER(result));
+ }
+}
+
+/*****************************************************************************/
+
+static void
+header_frm_child_set(GtkWidget *widget, gpointer data)
+{
+ gtk_widget_set_sensitive(widget, GPOINTER_TO_UINT(data));
+}
+
+static void
+header_cb_toggle(GtkWidget *widget, gpointer data)
+{
+ gtk_container_foreach(GTK_CONTAINER(data), header_frm_child_set,
+ GUINT_TO_POINTER(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))));
+ /* The frame's checkbox must stay sensitive, of course... */
+ gtk_widget_set_sensitive(widget, TRUE);
+}
+
+/*
+ * Header radio button toggle handlers
+ */
+static void
+header_eth_rb_toggle(GtkWidget *widget, gpointer data)
+{
+ GtkWidget *etype_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_ETYPE_LBL_KEY));
+ GtkWidget *etype_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_ETYPE_TE_KEY));
+ GtkWidget *prot_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PROT_LBL_KEY));
+ GtkWidget *prot_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PROT_TE_KEY));
+ GtkWidget *src_port_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_SRC_PORT_LBL_KEY));
+ GtkWidget *src_port_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_SRC_PORT_TE_KEY));
+ GtkWidget *dst_port_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_DST_PORT_LBL_KEY));
+ GtkWidget *dst_port_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_DST_PORT_TE_KEY));
+ GtkWidget *tag_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_TAG_LBL_KEY));
+ GtkWidget *tag_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_TAG_TE_KEY));
+ GtkWidget *ppi_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PPI_LBL_KEY));
+ GtkWidget *ppi_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PPI_TE_KEY));
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
+ {
+ gtk_widget_set_sensitive(etype_lbl, TRUE);
+ gtk_widget_set_sensitive(etype_te, TRUE);
+ gtk_widget_set_sensitive(prot_lbl, FALSE);
+ gtk_widget_set_sensitive(prot_te, FALSE);
+ gtk_widget_set_sensitive(src_port_lbl, FALSE);
+ gtk_widget_set_sensitive(src_port_te, FALSE);
+ gtk_widget_set_sensitive(dst_port_lbl, FALSE);
+ gtk_widget_set_sensitive(dst_port_te, FALSE);
+ gtk_widget_set_sensitive(tag_lbl, FALSE);
+ gtk_widget_set_sensitive(tag_te, FALSE);
+ gtk_widget_set_sensitive(ppi_lbl, FALSE);
+ gtk_widget_set_sensitive(ppi_te, FALSE);
+ }
+ else
+ {
+ gtk_widget_set_sensitive(etype_lbl, FALSE);
+ gtk_widget_set_sensitive(etype_te, FALSE);
+ }
+}
+
+static void
+header_ipv4_rb_toggle(GtkWidget *widget, gpointer data)
+{
+ GtkWidget *etype_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_ETYPE_LBL_KEY));
+ GtkWidget *etype_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_ETYPE_TE_KEY));
+ GtkWidget *prot_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PROT_LBL_KEY));
+ GtkWidget *prot_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PROT_TE_KEY));
+ GtkWidget *src_port_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_SRC_PORT_LBL_KEY));
+ GtkWidget *src_port_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_SRC_PORT_TE_KEY));
+ GtkWidget *dst_port_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_DST_PORT_LBL_KEY));
+ GtkWidget *dst_port_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_DST_PORT_TE_KEY));
+ GtkWidget *tag_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_TAG_LBL_KEY));
+ GtkWidget *tag_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_TAG_TE_KEY));
+ GtkWidget *ppi_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PPI_LBL_KEY));
+ GtkWidget *ppi_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PPI_TE_KEY));
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
+ {
+ gtk_widget_set_sensitive(etype_lbl, FALSE);
+ gtk_widget_set_sensitive(etype_te, FALSE);
+ gtk_widget_set_sensitive(prot_lbl, TRUE);
+ gtk_widget_set_sensitive(prot_te, TRUE);
+ gtk_widget_set_sensitive(src_port_lbl, FALSE);
+ gtk_widget_set_sensitive(src_port_te, FALSE);
+ gtk_widget_set_sensitive(dst_port_lbl, FALSE);
+ gtk_widget_set_sensitive(dst_port_te, FALSE);
+ gtk_widget_set_sensitive(tag_lbl, FALSE);
+ gtk_widget_set_sensitive(tag_te, FALSE);
+ gtk_widget_set_sensitive(ppi_lbl, FALSE);
+ gtk_widget_set_sensitive(ppi_te, FALSE);
+ }
+ else
+ {
+ gtk_widget_set_sensitive(prot_lbl, FALSE);
+ gtk_widget_set_sensitive(prot_te, FALSE);
+ }
+}
+
+static void
+header_udp_rb_toggle(GtkWidget *widget, gpointer data)
+{
+ GtkWidget *etype_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_ETYPE_LBL_KEY));
+ GtkWidget *etype_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_ETYPE_TE_KEY));
+ GtkWidget *prot_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PROT_LBL_KEY));
+ GtkWidget *prot_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PROT_TE_KEY));
+ GtkWidget *src_port_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_SRC_PORT_LBL_KEY));
+ GtkWidget *src_port_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_SRC_PORT_TE_KEY));
+ GtkWidget *dst_port_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_DST_PORT_LBL_KEY));
+ GtkWidget *dst_port_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_DST_PORT_TE_KEY));
+ GtkWidget *tag_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_TAG_LBL_KEY));
+ GtkWidget *tag_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_TAG_TE_KEY));
+ GtkWidget *ppi_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PPI_LBL_KEY));
+ GtkWidget *ppi_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PPI_TE_KEY));
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
+ {
+ gtk_widget_set_sensitive(etype_lbl, FALSE);
+ gtk_widget_set_sensitive(etype_te, FALSE);
+ gtk_widget_set_sensitive(prot_lbl, FALSE);
+ gtk_widget_set_sensitive(prot_te, FALSE);
+ gtk_widget_set_sensitive(src_port_lbl, TRUE);
+ gtk_widget_set_sensitive(src_port_te, TRUE);
+ gtk_widget_set_sensitive(dst_port_lbl, TRUE);
+ gtk_widget_set_sensitive(dst_port_te, TRUE);
+ gtk_widget_set_sensitive(tag_lbl, FALSE);
+ gtk_widget_set_sensitive(tag_te, FALSE);
+ gtk_widget_set_sensitive(ppi_lbl, FALSE);
+ gtk_widget_set_sensitive(ppi_te, FALSE);
+ }
+ else
+ {
+ gtk_widget_set_sensitive(src_port_lbl, FALSE);
+ gtk_widget_set_sensitive(src_port_te, FALSE);
+ gtk_widget_set_sensitive(dst_port_lbl, FALSE);
+ gtk_widget_set_sensitive(dst_port_te, FALSE);
+ }
+}
+
+static void
+header_tcp_rb_toggle(GtkWidget *widget, gpointer data)
+{
+ GtkWidget *etype_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_ETYPE_LBL_KEY));
+ GtkWidget *etype_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_ETYPE_TE_KEY));
+ GtkWidget *prot_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PROT_LBL_KEY));
+ GtkWidget *prot_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PROT_TE_KEY));
+ GtkWidget *src_port_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_SRC_PORT_LBL_KEY));
+ GtkWidget *src_port_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_SRC_PORT_TE_KEY));
+ GtkWidget *dst_port_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_DST_PORT_LBL_KEY));
+ GtkWidget *dst_port_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_DST_PORT_TE_KEY));
+ GtkWidget *tag_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_TAG_LBL_KEY));
+ GtkWidget *tag_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_TAG_TE_KEY));
+ GtkWidget *ppi_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PPI_LBL_KEY));
+ GtkWidget *ppi_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PPI_TE_KEY));
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
+ {
+ gtk_widget_set_sensitive(etype_lbl, FALSE);
+ gtk_widget_set_sensitive(etype_te, FALSE);
+ gtk_widget_set_sensitive(prot_lbl, FALSE);
+ gtk_widget_set_sensitive(prot_te, FALSE);
+ gtk_widget_set_sensitive(src_port_lbl, TRUE);
+ gtk_widget_set_sensitive(src_port_te, TRUE);
+ gtk_widget_set_sensitive(dst_port_lbl, TRUE);
+ gtk_widget_set_sensitive(dst_port_te, TRUE);
+ gtk_widget_set_sensitive(tag_lbl, FALSE);
+ gtk_widget_set_sensitive(tag_te, FALSE);
+ gtk_widget_set_sensitive(ppi_lbl, FALSE);
+ gtk_widget_set_sensitive(ppi_te, FALSE);
+ }
+ else
+ {
+ gtk_widget_set_sensitive(src_port_lbl, FALSE);
+ gtk_widget_set_sensitive(src_port_te, FALSE);
+ gtk_widget_set_sensitive(dst_port_lbl, FALSE);
+ gtk_widget_set_sensitive(dst_port_te, FALSE);
+ }
+}
+
+static void
+header_sctp_rb_toggle(GtkWidget *widget, gpointer data)
+{
+ GtkWidget *etype_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_ETYPE_LBL_KEY));
+ GtkWidget *etype_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_ETYPE_TE_KEY));
+ GtkWidget *prot_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PROT_LBL_KEY));
+ GtkWidget *prot_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PROT_TE_KEY));
+ GtkWidget *src_port_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_SRC_PORT_LBL_KEY));
+ GtkWidget *src_port_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_SRC_PORT_TE_KEY));
+ GtkWidget *dst_port_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_DST_PORT_LBL_KEY));
+ GtkWidget *dst_port_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_DST_PORT_TE_KEY));
+ GtkWidget *tag_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_TAG_LBL_KEY));
+ GtkWidget *tag_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_TAG_TE_KEY));
+ GtkWidget *ppi_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PPI_LBL_KEY));
+ GtkWidget *ppi_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PPI_TE_KEY));
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
+ {
+ gtk_widget_set_sensitive(etype_lbl, FALSE);
+ gtk_widget_set_sensitive(etype_te, FALSE);
+ gtk_widget_set_sensitive(prot_lbl, FALSE);
+ gtk_widget_set_sensitive(prot_te, FALSE);
+ gtk_widget_set_sensitive(src_port_lbl, TRUE);
+ gtk_widget_set_sensitive(src_port_te, TRUE);
+ gtk_widget_set_sensitive(dst_port_lbl, TRUE);
+ gtk_widget_set_sensitive(dst_port_te, TRUE);
+ gtk_widget_set_sensitive(tag_lbl, TRUE);
+ gtk_widget_set_sensitive(tag_te, TRUE);
+ gtk_widget_set_sensitive(ppi_lbl, FALSE);
+ gtk_widget_set_sensitive(ppi_te, FALSE);
+ }
+ else
+ {
+ gtk_widget_set_sensitive(src_port_lbl, FALSE);
+ gtk_widget_set_sensitive(src_port_te, FALSE);
+ gtk_widget_set_sensitive(dst_port_lbl, FALSE);
+ gtk_widget_set_sensitive(dst_port_te, FALSE);
+ gtk_widget_set_sensitive(tag_lbl, FALSE);
+ gtk_widget_set_sensitive(tag_te, FALSE);
+ }
+}
+
+static void
+header_sctp_data_rb_toggle(GtkWidget *widget, gpointer data)
+{
+ GtkWidget *etype_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_ETYPE_LBL_KEY));
+ GtkWidget *etype_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_ETYPE_TE_KEY));
+ GtkWidget *prot_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PROT_LBL_KEY));
+ GtkWidget *prot_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PROT_TE_KEY));
+ GtkWidget *src_port_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_SRC_PORT_LBL_KEY));
+ GtkWidget *src_port_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_SRC_PORT_TE_KEY));
+ GtkWidget *dst_port_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_DST_PORT_LBL_KEY));
+ GtkWidget *dst_port_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_DST_PORT_TE_KEY));
+ GtkWidget *tag_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_TAG_LBL_KEY));
+ GtkWidget *tag_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_TAG_TE_KEY));
+ GtkWidget *ppi_lbl = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PPI_LBL_KEY));
+ GtkWidget *ppi_te = GTK_WIDGET(g_object_get_data(G_OBJECT(data), IMPORT_HEADER_PPI_TE_KEY));
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
+ {
+ gtk_widget_set_sensitive(etype_lbl, FALSE);
+ gtk_widget_set_sensitive(etype_te, FALSE);
+ gtk_widget_set_sensitive(prot_lbl, FALSE);
+ gtk_widget_set_sensitive(prot_te, FALSE);
+ gtk_widget_set_sensitive(src_port_lbl, TRUE);
+ gtk_widget_set_sensitive(src_port_te, TRUE);
+ gtk_widget_set_sensitive(dst_port_lbl, TRUE);
+ gtk_widget_set_sensitive(dst_port_te, TRUE);
+ gtk_widget_set_sensitive(tag_lbl, FALSE);
+ gtk_widget_set_sensitive(tag_te, FALSE);
+ gtk_widget_set_sensitive(ppi_lbl, TRUE);
+ gtk_widget_set_sensitive(ppi_te, TRUE);
+ }
+ else
+ {
+ gtk_widget_set_sensitive(src_port_lbl, FALSE);
+ gtk_widget_set_sensitive(src_port_te, FALSE);
+ gtk_widget_set_sensitive(dst_port_lbl, FALSE);
+ gtk_widget_set_sensitive(dst_port_te, FALSE);
+ gtk_widget_set_sensitive(ppi_lbl, FALSE);
+ gtk_widget_set_sensitive(ppi_te, FALSE);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+file_import_open(text_import_info_t *info)
+{
+ int import_file_fd;
+ char *tmpname, *capfile_name = NULL;
+ int err;
+
+ /* Choose a random name for the temporary import buffer */
+ import_file_fd = create_tempfile(&tmpname, "import");
+ capfile_name = g_strdup(tmpname);
+
+ info->wdh = wtap_dump_fdopen(import_file_fd, WTAP_FILE_PCAP, info->encapsulation, info->max_frame_length, FALSE, &err);
+ if (info->wdh == NULL) {
+ open_failure_alert_box(capfile_name, err, TRUE);
+ fclose(info->import_text_file);
+ goto end;
+ }
+
+ text_import_setup(info);
+
+ text_importin = info->import_text_file;
+
+ text_importlex();
+
+ text_import_cleanup();
+
+ if (fclose(info->import_text_file))
+ {
+ read_failure_alert_box(info->import_text_filename, errno);
+ }
+
+ if (!wtap_dump_close(info->wdh, &err))
+ {
+ write_failure_alert_box(capfile_name, err);
+ }
+
+ if (cf_open(&cfile, capfile_name, TRUE /* temporary file */, &err) != CF_OK)
+ {
+ open_failure_alert_box(capfile_name, err, FALSE);
+ goto end;
+ }
+
+ switch (cf_read(&cfile, FALSE)) {
+ case CF_READ_OK:
+ case CF_READ_ERROR:
+ /* Just because we got an error, that doesn't mean we were unable
+ to read any of the file; we handle what we could get from the
+ file. */
+ break;
+
+ case CF_READ_ABORTED:
+ /* The user bailed out of re-reading the capture file; the
+ capture file has been closed - just free the capture file name
+ string and return (without changing the last containing
+ directory). */
+ break;
+ }
+
+end:
+ g_free(info->import_text_filename);
+ g_free(info->date_timestamp_format);
+ g_free(info);
+ g_free(capfile_name);
+ window_destroy(file_import_dlg_w);
+}
+
+static text_import_info_t *
+setup_file_import(GtkWidget *main_w)
+{
+ GtkWidget *input_frm, *import_frm;
+
+ text_import_info_t *text_import_info = g_malloc0(sizeof(text_import_info_t));
+
+ /* Retrieve the input and import settings from the dialog */
+
+ /* First the main components */
+ input_frm = GTK_WIDGET(g_object_get_data(G_OBJECT(main_w), INPUT_FRM_KEY));
+ import_frm = GTK_WIDGET(g_object_get_data(G_OBJECT(main_w), IMPORT_FRM_KEY));
+
+ /* Then the input frame controls of interest */
+ {
+ GtkWidget *filename_te = GTK_WIDGET(g_object_get_data(G_OBJECT(input_frm), INPUT_FILENAME_TE_KEY));
+ GtkWidget *offset_hex_rb = GTK_WIDGET(g_object_get_data(G_OBJECT(input_frm), INPUT_OFFSET_HEX_RB_KEY));
+ GtkWidget *offset_oct_rb = GTK_WIDGET(g_object_get_data(G_OBJECT(input_frm), INPUT_OFFSET_OCT_RB_KEY));
+ GtkWidget *offset_dec_rb = GTK_WIDGET(g_object_get_data(G_OBJECT(input_frm), INPUT_OFFSET_DEC_RB_KEY));
+ GtkWidget *timefmt_cb = GTK_WIDGET(g_object_get_data(G_OBJECT(input_frm), INPUT_DATETIME_CB_KEY));
+ GtkWidget *timefmt_te = GTK_WIDGET(g_object_get_data(G_OBJECT(input_frm), INPUT_TIMEFMT_TE_KEY));
+
+ text_import_info->import_text_filename = g_strdup(gtk_entry_get_text(GTK_ENTRY(filename_te)));
+
+ /* Try to open the input file */
+ text_import_info->import_text_file = ws_fopen(text_import_info->import_text_filename, "rb");
+ if (!text_import_info->import_text_file) {
+ open_failure_alert_box(text_import_info->import_text_filename, errno, FALSE);
+ g_free(text_import_info->import_text_filename);
+ g_free(text_import_info->date_timestamp_format);
+ g_free(text_import_info);
+ return NULL;
+ }
+
+ text_import_info->offset_type =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(offset_hex_rb)) ? OFFSET_HEX :
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(offset_oct_rb)) ? OFFSET_OCT :
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(offset_dec_rb)) ? OFFSET_DEC :
+ 0;
+ text_import_info->date_timestamp = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(timefmt_cb));
+ text_import_info->date_timestamp_format = g_strdup(gtk_entry_get_text(GTK_ENTRY(timefmt_te)));
+ }
+
+ /* Then the import frame controls of interest */
+ {
+ GtkWidget *encap_co = GTK_WIDGET(g_object_get_data(G_OBJECT(import_frm), IMPORT_ENCAP_CO_KEY));
+ GtkWidget *header_frm = GTK_WIDGET(g_object_get_data(G_OBJECT(import_frm), IMPORT_HEADER_FRM_KEY));
+ GtkWidget *framelen_te = GTK_WIDGET(g_object_get_data(G_OBJECT(import_frm), IMPORT_FRAME_LENGTH_TE_KEY));
+
+ /* Then the header frame controls of interest */
+ GtkWidget *header_cb = GTK_WIDGET(g_object_get_data(G_OBJECT(header_frm), IMPORT_HEADER_CB_KEY));
+
+ GtkWidget *header_eth_rb = GTK_WIDGET(g_object_get_data(G_OBJECT(header_frm), IMPORT_HEADER_ETH_RB_KEY));
+ GtkWidget *header_ipv4_rb = GTK_WIDGET(g_object_get_data(G_OBJECT(header_frm), IMPORT_HEADER_IPV4_RB_KEY));
+ GtkWidget *header_udp_rb = GTK_WIDGET(g_object_get_data(G_OBJECT(header_frm), IMPORT_HEADER_UDP_RB_KEY));
+ GtkWidget *header_tcp_rb = GTK_WIDGET(g_object_get_data(G_OBJECT(header_frm), IMPORT_HEADER_TCP_RB_KEY));
+ GtkWidget *header_sctp_rb = GTK_WIDGET(g_object_get_data(G_OBJECT(header_frm), IMPORT_HEADER_SCTP_RB_KEY));
+ GtkWidget *header_sctp_data_rb = GTK_WIDGET(g_object_get_data(G_OBJECT(header_frm), IMPORT_HEADER_SCTP_D_RB_KEY));
+
+ GtkWidget *etype_te = GTK_WIDGET(g_object_get_data(G_OBJECT(header_frm), IMPORT_HEADER_ETYPE_TE_KEY));
+ GtkWidget *protocol_te = GTK_WIDGET(g_object_get_data(G_OBJECT(header_frm), IMPORT_HEADER_PROT_TE_KEY));
+ GtkWidget *src_port_te = GTK_WIDGET(g_object_get_data(G_OBJECT(header_frm), IMPORT_HEADER_SRC_PORT_TE_KEY));
+ GtkWidget *dst_port_te = GTK_WIDGET(g_object_get_data(G_OBJECT(header_frm), IMPORT_HEADER_DST_PORT_TE_KEY));
+ GtkWidget *tag_te = GTK_WIDGET(g_object_get_data(G_OBJECT(header_frm), IMPORT_HEADER_TAG_TE_KEY));
+ GtkWidget *ppi_te = GTK_WIDGET(g_object_get_data(G_OBJECT(header_frm), IMPORT_HEADER_PPI_TE_KEY));
+
+ GtkTreeIter iter;
+
+ if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(encap_co), &iter))
+ {
+ GtkTreeModel *model = gtk_combo_box_get_model(GTK_COMBO_BOX(encap_co));
+ gtk_tree_model_get(model, &iter, 1, &text_import_info->encapsulation, -1);
+ }
+
+ if ((text_import_info->encapsulation == WTAP_ENCAP_ETHERNET) &&
+ (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(header_cb))))
+ {
+ text_import_info->dummy_header_type =
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(header_eth_rb)) ? HEADER_ETH :
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(header_ipv4_rb)) ? HEADER_IPV4 :
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(header_udp_rb)) ? HEADER_UDP :
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(header_tcp_rb)) ? HEADER_TCP :
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(header_sctp_rb)) ? HEADER_SCTP :
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(header_sctp_data_rb)) ? HEADER_SCTP_DATA :
+ HEADER_NONE;
+
+ switch (text_import_info->dummy_header_type)
+ {
+ case HEADER_ETH:
+ text_import_info->pid = strtol(gtk_entry_get_text(GTK_ENTRY(etype_te)), NULL, 16);
+ if (text_import_info->pid > 0xffff)
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The Ethertype (%x) is too large.",
+ text_import_info->pid);
+ g_free(text_import_info->import_text_filename);
+ fclose(text_import_info->import_text_file);
+ g_free(text_import_info->date_timestamp_format);
+ g_free(text_import_info);
+ return NULL;
+ }
+ break;
+
+ case HEADER_IPV4:
+ text_import_info->protocol = strtol(gtk_entry_get_text(GTK_ENTRY(protocol_te)), NULL, 10);
+ if (text_import_info->protocol > 0xff)
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The IPv4 protocol (%u) is too large.",
+ text_import_info->protocol);
+ g_free(text_import_info->import_text_filename);
+ fclose(text_import_info->import_text_file);
+ g_free(text_import_info->date_timestamp_format);
+ g_free(text_import_info);
+ return NULL;
+ }
+ break;
+
+ case HEADER_UDP:
+ case HEADER_TCP:
+ text_import_info->src_port = strtol(gtk_entry_get_text(GTK_ENTRY(src_port_te)), NULL, 10);
+ if (text_import_info->src_port > 0xffff)
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The source port (%u) is too large.",
+ text_import_info->src_port);
+ g_free(text_import_info->import_text_filename);
+ fclose(text_import_info->import_text_file);
+ g_free(text_import_info->date_timestamp_format);
+ g_free(text_import_info);
+ return NULL;
+ }
+ text_import_info->dst_port = strtol(gtk_entry_get_text(GTK_ENTRY(dst_port_te)), NULL, 10);
+ if (text_import_info->dst_port > 0xffff)
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The destination port (%u) is too large.",
+ text_import_info->dst_port);
+ g_free(text_import_info->import_text_filename);
+ fclose(text_import_info->import_text_file);
+ g_free(text_import_info->date_timestamp_format);
+ g_free(text_import_info);
+ return NULL;
+ }
+ break;
+
+ case HEADER_SCTP:
+ text_import_info->src_port = strtol(gtk_entry_get_text(GTK_ENTRY(src_port_te)), NULL, 10);
+ if (text_import_info->src_port > 0xffff)
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The source port (%u) is too large.",
+ text_import_info->src_port);
+ g_free(text_import_info->import_text_filename);
+ fclose(text_import_info->import_text_file);
+ g_free(text_import_info->date_timestamp_format);
+ g_free(text_import_info);
+ return NULL;
+ }
+ text_import_info->dst_port = strtol(gtk_entry_get_text(GTK_ENTRY(dst_port_te)), NULL, 10);
+ if (text_import_info->dst_port > 0xffff)
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The destination port (%u) is too large.",
+ text_import_info->dst_port);
+ g_free(text_import_info->import_text_filename);
+ fclose(text_import_info->import_text_file);
+ g_free(text_import_info->date_timestamp_format);
+ g_free(text_import_info);
+ return NULL;
+ }
+ text_import_info->tag = strtol(gtk_entry_get_text(GTK_ENTRY(tag_te)), NULL, 10);
+ break;
+
+ case HEADER_SCTP_DATA:
+ text_import_info->src_port = strtol(gtk_entry_get_text(GTK_ENTRY(src_port_te)), NULL, 10);
+ if (text_import_info->src_port > 0xffff)
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The source port (%u) is too large.",
+ text_import_info->src_port);
+ g_free(text_import_info->import_text_filename);
+ fclose(text_import_info->import_text_file);
+ g_free(text_import_info->date_timestamp_format);
+ g_free(text_import_info);
+ return NULL;
+ }
+ text_import_info->dst_port = strtol(gtk_entry_get_text(GTK_ENTRY(dst_port_te)), NULL, 10);
+ if (text_import_info->dst_port > 0xffff)
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The destination port (%u) is too large.",
+ text_import_info->dst_port);
+ g_free(text_import_info->import_text_filename);
+ fclose(text_import_info->import_text_file);
+ g_free(text_import_info->date_timestamp_format);
+ g_free(text_import_info);
+ return NULL;
+ }
+ text_import_info->ppi = strtol(gtk_entry_get_text(GTK_ENTRY(ppi_te)), NULL, 10);
+ break;
+
+ default:
+ break;
+ }
+ } else {
+ text_import_info->dummy_header_type = HEADER_NONE;
+ }
+
+ text_import_info->max_frame_length = strtol(gtk_entry_get_text(GTK_ENTRY(framelen_te)), NULL, 10);
+ if (text_import_info->max_frame_length == 0) {
+ text_import_info->max_frame_length = IMPORT_MAX_PACKET;
+ }
+ else if (text_import_info->max_frame_length > IMPORT_MAX_PACKET)
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The maximum frame length (%u) is too long.",
+ text_import_info->max_frame_length);
+ g_free(text_import_info->import_text_filename);
+ fclose(text_import_info->import_text_file);
+ g_free(text_import_info->date_timestamp_format);
+ g_free(text_import_info);
+ return NULL;
+ }
+ }
+
+ return text_import_info;
+}
+
+/*****************************************************************************/
+
+static void
+file_import_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
+{
+ text_import_info_t *text_import_info;
+
+ switch (btn) {
+ case ESD_BTN_SAVE:
+ /* save file first */
+ file_save_as_cmd(after_save_no_action, NULL, FALSE);
+ break;
+ case ESD_BTN_DONT_SAVE:
+ cf_close(&cfile);
+ break;
+ case ESD_BTN_CANCEL:
+ return;
+ default:
+ g_assert_not_reached();
+ }
+
+ text_import_info = setup_file_import(data);
+ if (text_import_info)
+ file_import_open(text_import_info);
+}
+
+static void
+file_import_ok_cb(GtkWidget *widget _U_, gpointer data)
+{
+ text_import_info_t *text_import_info;
+
+ if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
+ gpointer dialog;
+ /* user didn't save his current file, ask him */
+ dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_SAVE_DONTSAVE_CANCEL,
+ "%sSave capture file before opening a new one?%s\n\n"
+ "If you open a new capture file without saving, your capture data will be discarded.",
+ simple_dialog_primary_start(), simple_dialog_primary_end());
+ simple_dialog_set_cb(dialog, file_import_answered_cb, data);
+ } else {
+ /* unchanged file, just open a new one */
+ text_import_info = setup_file_import(data);
+ if (text_import_info)
+ file_import_open(text_import_info);
+ }
+
+}
+
+/*****************************************************************************/
+
+/*
+ * Dialog creator
+ */
+static GtkWidget *
+file_import_dlg_new(void)
+{
+ GtkWidget *main_w, *main_vb,
+ *input_frm, *input_tb, *input_vb,
+ *filename_hb, *filename_lbl, *filename_te, *browse_bt,
+ *offset_hb, *offset_lbl_vb, *offset_lbl, *offset_rb_vb,
+ *offset_hex_rb, *offset_oct_rb, *offset_dec_rb,
+ *timefmt_hb, *timefmt_cb, *timefmt_lbl, *timefmt_te,
+ *import_frm, *import_vb,
+ *encap_hb, *encap_lbl, *encap_co,
+ *header_cb, *header_frm, *header_hb,
+ *header_eth_rb, *header_ipv4_rb, *header_udp_rb,
+ *header_tcp_rb, *header_sctp_rb, *header_sctp_data_rb,
+ *header_rblbl_vb,
+ *header_rblbl_1_hb, *header_rblbl_1_lbl,
+ *header_rblbl_2_hb, *header_rblbl_2_lbl,
+ *header_rblbl_3_hb, *header_rblbl_3_lbl,
+ *header_rblbl_4_hb, *header_rblbl_4_lbl,
+ *header_rblbl_5_hb, *header_rblbl_5_lbl,
+ *header_rblbl_6_hb, *header_rblbl_6_lbl,
+ *etype_te, *protocol_te, *src_port_te,
+ *dst_port_te, *tag_te, *ppi_te,
+ *framelen_hb, *framelen_lbl, *framelen_te,
+ *bbox, *help_bt, *close_bt, *ok_bt;
+
+ /* Setup the dialog */
+
+ main_w = dlg_window_new("Wireshark: Import from Text");
+ gtk_window_set_default_size(GTK_WINDOW(main_w), 400, 300);
+
+ main_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 3);
+ gtk_container_add(GTK_CONTAINER(main_w), main_vb);
+
+ /* Setup the input frame */
+
+ input_frm = gtk_frame_new("Input");
+ gtk_box_pack_start(GTK_BOX(main_vb), input_frm, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(main_w), INPUT_FRM_KEY, input_frm);
+
+ input_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(input_frm), input_vb);
+
+ input_tb = gtk_table_new(2, 3, FALSE);
+ gtk_container_set_border_width(GTK_CONTAINER(input_tb), 5);
+ gtk_box_pack_start(GTK_BOX(input_vb), input_tb, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(input_tb), 5);
+ gtk_table_set_col_spacings(GTK_TABLE(input_tb), 5);
+
+ /* Filename */
+ filename_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(filename_hb), 3);
+
+ filename_lbl = gtk_label_new("Filename:");
+ gtk_table_attach(GTK_TABLE(input_tb), filename_lbl, 0, 1, 0, 1, 0, 0, 0, 0);
+
+ filename_te = gtk_entry_new();
+ gtk_widget_set_tooltip_text(filename_te, "Set name of text file to import");
+ gtk_table_attach_defaults(GTK_TABLE(input_tb), filename_te, 1, 2, 0, 1);
+
+ g_object_set_data(G_OBJECT(input_frm), INPUT_FILENAME_TE_KEY, filename_te);
+
+ browse_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_BROWSE);
+ gtk_widget_set_tooltip_text(browse_bt, "Browse for text file to import");
+ gtk_table_attach(GTK_TABLE(input_tb), browse_bt, 2, 3, 0, 1, 0, 0, 0, 0);
+
+ g_signal_connect(browse_bt, "clicked", G_CALLBACK(browse_file_cb), filename_te);
+
+ /* Offsets */
+ offset_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(offset_hb), 3);
+ gtk_box_pack_start(GTK_BOX(input_vb), offset_hb, FALSE, FALSE, 0);
+
+ offset_lbl_vb = gtk_vbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(offset_hb), offset_lbl_vb, FALSE, FALSE, 0);
+
+ offset_lbl = gtk_label_new("Offsets:");
+ gtk_misc_set_alignment(GTK_MISC(offset_lbl), 1.0f, 0.0f);
+ gtk_table_attach(GTK_TABLE(input_tb), offset_lbl, 0, 1, 1, 2, 0, 0, 0, 0);
+
+ offset_rb_vb = gtk_vbox_new(FALSE, 0);
+ gtk_table_attach_defaults(GTK_TABLE(input_tb), offset_rb_vb, 1, 2, 1, 2);
+
+ /* First entry in the group */
+ offset_hex_rb = gtk_radio_button_new_with_label(NULL, "Hexadecimal");
+ gtk_widget_set_tooltip_text(offset_hex_rb, "Offsets in the text file are in hexadecimal notation");
+ gtk_box_pack_start(GTK_BOX(offset_rb_vb), offset_hex_rb, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(input_frm), INPUT_OFFSET_HEX_RB_KEY, offset_hex_rb);
+
+ offset_oct_rb = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(offset_hex_rb), "Octal");
+ gtk_widget_set_tooltip_text(offset_oct_rb, "Offsets in the text file are in octal notation");
+ gtk_box_pack_start(GTK_BOX(offset_rb_vb), offset_oct_rb, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(input_frm), INPUT_OFFSET_OCT_RB_KEY, offset_oct_rb);
+
+ offset_dec_rb = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(offset_hex_rb), "Decimal");
+ gtk_widget_set_tooltip_text(offset_dec_rb, "Offsets in the text file are in decimal notation");
+ gtk_box_pack_start(GTK_BOX(offset_rb_vb), offset_dec_rb, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(input_frm), INPUT_OFFSET_DEC_RB_KEY, offset_dec_rb);
+
+ /* Time format */
+ timefmt_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(timefmt_hb), 3);
+ gtk_box_pack_start(GTK_BOX(input_vb), timefmt_hb, FALSE, FALSE, 0);
+
+ timefmt_cb = gtk_check_button_new_with_label("Date/Time");
+ gtk_widget_set_tooltip_text(timefmt_cb, "Whether or not the text file contains timestamp information");
+ gtk_box_pack_start(GTK_BOX(timefmt_hb), timefmt_cb, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(input_frm), INPUT_DATETIME_CB_KEY, timefmt_cb);
+
+ timefmt_lbl = gtk_label_new(" Format:");
+ gtk_box_pack_start(GTK_BOX(timefmt_hb), timefmt_lbl, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(timefmt_cb), INPUT_TIMEFMT_LBL_KEY, timefmt_lbl);
+
+ timefmt_te = gtk_entry_new();
+ gtk_widget_set_tooltip_text(timefmt_te, "The format in which to parse timestamps in the text file (eg. %H:%M:%S.). Format specifiers are based on strptime(3)");
+ gtk_box_pack_start(GTK_BOX(timefmt_hb), timefmt_te, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(timefmt_cb), INPUT_TIMEFMT_TE_KEY, timefmt_te);
+ g_object_set_data(G_OBJECT(input_frm), INPUT_TIMEFMT_TE_KEY, timefmt_te);
+
+ g_signal_connect(timefmt_cb, "toggled", G_CALLBACK(timefmt_cb_toggle), NULL);
+ g_signal_emit_by_name(G_OBJECT(timefmt_cb), "toggled", NULL);
+
+ /* Setup the import frame */
+
+ import_frm = gtk_frame_new("Import");
+ gtk_box_pack_start(GTK_BOX(main_vb), import_frm, TRUE, TRUE, 3);
+
+ g_object_set_data(G_OBJECT(main_w), IMPORT_FRM_KEY, import_frm);
+
+ import_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(import_frm), import_vb);
+
+ /* Encapsulation */
+ encap_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(encap_hb), 3);
+ gtk_box_pack_start(GTK_BOX(import_vb), encap_hb, FALSE, FALSE, 0);
+
+ encap_lbl = gtk_label_new("Encapsulation type:");
+ gtk_box_pack_start(GTK_BOX(encap_hb), encap_lbl, FALSE, FALSE, 0);
+
+ encap_co = fill_encap_combo();
+ gtk_widget_set_tooltip_text(encap_co, "Encapsulation type for the frames in the import capture file");
+ gtk_box_pack_start(GTK_BOX(encap_hb), encap_co, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(import_frm), IMPORT_ENCAP_CO_KEY, encap_co);
+
+ /* Dummy header */
+ header_frm = gtk_frame_new(NULL);
+ header_cb = gtk_check_button_new_with_label("Dummy header");
+ gtk_widget_set_tooltip_text(header_cb, "Whether or not to prefix a dummy header to the frames");
+ gtk_frame_set_label_widget(GTK_FRAME(header_frm), header_cb);
+ gtk_container_set_border_width(GTK_CONTAINER(header_frm), 3);
+ gtk_box_pack_start(GTK_BOX(import_vb), header_frm, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(import_frm), IMPORT_HEADER_FRM_KEY, header_frm);
+ g_object_set_data(G_OBJECT(header_frm), IMPORT_HEADER_CB_KEY, header_cb);
+
+ header_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(header_hb), 3);
+ gtk_container_add(GTK_CONTAINER(header_frm), header_hb);
+
+ header_rblbl_vb = gtk_vbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(header_hb), header_rblbl_vb, TRUE, TRUE, 0);
+
+ /* Line 1 */
+ header_rblbl_1_hb = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(header_rblbl_vb), header_rblbl_1_hb, FALSE, FALSE, 2);
+
+ /* First entry in the group */
+ header_eth_rb = gtk_radio_button_new_with_label(NULL, "Ethernet");
+ gtk_widget_set_tooltip_text(header_eth_rb, "Prefix an Ethernet header to the frames");
+ g_signal_connect(header_eth_rb, "toggled", G_CALLBACK(header_eth_rb_toggle), header_frm);
+ gtk_box_pack_start(GTK_BOX(header_rblbl_1_hb), header_eth_rb, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(header_frm), IMPORT_HEADER_ETH_RB_KEY, header_eth_rb);
+
+ header_rblbl_1_lbl = gtk_label_new(" Ethertype (hex):");
+ gtk_box_pack_start(GTK_BOX(header_rblbl_1_hb), header_rblbl_1_lbl, TRUE, TRUE, 0);
+ gtk_misc_set_alignment(GTK_MISC(header_rblbl_1_lbl), 1.0f, 0.5f);
+
+ etype_te = gtk_entry_new();
+ gtk_widget_set_tooltip_text(etype_te, "The type to set in the Ethernet header");
+ gtk_box_pack_end(GTK_BOX(header_rblbl_1_hb), etype_te, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(header_frm), IMPORT_HEADER_ETYPE_LBL_KEY, header_rblbl_1_lbl);
+ g_object_set_data(G_OBJECT(header_frm), IMPORT_HEADER_ETYPE_TE_KEY, etype_te);
+
+ /* Line 2 */
+ header_rblbl_2_hb = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(header_rblbl_vb), header_rblbl_2_hb, FALSE, FALSE, 2);
+
+ header_ipv4_rb = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(header_eth_rb), "IPv4");
+ gtk_widget_set_tooltip_text(header_ipv4_rb, "Prefix an Ethernet and IPv4 header to the frames");
+ g_signal_connect(header_ipv4_rb, "toggled", G_CALLBACK(header_ipv4_rb_toggle), header_frm);
+ gtk_box_pack_start(GTK_BOX(header_rblbl_2_hb), header_ipv4_rb, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(header_frm), IMPORT_HEADER_IPV4_RB_KEY, header_ipv4_rb);
+
+ header_rblbl_2_lbl = gtk_label_new(" Protocol (dec):");
+ gtk_box_pack_start(GTK_BOX(header_rblbl_2_hb), header_rblbl_2_lbl, TRUE, TRUE, 0);
+ gtk_misc_set_alignment(GTK_MISC(header_rblbl_2_lbl), 1.0f, 0.5f);
+
+ protocol_te = gtk_entry_new();
+ gtk_widget_set_tooltip_text(protocol_te, "The protocol id to set in the IPv4 header");
+ gtk_box_pack_end(GTK_BOX(header_rblbl_2_hb), protocol_te, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(header_frm), IMPORT_HEADER_PROT_LBL_KEY, header_rblbl_2_lbl);
+ g_object_set_data(G_OBJECT(header_frm), IMPORT_HEADER_PROT_TE_KEY, protocol_te);
+
+ /* Line 3 */
+ header_rblbl_3_hb = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(header_rblbl_vb), header_rblbl_3_hb, FALSE, FALSE, 2);
+
+ header_udp_rb = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(header_eth_rb), "UDP");
+ gtk_widget_set_tooltip_text(header_udp_rb, "Prefix an Ethernet, IPv4 and UDP header to the frames");
+ g_signal_connect(header_udp_rb, "toggled", G_CALLBACK(header_udp_rb_toggle), header_frm);
+ gtk_box_pack_start(GTK_BOX(header_rblbl_3_hb), header_udp_rb, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(header_frm), IMPORT_HEADER_UDP_RB_KEY, header_udp_rb);
+
+ header_rblbl_3_lbl = gtk_label_new(" Source port:");
+ gtk_box_pack_start(GTK_BOX(header_rblbl_3_hb), header_rblbl_3_lbl, TRUE, TRUE, 0);
+ gtk_misc_set_alignment(GTK_MISC(header_rblbl_3_lbl), 1.0f, 0.5f);
+
+ src_port_te = gtk_entry_new();
+ gtk_widget_set_tooltip_text(src_port_te, "The source port to set in the UDP, TCP or SCTP header");
+ gtk_box_pack_end(GTK_BOX(header_rblbl_3_hb), src_port_te, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(header_frm), IMPORT_HEADER_SRC_PORT_LBL_KEY, header_rblbl_3_lbl);
+ g_object_set_data(G_OBJECT(header_frm), IMPORT_HEADER_SRC_PORT_TE_KEY, src_port_te);
+
+ /* Line 4 */
+ header_rblbl_4_hb = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(header_rblbl_vb), header_rblbl_4_hb, FALSE, FALSE, 2);
+
+ header_tcp_rb = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(header_eth_rb), "TCP");
+ gtk_widget_set_tooltip_text(header_tcp_rb, "Prefix an Ethernet, IPv4 and TCP header to the frames");
+ g_signal_connect(header_tcp_rb, "toggled", G_CALLBACK(header_tcp_rb_toggle), header_frm);
+ gtk_box_pack_start(GTK_BOX(header_rblbl_4_hb), header_tcp_rb, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(header_frm), IMPORT_HEADER_TCP_RB_KEY, header_tcp_rb);
+
+ header_rblbl_4_lbl = gtk_label_new(" Destination port:");
+ gtk_box_pack_start(GTK_BOX(header_rblbl_4_hb), header_rblbl_4_lbl, TRUE, TRUE, 0);
+ gtk_misc_set_alignment(GTK_MISC(header_rblbl_4_lbl), 1.0f, 0.5f);
+
+ dst_port_te = gtk_entry_new();
+ gtk_widget_set_tooltip_text(dst_port_te, "The destination port to set in the UDP, TCP or SCTP header");
+ gtk_box_pack_end(GTK_BOX(header_rblbl_4_hb), dst_port_te, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(header_frm), IMPORT_HEADER_DST_PORT_LBL_KEY, header_rblbl_4_lbl);
+ g_object_set_data(G_OBJECT(header_frm), IMPORT_HEADER_DST_PORT_TE_KEY, dst_port_te);
+
+ /* Line 5 */
+ header_rblbl_5_hb = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(header_rblbl_vb), header_rblbl_5_hb, FALSE, FALSE, 2);
+
+ header_sctp_rb = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(header_eth_rb), "SCTP");
+ gtk_widget_set_tooltip_text(header_sctp_rb, "Prefix an Ethernet, IPv4 and SCTP header to the frames");
+ g_signal_connect(header_sctp_rb, "toggled", G_CALLBACK(header_sctp_rb_toggle), header_frm);
+ gtk_box_pack_start(GTK_BOX(header_rblbl_5_hb), header_sctp_rb, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(header_frm), IMPORT_HEADER_SCTP_RB_KEY, header_sctp_rb);
+
+ header_rblbl_5_lbl = gtk_label_new(" Tag:");
+ gtk_box_pack_start(GTK_BOX(header_rblbl_5_hb), header_rblbl_5_lbl, TRUE, TRUE, 0);
+ gtk_misc_set_alignment(GTK_MISC(header_rblbl_5_lbl), 1.0f, 0.5f);
+
+ tag_te = gtk_entry_new();
+ gtk_widget_set_tooltip_text(tag_te, "The verification tag to set in the SCTP header");
+ gtk_box_pack_end(GTK_BOX(header_rblbl_5_hb), tag_te, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(header_frm), IMPORT_HEADER_TAG_LBL_KEY, header_rblbl_5_lbl);
+ g_object_set_data(G_OBJECT(header_frm), IMPORT_HEADER_TAG_TE_KEY, tag_te);
+
+ /* Line 6 */
+ header_rblbl_6_hb = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(header_rblbl_vb), header_rblbl_6_hb, FALSE, FALSE, 2);
+
+ header_sctp_data_rb = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(header_eth_rb), "SCTP (DATA)");
+ gtk_widget_set_tooltip_text(header_sctp_data_rb, "Prefix an Ethernet, IPv4 and SCTP DATA header to the frames");
+ g_signal_connect(header_sctp_data_rb, "toggled", G_CALLBACK(header_sctp_data_rb_toggle), header_frm);
+ gtk_box_pack_start(GTK_BOX(header_rblbl_6_hb), header_sctp_data_rb, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(header_frm), IMPORT_HEADER_SCTP_D_RB_KEY, header_sctp_data_rb);
+
+ header_rblbl_6_lbl = gtk_label_new(" PPI:");
+ gtk_box_pack_start(GTK_BOX(header_rblbl_6_hb), header_rblbl_6_lbl, TRUE, TRUE, 0);
+ gtk_misc_set_alignment(GTK_MISC(header_rblbl_6_lbl), 1.0f, 0.5f);
+
+ ppi_te = gtk_entry_new();
+ gtk_widget_set_tooltip_text(ppi_te, "The payload protocol identifier to set in the SCTP DATA header");
+ gtk_box_pack_end(GTK_BOX(header_rblbl_6_hb), ppi_te, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(header_frm), IMPORT_HEADER_PPI_LBL_KEY, header_rblbl_6_lbl);
+ g_object_set_data(G_OBJECT(header_frm), IMPORT_HEADER_PPI_TE_KEY, ppi_te);
+
+ /* Set sensitivity */
+ g_signal_connect(header_cb, "toggled", G_CALLBACK(header_cb_toggle), header_frm);
+ g_signal_emit_by_name(G_OBJECT(header_cb), "toggled", header_frm);
+
+ g_signal_emit_by_name(G_OBJECT(header_eth_rb), "toggled", header_frm);
+
+ gtk_combo_box_set_active(GTK_COMBO_BOX(encap_co), 0);
+ g_signal_connect(encap_co, "changed", G_CALLBACK(encap_co_changed), header_frm);
+
+ /* Frame length */
+ framelen_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(framelen_hb), 3);
+ gtk_box_pack_start(GTK_BOX(import_vb), framelen_hb, FALSE, FALSE, 0);
+
+ framelen_lbl = gtk_label_new("Max. frame length:");
+ gtk_box_pack_start(GTK_BOX(framelen_hb), framelen_lbl, FALSE, FALSE, 0);
+
+ framelen_te = gtk_entry_new();
+ gtk_widget_set_tooltip_text(framelen_te, "The maximum size of the frames to write to the import capture file (max 64000)");
+ gtk_box_pack_start(GTK_BOX(framelen_hb), framelen_te, FALSE, FALSE, 0);
+
+ g_object_set_data(G_OBJECT(import_frm), IMPORT_FRAME_LENGTH_TE_KEY, framelen_te);
+
+ /* Setup the button row */
+
+ bbox = dlg_button_row_new(GTK_STOCK_HELP, GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL);
+ gtk_box_pack_end(GTK_BOX(main_vb), bbox, FALSE, FALSE, 3);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_IMPORT_DIALOG);
+ gtk_widget_set_tooltip_text(help_bt, "Show topic specific help");
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ window_set_cancel_button(main_w, close_bt, window_cancel_button_cb);
+ gtk_widget_set_tooltip_text(close_bt, "Close this dialog");
+
+ ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(file_import_ok_cb), main_w);
+ gtk_widget_grab_default(ok_bt);
+ gtk_widget_set_tooltip_text(ok_bt, "Import the selected file into a temporary capture file");
+
+ /* Setup widget handling */
+
+ g_signal_connect(main_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(main_w, "destroy", G_CALLBACK(file_import_dlg_destroy_cb), NULL);
+
+ gtk_widget_show_all(main_w);
+ window_present(main_w);
+
+ return main_w;
+}
+
+void
+file_import_cmd_cb(GtkWidget *widget _U_)
+{
+ /* Do we have an encapsulation type list? */
+ if (!encap_list_store)
+ {
+ /* No. Create one. */
+ create_encap_list_store();
+ }
+
+ /* Has a file import dialog already been opened? */
+ if (file_import_dlg_w)
+ {
+ /* Yes. Just re-activate that dialog box. */
+ reactivate_window(file_import_dlg_w);
+ } else {
+ /* No. Create one */
+ file_import_dlg_w = file_import_dlg_new();
+ }
+
+ return;
+}
diff --git a/ui/gtk/file_import_dlg.h b/ui/gtk/file_import_dlg.h
new file mode 100644
index 0000000000..573c0eb486
--- /dev/null
+++ b/ui/gtk/file_import_dlg.h
@@ -0,0 +1,31 @@
+/* file_import_dlg.h
+ * Definitions for file import dialog box
+ * November 2010, Jaap Keuter <jaap.keuter@xs4all.nl>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __FILE_IMPORT_DLG_H__
+#define __FILE_IMPORT_DLG_H__
+
+void file_import_cmd_cb(GtkWidget *widget);
+
+#endif /* file_import_dlg.h */
diff --git a/ui/gtk/fileset_dlg.c b/ui/gtk/fileset_dlg.c
new file mode 100644
index 0000000000..1684190587
--- /dev/null
+++ b/ui/gtk/fileset_dlg.c
@@ -0,0 +1,401 @@
+/* fileset_dlg.c
+ * Routines for the file set dialog
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <gtk/gtk.h>
+
+#include <epan/filesystem.h>
+
+#include "../simple_dialog.h"
+#include "../fileset.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/menus.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/fileset_dlg.h"
+
+
+
+/*
+ * Keep a static pointer to the current "File Set" window, if
+ * any, so that if somebody tries to do "File Set" while there's
+ * already a "File Set" window up, we just pop up the existing
+ * one, rather than creating a new one.
+ */
+static GtkWidget *fs_w;
+
+
+
+/* various widget related global data */
+static int row;
+static GtkWidget *fs_tb;
+static GtkWidget *fs_sw;
+static GtkWidget *fs_dir_lb;
+static GtkWidget *fs_first_rb;
+static GtkWidget *fs_tb_vb;
+
+
+
+/* open the file corresponding to the given fileset entry */
+static void
+fs_open_entry(fileset_entry *entry)
+{
+ char *fname;
+ int err;
+
+
+ /* make a copy of the filename (cf_close will indirectly destroy it right now) */
+ fname = g_strdup(entry->fullname);
+
+ /* close the old and open the new file */
+ cf_close(&cfile);
+ if (cf_open(&cfile, fname, FALSE, &err) == CF_OK) {
+ cf_read(&cfile, FALSE);
+ }
+
+ g_free(fname);
+}
+
+
+/* radio button was pressed/released */
+static void
+fs_rb_cb(GtkWidget *open_bt, gpointer fs_data)
+{
+ fileset_entry *entry = fs_data;
+
+ /* button release should have no effect */
+ if(!gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(open_bt) )) {
+ return;
+ }
+
+ fs_open_entry(entry);
+}
+
+
+/* the window was closed, cleanup things */
+static void
+fs_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+ /* Note that we no longer have a "File Set" dialog box. */
+ fs_w = NULL;
+}
+
+
+/* get creation date (converted from filename) */
+/* */
+static char *
+fileset_dlg_name2date_dup(const char * name) {
+ char *pfx;
+ char *filename;
+ size_t pos;
+
+
+ /* just to be sure ... */
+ if(!fileset_filename_match_pattern(name)) {
+ return NULL;
+ }
+
+ /* find char position behind the last underscore */
+ pfx = strrchr(name, '_');
+ pfx++;
+ pos = pfx - name;
+
+ /* start conversion behind that underscore */
+ filename = g_strdup_printf("%c%c%c%c.%c%c.%c%c %c%c:%c%c:%c%c",
+ /* year */ name[pos] , name[pos+1], name[pos+2], name[pos+3],
+ /* month */ name[pos+4], name[pos+5],
+ /* day */ name[pos+6], name[pos+7],
+ /* hour */ name[pos+8], name[pos+9],
+ /* min */ name[pos+10], name[pos+11],
+ /* second */ name[pos+12], name[pos+13]);
+
+ return filename;
+}
+
+
+/* this file is a part of the current file set, add it to the dialog */
+void
+fileset_dlg_add_file(fileset_entry *entry) {
+ char *created;
+ char *modified;
+ char *size;
+ struct tm *local;
+ GtkWidget *fs_lb;
+ GtkWidget *fs_rb;
+ gchar *title;
+
+
+ if (fs_w == NULL) {
+ return;
+ }
+
+ created = fileset_dlg_name2date_dup(entry->name);
+ if(!created) {
+ /* if this file doesn't follow the file set pattern, */
+ /* use the creation time of that file */
+ local = localtime(&entry->ctime);
+ created = g_strdup_printf("%04u.%02u.%02u %02u:%02u:%02u",
+ local->tm_year+1900, local->tm_mon+1, local->tm_mday,
+ local->tm_hour, local->tm_min, local->tm_sec);
+ }
+
+ local = localtime(&entry->mtime);
+ modified = g_strdup_printf("%04u.%02u.%02u %02u:%02u:%02u",
+ local->tm_year+1900, local->tm_mon+1, local->tm_mday,
+ local->tm_hour, local->tm_min, local->tm_sec);
+ size = g_strdup_printf("%" G_GINT64_MODIFIER "d Bytes", entry->size);
+
+ fs_rb = gtk_radio_button_new_with_label_from_widget(
+ fs_first_rb ? GTK_RADIO_BUTTON(fs_first_rb) : NULL, entry->name);
+ if(row == 1) {
+ fs_first_rb = fs_rb;
+ }
+ if(entry->current) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (fs_rb), entry->current);
+ }
+ gtk_widget_set_tooltip_text(fs_rb, "Open this capture file");
+ gtk_table_attach_defaults(GTK_TABLE(fs_tb), fs_rb, 0, 1, row, row+1);
+ g_signal_connect(fs_rb, "toggled", G_CALLBACK(fs_rb_cb), entry);
+ gtk_widget_show(fs_rb);
+
+ fs_lb = gtk_label_new(created);
+ gtk_table_attach_defaults(GTK_TABLE(fs_tb), fs_lb, 1, 2, row, row+1);
+ gtk_widget_set_sensitive(fs_lb, entry->current);
+ gtk_widget_show(fs_lb);
+
+ fs_lb = gtk_label_new(modified);
+ gtk_table_attach_defaults(GTK_TABLE(fs_tb), fs_lb, 2, 3, row, row+1);
+ gtk_widget_set_sensitive(fs_lb, entry->current);
+ gtk_widget_show(fs_lb);
+
+ fs_lb = gtk_label_new(size);
+ gtk_table_attach_defaults(GTK_TABLE(fs_tb), fs_lb, 3, 4, row, row+1);
+ gtk_widget_set_sensitive(fs_lb, entry->current);
+ gtk_widget_show(fs_lb);
+
+ title = g_strdup_printf("Wireshark: %u File%s in Set", row, plurality(row, "", "s"));
+ gtk_window_set_title(GTK_WINDOW(fs_w), title);
+ g_free(title);
+
+ title = g_strdup_printf("... in directory: %s", fileset_get_dirname());
+ gtk_label_set_text(GTK_LABEL(fs_dir_lb), title);
+ g_free(title);
+
+ gtk_widget_show_all(fs_tb);
+
+ /* resize the table until we use 18 rows (fits well into 800*600), if it's bigger use a scrollbar */
+ /* XXX - I didn't found a way to automatically shrink the table size again */
+ if(row <= 18) {
+ GtkRequisition requisition;
+
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_get_preferred_size(fs_tb, &requisition, NULL);
+#else
+ gtk_widget_size_request(fs_tb, &requisition);
+#endif
+ /* XXX use gtk_window_set_default_size()? */
+ gtk_widget_set_size_request(fs_sw, -1, requisition.height);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(fs_sw), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
+ }
+
+ if(row == 18) {
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(fs_sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ }
+
+ row++;
+
+ g_free(created);
+ g_free(modified);
+ g_free(size);
+}
+
+
+/* init the fileset table */
+static void
+fileset_init_table(GtkWidget *parent)
+{
+ GtkWidget *fs_lb;
+
+
+ fs_tb = gtk_table_new(6,1, FALSE);
+ gtk_table_set_row_spacings(GTK_TABLE(fs_tb), 1);
+ gtk_table_set_col_spacings(GTK_TABLE(fs_tb), 12);
+ gtk_container_add(GTK_CONTAINER(parent), fs_tb);
+
+ row = 0;
+ fs_first_rb = NULL;
+
+ fs_lb = gtk_label_new("Filename");
+ gtk_table_attach(GTK_TABLE(fs_tb), fs_lb, 0, 1, row, row+1, GTK_EXPAND|GTK_FILL, 0, 0,0);
+
+ fs_lb = gtk_label_new("Created");
+ gtk_table_attach(GTK_TABLE(fs_tb), fs_lb, 1, 2, row, row+1, GTK_EXPAND|GTK_FILL, 0, 0,0);
+
+ fs_lb = gtk_label_new("Last Modified");
+ gtk_table_attach(GTK_TABLE(fs_tb), fs_lb, 2, 3, row, row+1, GTK_EXPAND|GTK_FILL, 0, 0,0);
+
+ fs_lb = gtk_label_new("Size");
+ gtk_table_attach(GTK_TABLE(fs_tb), fs_lb, 3, 4, row, row+1, GTK_EXPAND|GTK_FILL, 0, 0,0);
+
+ gtk_widget_hide(fs_tb);
+
+ gtk_window_set_title(GTK_WINDOW(fs_w), "Wireshark: 0 Files in Set");
+
+ gtk_label_set_text(GTK_LABEL(fs_dir_lb), "No capture file loaded!");
+
+ row++;
+}
+
+
+/* open the fileset dialog */
+void
+fileset_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ GtkWidget *main_vb, *bbox, *close_bt, *help_bt;
+
+
+ if (fs_w != NULL) {
+ /* There's already a "File Set" dialog box; reactivate it. */
+ reactivate_window(fs_w);
+ return;
+ }
+
+ fs_w = dlg_window_new(""); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(fs_w), TRUE);
+
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(fs_w), main_vb);
+
+ fs_sw = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(fs_sw), GTK_POLICY_NEVER, GTK_POLICY_NEVER);
+ gtk_box_pack_start(GTK_BOX(main_vb), fs_sw, TRUE, TRUE, 0);
+
+ /* add a dummy container, so we can replace the table later */
+ fs_tb_vb = gtk_vbox_new(FALSE, 0);
+ gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(fs_sw), fs_tb_vb);
+
+ fs_dir_lb = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(main_vb), fs_dir_lb, FALSE, FALSE, 0);
+
+ fileset_init_table(fs_tb_vb);
+
+ /* Button row: close and help button */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 5);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(fs_w, close_bt, window_cancel_button_cb);
+ gtk_widget_set_tooltip_text(close_bt, "Close this window.");
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_FILESET_DIALOG);
+
+ gtk_widget_grab_default(close_bt);
+
+ g_signal_connect(fs_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(fs_w, "destroy", G_CALLBACK(fs_destroy_cb), NULL);
+
+ /* init the dialog content */
+ fileset_update_dlg();
+
+ gtk_widget_show_all(fs_w);
+ window_present(fs_w);
+}
+
+
+/* open the next file in the file set, or do nothing if already the last file */
+void
+fileset_next_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ fileset_entry *entry;
+
+ entry = fileset_get_next();
+
+ if(entry) {
+ fs_open_entry(entry);
+ }
+}
+
+
+/* open the previous file in the file set, or do nothing if already the first file */
+void
+fileset_previous_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ fileset_entry *entry;
+
+ entry = fileset_get_previous();
+
+ if(entry) {
+ fs_open_entry(entry);
+ }
+}
+
+
+/* a new capture file was opened, browse the dir and look for files matching the given file set */
+void
+fileset_file_opened(const char *fname) {
+ fileset_add_dir(fname);
+ if(fs_w) {
+ window_present(fs_w);
+ }
+
+ /* update the menu */
+ set_menus_for_file_set(TRUE /* file_set */,
+ fileset_get_previous() != NULL, fileset_get_next() != NULL );
+}
+
+
+/* the capture file was closed */
+void
+fileset_file_closed(void)
+{
+ if(fs_w) {
+ /* reinit the table, title and alike */
+ g_object_ref(G_OBJECT(fs_tb_vb));
+ gtk_widget_destroy(fs_tb);
+ fileset_delete();
+ fileset_init_table(fs_tb_vb);
+ window_present(fs_w);
+ } else {
+ fileset_delete();
+ }
+
+ /* update the menu */
+ set_menus_for_file_set(FALSE /* file_set */,
+ fileset_get_previous() != NULL,
+ fileset_get_next() != NULL );
+}
+
diff --git a/ui/gtk/fileset_dlg.h b/ui/gtk/fileset_dlg.h
new file mode 100644
index 0000000000..89fe16493a
--- /dev/null
+++ b/ui/gtk/fileset_dlg.h
@@ -0,0 +1,54 @@
+/* fileset_dlg.h
+ * Definitions for the fileset dialog box
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __FILESET_DLG_H__
+#define __FILESET_DLG_H__
+
+/** @file
+ * "File Set" dialog box.
+ * @ingroup dialog_group
+ */
+
+/** Open the fileset dialog.
+ *
+ * @param w calling widget (unused)
+ * @param d data from calling widget (unused)
+ */
+extern void fileset_cb(GtkWidget *w, gpointer d);
+
+/** Open the next file in the file set, or do nothing if already the last file.
+ *
+ * @param w calling widget (unused)
+ * @param d data from calling widget (unused)
+ */
+extern void fileset_next_cb(GtkWidget *w, gpointer d);
+
+/** Open the previous file in the file set, or do nothing if already the first file.
+ *
+ * @param w calling widget (unused)
+ * @param d data from calling widget (unused)
+ */
+extern void fileset_previous_cb(GtkWidget *w, gpointer d);
+
+#endif /* fileset_dlg.h */
diff --git a/ui/gtk/filter_autocomplete.c b/ui/gtk/filter_autocomplete.c
new file mode 100644
index 0000000000..6a67b51eb8
--- /dev/null
+++ b/ui/gtk/filter_autocomplete.c
@@ -0,0 +1,885 @@
+/* filter_autocomplete.h
+ * Definitions for filter autocomplete
+ *
+ * Copyright 2008, Bahaa Naamneh <b.naamneh@gmail.com>
+ *
+ * With several usability improvements:
+ * Copyright 2008, Stig Bjorlykke <stig@bjorlykke.org>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#if GTK_CHECK_VERSION(3,0,0)
+# include <gdk/gdkkeysyms-compat.h>
+#endif
+
+#include <epan/proto.h>
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/filter_autocomplete.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+#define E_FILT_AUTOCOMP_TREE_KEY "filter_autocomplete_tree"
+
+
+static GtkWidget *filter_autocomplete_new(GtkWidget *filter_te, const gchar *protocol_name,
+ gboolean protocols_only, gboolean *stop_propagation);
+static void autocomplete_protocol_string(GtkWidget *filter_te, gchar* selected_str);
+static void autoc_filter_row_activated_cb(GtkTreeView *treeview, GtkTreePath *path,
+ GtkTreeViewColumn *column, gpointer data);
+static gboolean filter_te_focus_out_cb(GtkWidget *filter_te, GdkEvent *event, gpointer data);
+static void init_autocompletion_list(GtkWidget *list);
+static void add_to_autocompletion_list(GtkWidget *list, const gchar *str);
+static gboolean autocompletion_list_lookup(GtkWidget *filter_te, GtkWidget *popup_win, GtkWidget *list,
+ const gchar *str, gboolean *stop_propagation);
+static void filter_autocomplete_handle_backspace(GtkWidget *filter_te, GtkWidget *list, GtkWidget *popup_win,
+ gchar *prefix, GtkWidget *main_win);
+static void filter_autocomplete_win_destroy_cb(GtkWidget *win, gpointer data);
+static gboolean is_protocol_name_being_typed(GtkWidget *filter_te, int str_len);
+
+
+/*
+ * Check if the string at the cursor position is a beginning of a protocol name.
+ * Possible false positives:
+ * "NOT" at the beginning of the display filter editable text.
+ * "NOT" adjacent to another logical operation. (e.g: exp1 AND NOT exp2).
+ **/
+static gboolean
+is_protocol_name_being_typed(GtkWidget *filter_te, int str_len)
+{
+ unsigned int i;
+ int op_len, cursor_pos;
+ gchar *start;
+ gchar *pos;
+ static gchar *logic_ops[] = { "!", "not",
+ "||", "or",
+ "&&", "and",
+ "^^", "xor" };
+
+ /* If the cursor is located at the beginning of the filter editable text,
+ * then it's _probably_ a protocol name.
+ **/
+ if(!(cursor_pos = gtk_editable_get_position(GTK_EDITABLE(filter_te))))
+ return TRUE;
+
+ start = gtk_editable_get_chars(GTK_EDITABLE(filter_te), 0, (gint) cursor_pos);
+
+ /* Point to one char before the current string in the filter editable text */
+ pos = start + (cursor_pos - str_len);
+
+ /* Walk back through string to find last char which isn't ' ' or '(' */
+ while(pos > start) {
+ if(*pos != ' ' && *pos != '(') {
+ /* Check if we have one of the logical operations */
+ for(i = 0; i < (sizeof(logic_ops)/sizeof(logic_ops[0])); i++) {
+ op_len = (int) strlen(logic_ops[i]);
+
+ if(pos-start+1 < op_len)
+ continue;
+
+ /* If one of the logical operations is found, then the current string is _probably_ a protocol name */
+ if(!strncmp(pos-op_len+1, logic_ops[i], op_len)) {
+ g_free (start);
+ return TRUE;
+ }
+ }
+
+ /* If none of the logical operations was found, then the current string is not a protocol */
+ g_free (start);
+ return FALSE;
+ }
+ pos--;
+ }
+
+ /* The "str" preceded only by ' ' or '(' chars,
+ * which means that the str is _probably_ a protocol name.
+ **/
+ g_free (start);
+ return TRUE;
+}
+
+
+static void
+autocomplete_protocol_string(GtkWidget *filter_te, gchar *selected_str)
+{
+ int pos;
+ gchar *filter_str;
+ gchar *pch;
+
+ /* Get the current filter string */
+ pos = gtk_editable_get_position(GTK_EDITABLE(filter_te));
+ filter_str = gtk_editable_get_chars(GTK_EDITABLE(filter_te), 0, pos);
+
+ /* Start from the end */
+ pch = filter_str + strlen(filter_str);
+
+ /* Walk back through string to find last non-punctuation */
+ while(pch != filter_str) {
+ pch--;
+ if(!g_ascii_isalnum(*pch) && (*pch) != '.' && (*pch) != '_' && (*pch) != '-') {
+ pch++;
+ break;
+ }
+ }
+
+ if(strncmp(pch, selected_str, pos-(pch-filter_str))) {
+ gtk_editable_delete_text(GTK_EDITABLE(filter_te), (gint) (pch-filter_str), pos);
+ pos = (int) (pch-filter_str);
+ pch = selected_str;
+ } else {
+ pch = (selected_str + strlen(pch));
+ }
+
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), pch, (gint) strlen(pch), &pos);
+ gtk_editable_set_position(GTK_EDITABLE(filter_te), pos);
+ g_free (filter_str);
+}
+
+/* On row activated signal, complete the protocol string automatically */
+static void
+autoc_filter_row_activated_cb(GtkTreeView *treeview,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column _U_,
+ gpointer data)
+{
+ GtkWidget *w_main;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkWidget *win;
+ gchar *proto;
+
+ model = gtk_tree_view_get_model(treeview);
+
+ if (gtk_tree_model_get_iter(model, &iter, path)) {
+
+ gtk_tree_model_get(model, &iter, 0, &proto, -1);
+ autocomplete_protocol_string(GTK_WIDGET(data), proto);
+
+ g_free (proto);
+ }
+
+ w_main = gtk_widget_get_toplevel(GTK_WIDGET(data));
+ win = g_object_get_data(G_OBJECT(w_main), E_FILT_AUTOCOMP_PTR_KEY);
+ if(win != NULL) {
+ gtk_widget_destroy(win);
+ g_object_set_data(G_OBJECT(w_main), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+ }
+}
+
+static gboolean
+filter_te_focus_out_cb(GtkWidget *filter_te _U_,
+ GdkEvent *event _U_,
+ gpointer data)
+{
+ GtkWidget *win;
+
+ win = g_object_get_data(G_OBJECT(data), E_FILT_AUTOCOMP_PTR_KEY);
+ if(win != NULL) {
+ gtk_widget_destroy(win);
+ g_object_set_data(G_OBJECT(data), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+check_select_region (GtkWidget *filter_te, GtkWidget *popup_win,
+ const gchar *string, unsigned int str_len)
+{
+ gint pos1 = gtk_editable_get_position(GTK_EDITABLE(filter_te));
+ gint pos2 = pos1 + (gint) strlen(string) - str_len;
+ gint pos3 = pos1;
+
+ if (pos2 > pos1) {
+ gtk_editable_insert_text(GTK_EDITABLE(filter_te), &string[str_len-1],
+ pos2-pos1+1, &pos3);
+ gtk_editable_set_position(GTK_EDITABLE(filter_te), pos1+1);
+ gtk_editable_select_region(GTK_EDITABLE(filter_te), pos1+1, pos2+1);
+ gtk_widget_hide (popup_win);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+init_autocompletion_list(GtkWidget *list)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkListStore *store;
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "text", 0, NULL);
+
+ gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
+
+ store = gtk_list_store_new(1, G_TYPE_STRING);
+
+ gtk_tree_view_set_model(GTK_TREE_VIEW(list), GTK_TREE_MODEL(store));
+
+ g_object_unref(store);
+}
+
+static void
+add_to_autocompletion_list(GtkWidget *list, const gchar *str)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
+
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, 0, str, -1);
+}
+
+static gboolean
+autocompletion_list_lookup(GtkWidget *filter_te, GtkWidget *popup_win, GtkWidget *list,
+ const gchar *str, gboolean *stop_propagation)
+{
+ GtkRequisition requisition;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkAllocation popup_win_alloc;
+ gchar *curr_str;
+ unsigned int str_len = (unsigned int) strlen(str);
+ gchar *first = NULL;
+ gint count = 0;
+ gboolean loop = TRUE;
+ gboolean exact_match = FALSE;
+
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
+
+ if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
+
+ do {
+
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 0, &curr_str, -1);
+
+ if( !g_ascii_strncasecmp(str, curr_str, str_len) ) {
+ loop = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
+ if (strlen(curr_str) == str_len) {
+ exact_match = TRUE;
+ }
+ count++;
+ if (count == 1)
+ first = g_strdup (curr_str);
+ } else {
+ loop = gtk_list_store_remove(store, &iter);
+ }
+
+ g_free(curr_str);
+
+ } while( loop );
+
+ if (count == 1 && !exact_match && strncmp(str, first, str_len) == 0) {
+ /* Only one match (not exact) with correct case */
+ *stop_propagation = check_select_region(filter_te, popup_win, first, str_len);
+ }
+
+ /* Don't show an autocompletion-list with only one entry with exact match */
+ if ((count == 1 && exact_match && strncmp(str, first, str_len) == 0) ||
+ !gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter))
+ {
+ g_free (first);
+ return FALSE;
+ }
+
+ g_free (first);
+
+ gtk_tree_view_columns_autosize(GTK_TREE_VIEW(list));
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_get_preferred_size(list, &requisition, NULL);
+#else
+ gtk_widget_size_request(list, &requisition);
+#endif
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_get_allocation(popup_win, &popup_win_alloc);
+#else
+ popup_win_alloc = popup_win->allocation;
+#endif
+
+ gtk_widget_set_size_request(popup_win, popup_win_alloc.width,
+ (requisition.height<200? requisition.height+8:200));
+ gtk_window_resize(GTK_WINDOW(popup_win), popup_win_alloc.width,
+ (requisition.height<200? requisition.height+8:200));
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean
+filter_string_te_key_pressed_cb(GtkWidget *filter_te, GdkEventKey *event, gpointer user_data _U_)
+{
+ GtkWidget *popup_win;
+ GtkWidget *w_toplevel;
+ GtkWidget *treeview;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ gchar* prefix;
+ gchar* prefix_start;
+ gboolean stop_propagation = FALSE;
+ guint k;
+ gchar ckey;
+ gint pos;
+
+ w_toplevel = gtk_widget_get_toplevel(filter_te);
+
+ popup_win = g_object_get_data(G_OBJECT(w_toplevel), E_FILT_AUTOCOMP_PTR_KEY);
+
+ k = event->keyval;
+ ckey = event->string[0];
+
+ /* If the pressed key is SHIFT then we have nothing to do with the pressed key. */
+ if( k == GDK_Shift_L || k == GDK_Shift_R)
+ return FALSE;
+
+ if (popup_win)
+ gtk_widget_show(popup_win);
+
+ pos = gtk_editable_get_position(GTK_EDITABLE(filter_te));
+ if (g_ascii_isalnum(ckey) ||
+ k == GDK_KP_Decimal || k == GDK_period ||
+ k == GDK_underscore || k == GDK_minus)
+ {
+ /* Ensure we delete the selected text */
+ gtk_editable_delete_selection(GTK_EDITABLE(filter_te));
+ } else if (k == GDK_Return || k == GDK_KP_Enter) {
+ /* Remove selection */
+ gtk_editable_select_region(GTK_EDITABLE(filter_te), pos, pos);
+ }
+ /* get the string from filter_te, start from 0 till cursor's position */
+ prefix_start = gtk_editable_get_chars(GTK_EDITABLE(filter_te), 0, pos);
+
+ /* If the pressed key is non-alphanumeric or one of the keys specified
+ * in the condition (decimal, period...) then destroy popup window.
+ **/
+ if( !g_ascii_isalnum(ckey) &&
+ k != GDK_KP_Decimal && k != GDK_period &&
+ k != GDK_underscore && k != GDK_minus &&
+ k != GDK_space && k != GDK_Return && k != GDK_KP_Enter &&
+ k != GDK_Page_Down && k != GDK_Down && k != GDK_Page_Up && k != GDK_Up &&
+ k != GDK_BackSpace)
+ {
+ if (popup_win) {
+ gtk_widget_destroy(popup_win);
+ g_object_set_data(G_OBJECT(w_toplevel), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+ }
+ return FALSE;
+ }
+
+ /* Let prefix points to the first char that is not aphanumeric,'.', '_' or '-',
+ * start from the end of filter_te_str.
+ **/
+ prefix = prefix_start + strlen(prefix_start);
+ while(prefix != prefix_start) {
+ prefix--;
+ if(!g_ascii_isalnum((*prefix)) && (*prefix) != '.' && (*prefix) != '_' && (*prefix) != '-') {
+ prefix++;
+ break;
+ }
+ }
+
+ /* Now, if the pressed key is decimal or period, and there is no period or
+ * decimal before it in prefix then construct the popup window.
+ *
+ * If the pressed key is backspace, and there is no existing popup window
+ * then construct the popup window again.
+ **/
+ if(k==GDK_period || k==GDK_KP_Decimal) {
+ if( !strchr(prefix, '.') || !popup_win) {
+
+ gchar* name_with_period;
+
+ if (popup_win) {
+ gtk_widget_destroy (popup_win);
+ }
+
+ name_with_period = g_strconcat(prefix, event->string, NULL);
+ popup_win = filter_autocomplete_new(filter_te, name_with_period, FALSE, &stop_propagation);
+ g_object_set_data(G_OBJECT(w_toplevel), E_FILT_AUTOCOMP_PTR_KEY, popup_win);
+
+ g_free(name_with_period);
+ g_free(prefix_start);
+
+ return stop_propagation;
+ }
+ } else if(k==GDK_BackSpace && !popup_win) {
+
+ if(strlen(prefix) > 1) {
+ /* Delete the last character in the prefix string */
+ prefix[strlen(prefix)-1] = '\0';
+ if(strchr(prefix, '.')) {
+ popup_win = filter_autocomplete_new(filter_te, prefix, FALSE, NULL);
+ g_object_set_data(G_OBJECT(w_toplevel), E_FILT_AUTOCOMP_PTR_KEY, popup_win);
+ } else if(strlen(prefix) && is_protocol_name_being_typed(filter_te, (int) strlen(prefix)+2)) {
+ popup_win = filter_autocomplete_new(filter_te, prefix, TRUE, NULL);
+ g_object_set_data(G_OBJECT(w_toplevel), E_FILT_AUTOCOMP_PTR_KEY, popup_win);
+ }
+ }
+
+ g_free(prefix_start);
+
+ return FALSE;
+ } else if(g_ascii_isalnum(ckey) && !popup_win) {
+ gchar *name = g_strconcat(prefix, event->string, NULL);
+
+ if( !strchr(name, '.') && is_protocol_name_being_typed(filter_te, (int) strlen(name)) ) {
+ popup_win = filter_autocomplete_new(filter_te, name, TRUE, &stop_propagation);
+ g_object_set_data(G_OBJECT(w_toplevel), E_FILT_AUTOCOMP_PTR_KEY, popup_win);
+ }
+
+ g_free(name);
+ g_free(prefix_start);
+
+ return stop_propagation;
+ }
+
+ /* If the popup window hasn't been constructed yet then we have nothing to do */
+ if( !popup_win ) {
+ g_free(prefix_start);
+
+ return FALSE;
+ }
+
+
+ treeview = g_object_get_data(G_OBJECT(popup_win), E_FILT_AUTOCOMP_TREE_KEY);
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
+
+ switch(k)
+ {
+ /* a better implementation for UP/DOWN keys would be moving the control to the popup'ed window, and letting
+ * the treeview handle the up, down actions directly and return the control to the filter text once
+ * the user press Enter or any key except for UP, DOWN arrows. * I wasn't able to find a way to do that. *
+ **/
+ case GDK_Page_Down:
+ case GDK_Down:
+ if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
+ if (gtk_tree_model_iter_next(model, &iter)) {
+ if (k == GDK_Page_Down) {
+ /* Skip up to 8 entries */
+ GtkTreeIter last_iter;
+ gint count = 0;
+ do {
+ last_iter = iter;
+ } while (++count < 8 && gtk_tree_model_iter_next(model, &iter));
+ iter = last_iter;
+ }
+ gtk_tree_selection_select_iter(GTK_TREE_SELECTION(selection), &iter);
+ path = gtk_tree_model_get_path(model, &iter);
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(treeview), path,
+ NULL, FALSE, 0, 0);
+ gtk_tree_path_free(path);
+ } else {
+ gtk_tree_selection_unselect_all(selection);
+ }
+ } else if (gtk_tree_model_get_iter_first(model, &iter)) {
+ gtk_tree_selection_select_iter(GTK_TREE_SELECTION(selection), &iter);
+ path = gtk_tree_model_get_path(model, &iter);
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(treeview), path,
+ NULL, FALSE, 0, 0);
+ gtk_tree_path_free(path);
+ }
+
+ g_free(prefix_start);
+
+ /* stop event propagation */
+ return TRUE;
+
+ case GDK_Page_Up:
+ case GDK_Up:
+ {
+ GtkTreeIter last_iter;
+
+ if (gtk_tree_selection_get_selected(selection, &model, &iter) ) {
+ path = gtk_tree_model_get_path(model, &iter);
+
+ if (gtk_tree_path_prev(path)) {
+ if (k == GDK_Page_Up) {
+ /* Skip up to 8 entries */
+ GtkTreePath *last_path;
+ gint count = 0;
+ do {
+ last_path = path;
+ } while (++count < 8 && gtk_tree_path_prev(path));
+ path = last_path;
+ }
+ gtk_tree_selection_select_path(GTK_TREE_SELECTION(selection), path);
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(treeview), path, NULL, FALSE, 0, 0);
+ } else {
+ gtk_tree_selection_unselect_iter(selection, &iter);
+ }
+ gtk_tree_path_free(path);
+ } else if (gtk_tree_model_get_iter_first(model, &iter)) {
+ do {
+ last_iter = iter;
+ } while (gtk_tree_model_iter_next(model, &iter));
+ gtk_tree_selection_select_iter(GTK_TREE_SELECTION(selection), &last_iter);
+ path = gtk_tree_model_get_path(model, &last_iter);
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(treeview), path,
+ NULL, FALSE, 0, 0);
+ gtk_tree_path_free(path);
+ }
+
+ g_free(prefix_start);
+
+ /* stop event propagation */
+ return TRUE;
+ }
+
+
+ /* if pressed key is Space or Enter then autocomplete protocol string */
+ case GDK_space:
+ case GDK_Return:
+ case GDK_KP_Enter:
+
+ if(gtk_tree_selection_get_selected(selection, &model, &iter) ) {
+ gchar *value;
+
+ /* Do not autocomplete protocols with space yet, because we can be in
+ * a operator or a value field.
+ **/
+ if(k != GDK_space || strchr(prefix, '.')) {
+ /* Use chosen string */
+ gtk_tree_model_get(model, &iter, 0, &value, -1);
+ autocomplete_protocol_string(filter_te, value);
+ g_free(value);
+ }
+ if(k != GDK_space) {
+ stop_propagation = TRUE; /* stop event propagation */
+ }
+ }
+
+ /* Lose popup */
+ gtk_widget_destroy(popup_win);
+ g_object_set_data(G_OBJECT(w_toplevel), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+ break;
+
+ case GDK_BackSpace:
+ filter_autocomplete_handle_backspace(filter_te, treeview, popup_win, prefix, w_toplevel);
+ break;
+
+ default: {
+ gchar* updated_str;
+
+ updated_str = g_strconcat(prefix, event->string, NULL);
+ if( !autocompletion_list_lookup(filter_te, popup_win, treeview, updated_str, &stop_propagation) ) {
+ /* function returned false, ie the list is empty -> close popup */
+ gtk_widget_destroy(popup_win);
+ g_object_set_data(G_OBJECT(w_toplevel), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+ }
+
+ g_free(updated_str);
+ }
+
+ }
+
+ g_free(prefix_start);
+
+ return stop_propagation;
+}
+
+/*
+ * In my implementation, I'm looking for fields that match the protocol name in the whole fields list
+ * and not only restrict the process by returning all the fields of the protocol that match the prefix using
+ * 'proto_get_id_by_filter_name(protocol_name)'; because I have noticed that some of the fields
+ * have a prefix different than its parent protocol; for example SIP protocol had this field raw_sip.line despite
+ * that there is a protocol called RAW_SIP which it should be associated with it.
+ * so the unorganized fields and nonexistent of a standardized protocols and fields naming rules prevent me from
+ * implementing the autocomplete in an optimized way.
+ **/
+static gboolean
+build_autocompletion_list(GtkWidget *filter_te, GtkWidget *treeview, GtkWidget *popup_win,
+ const gchar *protocol_name, gboolean protocols_only, gboolean *stop_propagation)
+{
+ void *cookie, *cookie2;
+ protocol_t *protocol;
+ unsigned int protocol_name_len;
+ header_field_info *hfinfo;
+ gint count = 0;
+ gboolean exact_match = FALSE;
+ const gchar *first = NULL;
+ int i;
+
+ protocol_name_len = (unsigned int) strlen(protocol_name);
+
+ /* Force load protocol fields, if not already done */
+ if(protocol_name[protocol_name_len-1] == '.')
+ proto_registrar_get_byname(protocol_name);
+
+ /* Walk protocols list */
+ for (i = proto_get_first_protocol(&cookie); i != -1; i = proto_get_next_protocol(&cookie)) {
+
+ protocol = find_protocol_by_id(i);
+
+ if (!proto_is_protocol_enabled(protocol))
+ continue;
+
+ if (protocols_only) {
+ const gchar *name = proto_get_protocol_filter_name (i);
+
+ if (!g_ascii_strncasecmp(protocol_name, name, protocol_name_len)) {
+ add_to_autocompletion_list(treeview, name);
+ if (strlen(name) == protocol_name_len) {
+ exact_match = TRUE;
+ }
+ count++;
+ if (count == 1)
+ first = name;
+ }
+ } else {
+
+ for (hfinfo = proto_get_first_protocol_field(i, &cookie2);
+ hfinfo != NULL;
+ hfinfo = proto_get_next_protocol_field(&cookie2))
+ {
+ if (hfinfo->same_name_prev != NULL) /* ignore duplicate names */
+ continue;
+
+ if(!g_ascii_strncasecmp(protocol_name, hfinfo->abbrev, protocol_name_len)) {
+ add_to_autocompletion_list(treeview, hfinfo->abbrev);
+ if (strlen(hfinfo->abbrev) == protocol_name_len) {
+ exact_match = TRUE;
+ }
+ count++;
+ if (count == 1)
+ first = hfinfo->abbrev;
+ }
+ }
+ }
+ }
+
+ if (count == 1 && !exact_match && stop_propagation &&
+ strncmp(protocol_name, first, protocol_name_len) == 0)
+ {
+ /* Only one match (not exact) with correct case */
+ *stop_propagation = check_select_region(filter_te, popup_win, first, protocol_name_len);
+ }
+
+ /* Don't show an empty autocompletion-list or
+ * an autocompletion-list with only one entry with exact match
+ **/
+ if (count == 0 || (count == 1 && exact_match &&
+ strncmp(protocol_name, first, protocol_name_len) == 0))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+filter_autocomplete_disable_sorting(GtkTreeModel *model)
+{
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model),
+ GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
+}
+
+static void
+filter_autocomplete_enable_sorting(GtkTreeModel *model)
+{
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), 0, GTK_SORT_ASCENDING);
+}
+
+static GtkWidget *
+filter_autocomplete_new(GtkWidget *filter_te, const gchar *protocol_name,
+ gboolean protocols_only, gboolean *stop_propagation)
+{
+ GtkWidget *popup_win;
+ GtkWidget *treeview;
+ GtkWidget *filter_sc;
+ gint x_pos, y_pos;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkRequisition requisition;
+ GtkWidget *w_toplevel;
+ GtkAllocation filter_te_alloc;
+
+ w_toplevel = gtk_widget_get_toplevel(filter_te);
+
+ /* Create popup window */
+ popup_win = gtk_window_new (GTK_WINDOW_POPUP);
+
+ /* Create scrolled window */
+ filter_sc = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (filter_sc), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(filter_sc), GTK_SHADOW_IN);
+ gtk_container_add(GTK_CONTAINER(popup_win), filter_sc);
+
+ /* Create tree view */
+ treeview = gtk_tree_view_new();
+ gtk_tree_view_set_hover_selection(GTK_TREE_VIEW(treeview), TRUE);
+ init_autocompletion_list(treeview);
+ g_object_set_data(G_OBJECT(popup_win), E_FILT_AUTOCOMP_TREE_KEY, treeview);
+
+ /* Build list */
+ if (!build_autocompletion_list(filter_te, treeview, popup_win, protocol_name, protocols_only, stop_propagation)) {
+ gtk_widget_destroy(popup_win);
+ return NULL;
+ }
+
+ /* Sort treeview */
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
+ if(model)
+ filter_autocomplete_enable_sorting(model);
+
+ gtk_container_add (GTK_CONTAINER (filter_sc), treeview);
+
+ g_signal_connect(treeview, "row-activated", G_CALLBACK(autoc_filter_row_activated_cb), filter_te);
+ g_signal_connect(filter_te, "focus-out-event", G_CALLBACK(filter_te_focus_out_cb), w_toplevel);
+ g_signal_connect(popup_win, "destroy", G_CALLBACK(filter_autocomplete_win_destroy_cb), NULL);
+
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_get_preferred_size(treeview, &requisition, NULL);
+#else
+ gtk_widget_size_request(treeview, &requisition);
+#endif
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_get_allocation(filter_te, &filter_te_alloc);
+#else
+ filter_te_alloc = filter_te->allocation;
+#endif
+
+ gtk_widget_set_size_request(popup_win, filter_te_alloc.width,
+ (requisition.height<200? requisition.height+8:200));
+ gtk_window_resize(GTK_WINDOW(popup_win), filter_te_alloc.width,
+ (requisition.height<200? requisition.height+8:200));
+
+ gdk_window_get_origin(gtk_widget_get_window(filter_te), &x_pos, &y_pos);
+#if GTK_CHECK_VERSION(3,0,0)
+ x_pos += filter_te_alloc.x;
+ y_pos += (filter_te_alloc.y + filter_te_alloc.height);
+#else
+ y_pos += filter_te_alloc.height;
+#endif
+ gtk_window_move(GTK_WINDOW(popup_win), x_pos, y_pos);
+
+ gtk_widget_show_all (popup_win);
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
+ gtk_tree_selection_unselect_all(selection);
+
+ return popup_win;
+}
+
+static void
+filter_autocomplete_handle_backspace(GtkWidget *filter_te, GtkWidget *list, GtkWidget *popup_win,
+ gchar *prefix, GtkWidget *main_win)
+{
+ GtkTreeModel *model;
+ GtkListStore *store;
+ GtkRequisition requisition;
+ size_t prefix_len;
+ gboolean protocols_only = FALSE;
+ GtkAllocation popup_win_alloc;
+
+ prefix_len = strlen(prefix);
+
+ if (prefix_len <= 1) {
+ /* Remove the popup window for protocols */
+ gtk_widget_destroy(popup_win);
+ g_object_set_data(G_OBJECT(main_win), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+ return;
+ }
+
+ /* Delete the last character in the prefix string */
+ prefix_len--;
+ prefix[prefix_len] = '\0';
+
+ if(strchr(prefix, '.') == NULL) {
+ protocols_only = TRUE;
+ }
+
+ /* Empty list */
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
+ store = GTK_LIST_STORE(model);
+ gtk_list_store_clear(store);
+
+ /* Disable sorting */
+ filter_autocomplete_disable_sorting(model);
+
+ /* Build new list */
+ if (!build_autocompletion_list(filter_te, list, popup_win, prefix, protocols_only, NULL)) {
+ gtk_widget_destroy(popup_win);
+ g_object_set_data(G_OBJECT(main_win), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+ return;
+ }
+
+ /* Enable sorting */
+ filter_autocomplete_enable_sorting(model);
+
+ gtk_tree_view_columns_autosize(GTK_TREE_VIEW(list));
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_get_preferred_size(list, &requisition, NULL);
+#else
+ gtk_widget_size_request(list, &requisition);
+#endif
+
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_get_allocation(popup_win, &popup_win_alloc);
+#else
+ popup_win_alloc = popup_win->allocation;
+#endif
+
+ /* XXX use gtk_window_set_default_size()? */
+ gtk_widget_set_size_request(popup_win, popup_win_alloc.width,
+ (requisition.height<200? requisition.height+8:200));
+ gtk_window_resize(GTK_WINDOW(popup_win), popup_win_alloc.width,
+ (requisition.height<200? requisition.height+8:200));
+}
+
+static void
+filter_autocomplete_win_destroy_cb(GtkWidget *win, gpointer data _U_)
+{
+ /* tell that the autocomplete window doesn't exist anymore */
+ g_object_set_data(G_OBJECT(win), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+}
+
+gboolean
+filter_parent_dlg_key_pressed_cb(GtkWidget *win, GdkEventKey *event, gpointer user_data _U_)
+{
+ GtkWidget *popup_win;
+
+ popup_win = g_object_get_data(G_OBJECT(win), E_FILT_AUTOCOMP_PTR_KEY);
+
+ if(popup_win && event->keyval == GDK_Escape) {
+ gtk_widget_destroy(popup_win);
+ g_object_set_data(G_OBJECT(win), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+
+ /* stop event propagation */
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/ui/gtk/filter_autocomplete.h b/ui/gtk/filter_autocomplete.h
new file mode 100644
index 0000000000..b7f6dada0c
--- /dev/null
+++ b/ui/gtk/filter_autocomplete.h
@@ -0,0 +1,55 @@
+/* filter_autocomplete.h
+ * Definitions for filter autocomplete
+ *
+ * Copyright 2008, Bahaa Naamneh <b.naamneh@gmail.com>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _FILTER_AUTO_COMPLETE_H_
+#define _FILTER_AUTO_COMPLETE_H_
+
+
+#define E_FILT_AUTOCOMP_PTR_KEY "filter_autocomplete_window"
+
+/** @file
+ * "Filter Auto Complete" dialog box.
+ * @ingroup dialog_group
+ */
+
+/** Callback function that is called when a "key-press-event" signal occur.
+ *
+ * @param filter_te text-editing filter widget
+ * @param event
+ * @param user_data pointer to user_data (unused)
+ */
+extern gboolean filter_string_te_key_pressed_cb(GtkWidget *filter_te, GdkEventKey *event, gpointer user_data _U_);
+
+/** Callback function that is called when a "key-press-event" signal occur.
+ *
+ * @param win parent window of the text-editing filter widget
+ * @param event
+ * @param user_data pointer to user_data (unused)
+ */
+extern gboolean filter_parent_dlg_key_pressed_cb(GtkWidget *win, GdkEventKey *event, gpointer user_data _U_);
+
+
+#endif
diff --git a/ui/gtk/filter_dlg.c b/ui/gtk/filter_dlg.c
new file mode 100644
index 0000000000..1349826b06
--- /dev/null
+++ b/ui/gtk/filter_dlg.c
@@ -0,0 +1,1395 @@
+/* filter_dlg.c
+ * Dialog boxes for (display and capture) filter editing
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/filesystem.h>
+#include <epan/prefs.h>
+#include <epan/proto.h>
+
+#include "../filters.h"
+#include "../simple_dialog.h"
+#include "../main_statusbar.h"
+
+#include "ui/gtk/main.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dfilter_expr_dlg.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/filter_autocomplete.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+#define E_FILT_DIALOG_PTR_KEY "filter_dialog_ptr"
+#define E_FILT_BUTTON_PTR_KEY "filter_button_ptr"
+#define E_FILT_PARENT_FILTER_TE_KEY "filter_parent_filter_te"
+#define E_FILT_CONSTRUCT_ARGS_KEY "filter_construct_args"
+#define E_FILT_LIST_ITEM_MODEL_KEY "filter_list_item_model"
+#define E_FILT_LBL_KEY "filter_label"
+#define E_FILT_FILTER_L_KEY "filter_filter_l"
+#define E_FILT_CHG_BT_KEY "filter_chg_bt"
+#define E_FILT_COPY_BT_KEY "filter_copy_bt"
+#define E_FILT_DEL_BT_KEY "filter_del_bt"
+#define E_FILT_NAME_TE_KEY "filter_name_te"
+#define E_FILT_DBLFUNC_KEY "filter_dblfunc"
+#define E_FILT_DBLARG_KEY "filter_dblarg"
+#define E_FILT_DBLACTIVATE_KEY "filter_dblactivate"
+
+typedef struct _filter_cb_data {
+ GList *fl;
+ GtkWidget *win;
+} filter_cb_data;
+
+static GtkWidget *filter_dialog_new(GtkWidget *button, GtkWidget *filter_te,
+ filter_list_type_t list_type,
+ construct_args_t *construct_args);
+static void filter_dlg_dclick(GtkWidget *dummy, gpointer main_w_arg,
+ gpointer activate);
+static void filter_dlg_ok_cb(GtkWidget *ok_bt, gpointer data);
+static void filter_dlg_apply_cb(GtkWidget *apply_bt, gpointer data);
+static void filter_apply(GtkWidget *main_w, gboolean destroy);
+static void filter_dlg_save(filter_list_type_t list_type);
+static void filter_dlg_save_cb(GtkWidget *save_bt, gpointer parent_w);
+static void filter_dlg_destroy_cb(GtkWidget *win, gpointer data);
+
+static gboolean
+filter_dlg_delete_event_cb(GtkWidget *prefs_w, GdkEvent *event, gpointer data);
+static void
+filter_dlg_cancel_cb(GtkWidget *cancel_bt, gpointer data);
+
+static gboolean filter_sel_list_button_cb(GtkWidget *, GdkEventButton *,
+ gpointer);
+static void filter_sel_list_cb(GtkTreeSelection *, gpointer);
+static void filter_new_bt_clicked_cb(GtkWidget *, gpointer);
+static void filter_del_bt_clicked_cb(GtkWidget *, gpointer);
+static void filter_name_te_changed_cb(GtkWidget *, gpointer);
+
+#ifdef HAVE_LIBPCAP
+/* Create a filter dialog for constructing a capture filter.
+
+ This is to be used as a callback for a button next to a text entry box,
+ which, when clicked, pops up this dialog to allow you to construct a
+ display filter by browsing the list of saved filters (the dialog
+ for constructing expressions assumes display filter syntax, not
+ capture filter syntax). The "OK" button sets the text entry box to the
+ constructed filter and activates that text entry box (which should have
+ no effect in the main capture dialog); this dialog is then dismissed. */
+void
+capture_filter_construct_cb(GtkWidget *w, gpointer user_data _U_)
+{
+ GtkWidget *filter_browse_w;
+ GtkWidget *parent_filter_te;
+ /* No Apply button, and "OK" just sets our text widget, it doesn't
+ activate it (i.e., it doesn't cause us to try to open the file). */
+ static construct_args_t args = {
+ "Wireshark: Capture Filter",
+ FALSE,
+ FALSE,
+ FALSE
+ };
+
+ /* Has a filter dialog box already been opened for that button? */
+ filter_browse_w = g_object_get_data(G_OBJECT(w), E_FILT_DIALOG_PTR_KEY);
+
+ if (filter_browse_w != NULL) {
+ /* Yes. Just re-activate that dialog box. */
+ reactivate_window(filter_browse_w);
+ return;
+ }
+
+ /* No. Get the text entry attached to the button. */
+ parent_filter_te = g_object_get_data(G_OBJECT(w), E_FILT_TE_PTR_KEY);
+
+ /* Now create a new dialog, without an "Add Expression..." button. */
+ filter_dialog_new(w, parent_filter_te, CFILTER_LIST, &args);
+}
+#endif
+
+/* Create a filter dialog for constructing a display filter.
+
+ This is to be used as a callback for a button next to a text entry box,
+ which, when clicked, pops up this dialog to allow you to construct a
+ display filter by browsing the list of saved filters and/or by adding
+ test expressions constructed with another dialog. The "OK" button
+ sets the text entry box to the constructed filter and activates that
+ text entry box, causing the filter to be used; this dialog is then
+ dismissed.
+
+ If "wants_apply_button" is non-null, we add an "Apply" button that
+ acts like "OK" but doesn't dismiss this dialog. */
+void
+display_filter_construct_cb(GtkWidget *w, gpointer construct_args_ptr)
+{
+ construct_args_t *construct_args = construct_args_ptr;
+ GtkWidget *filter_browse_w;
+ GtkWidget *parent_filter_te;
+
+ /* Has a filter dialog box already been opened for the button? */
+ filter_browse_w = g_object_get_data(G_OBJECT(w), E_FILT_DIALOG_PTR_KEY);
+
+ if (filter_browse_w != NULL) {
+ /* Yes. Just re-activate that dialog box. */
+ reactivate_window(filter_browse_w);
+ return;
+ }
+
+ /* No. Get the text entry attached to the button. */
+ parent_filter_te = g_object_get_data(G_OBJECT(w), E_FILT_TE_PTR_KEY);
+
+ /* Now create a new dialog, possibly with an "Apply" button, and
+ definitely with an "Add Expression..." button. */
+ filter_dialog_new(w, parent_filter_te, DFILTER_LIST, construct_args);
+}
+
+/* Should be called when a button that creates filters is destroyed; it
+ destroys any filter dialog created by that button. */
+void
+filter_button_destroy_cb(GtkWidget *button, gpointer user_data _U_)
+{
+ GtkWidget *filter_w;
+
+ /* Is there a filter edit/selection dialog associated with this
+ button? */
+ filter_w = g_object_get_data(G_OBJECT(button), E_FILT_DIALOG_PTR_KEY);
+
+ if (filter_w != NULL) {
+ /* Yes. Break the association, and destroy the dialog. */
+ g_object_set_data(G_OBJECT(button), E_FILT_DIALOG_PTR_KEY, NULL);
+ window_destroy(filter_w);
+ }
+}
+
+#ifdef HAVE_LIBPCAP
+static GtkWidget *global_cfilter_w;
+
+/* Create a filter dialog for editing capture filters; this is to be used
+ as a callback for menu items, toolbars, etc.. */
+void
+cfilter_dialog_cb(GtkWidget *w _U_)
+{
+ /* No Apply button, and there's no text widget to set, much less
+ activate, on "OK". */
+ static construct_args_t args = {
+ "Wireshark: Capture Filter",
+ FALSE,
+ FALSE,
+ FALSE
+ };
+
+ /* Has a filter dialog box already been opened for editing
+ capture filters? */
+ if (global_cfilter_w != NULL) {
+ /* Yes. Just reactivate it. */
+ reactivate_window(global_cfilter_w);
+ return;
+ }
+
+ /*
+ * No. Create one; we didn't pop this up as a result of pressing
+ * a button next to some text entry field, so don't associate it
+ * with a text entry field or button.
+ */
+ global_cfilter_w = filter_dialog_new(NULL, NULL, CFILTER_LIST, &args);
+}
+#endif
+
+/* Create a filter dialog for editing display filters; this is to be used
+ as a callback for menu items, toolbars, etc.. */
+void
+dfilter_dialog_cb(GtkWidget *w _U_)
+{
+ static construct_args_t args = {
+ "Wireshark: Display Filter",
+ TRUE,
+ TRUE,
+ FALSE
+ };
+
+ display_filter_construct_cb(g_object_get_data(G_OBJECT(top_level), E_FILT_BT_PTR_KEY), &args);
+}
+
+/* List of capture filter dialogs, so that if the list of filters changes
+ (the model, if you will), we can update all of their lists displaying
+ the filters (the views). */
+static GList *cfilter_dialogs;
+
+/* List of display filter dialogs, so that if the list of filters changes
+ (the model, if you will), we can update all of their lists displaying
+ the filters (the views). */
+static GList *dfilter_dialogs;
+
+static void
+remember_filter_dialog(GtkWidget *main_w, GList **filter_dialogs)
+{
+ *filter_dialogs = g_list_append(*filter_dialogs, main_w);
+}
+
+/* Remove a filter dialog from the specified list of filter_dialogs. */
+static void
+forget_filter_dialog(GtkWidget *main_w, filter_list_type_t list_type)
+{
+ switch (list_type) {
+
+ case CFILTER_EDITED_LIST:
+ cfilter_dialogs = g_list_remove(cfilter_dialogs, main_w);
+ break;
+
+ case DFILTER_EDITED_LIST:
+ dfilter_dialogs = g_list_remove(dfilter_dialogs, main_w);
+ break;
+
+ default:
+ g_assert_not_reached();
+ break;
+ }
+}
+
+/* Get the dialog list corresponding to a particular filter list. */
+static GList *
+get_filter_dialog_list(filter_list_type_t list_type)
+{
+ switch (list_type) {
+
+ case CFILTER_EDITED_LIST:
+ return cfilter_dialogs;
+
+ case DFILTER_EDITED_LIST:
+ return dfilter_dialogs;
+
+ default:
+ g_assert_not_reached();
+ return NULL;
+ }
+}
+
+
+static GtkTreeIter *
+fill_list(GtkWidget *main_w, filter_list_type_t list_type, const gchar *filter_te_str)
+{
+ GList *fl_entry;
+ filter_def *filt;
+ GtkTreeView *filter_l;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkTreeIter *l_select = NULL;
+
+ filter_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY));
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(filter_l));
+
+ /* fill in data */
+ fl_entry = get_filter_list_first(list_type);
+ while (fl_entry != NULL) {
+ filt = (filter_def *) fl_entry->data;
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, 0, filt->name,
+ 1, fl_entry, -1);
+
+ if (filter_te_str && filt->strval) {
+ if (strcmp(filter_te_str, filt->strval) == 0) {
+ /*
+ * XXX - We're assuming that we can just copy a GtkTreeIter
+ * and use it later without any crashes. This may not be a
+ * valid assumption.
+ */
+ l_select = g_memdup(&iter, sizeof(iter));
+ }
+ }
+
+ fl_entry = fl_entry->next;
+ }
+ return l_select;
+}
+
+#if 0
+static void
+clear_list(GtkWidget *main_w) {
+ GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
+ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l));
+
+ gtk_list_store_clear(GTK_LIST_STORE(model));
+}
+#endif /* 0 */
+
+static GtkWidget *
+filter_dialog_new(GtkWidget *button, GtkWidget *parent_filter_te,
+ filter_list_type_t list_type, construct_args_t *construct_args)
+{
+ GtkWidget *main_w, /* main window */
+ *main_vb, /* main container */
+ *bbox, /* button container */
+ *ok_bt, /* "OK" button */
+ *apply_bt, /* "Apply" button */
+ *save_bt, /* "Save" button */
+ *cancel_bt, /* "Cancel" button */
+ *help_bt; /* "Help" button */
+ GtkWidget *filter_vb, /* filter settings box */
+ *props_vb;
+ GtkWidget *top_hb,
+ *list_bb,
+ *new_bt,
+ *del_bt,
+ *filter_sc,
+ *filter_l,
+ *middle_hb,
+ *name_lb,
+ *name_te,
+ *bottom_hb,
+ *filter_lb,
+ *filter_te,
+ *add_expression_bt,
+ *filter_fr,
+ *edit_fr,
+ *props_fr;
+ GdkWindow *parent;
+ static filter_list_type_t cfilter_list_type = CFILTER_EDITED_LIST;
+ static filter_list_type_t dfilter_list_type = DFILTER_EDITED_LIST;
+ filter_list_type_t *filter_list_type_p;
+ GList **filter_dialogs;
+ const gchar *filter_te_str = NULL;
+ GtkListStore *store;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *sel;
+ GtkTreeIter *l_select;
+ gchar *list_name = NULL;
+
+ /* Get a pointer to a static variable holding the type of filter on
+ which we're working, so we can pass that pointer to callback
+ routines. */
+ switch (list_type) {
+
+ case CFILTER_LIST:
+ filter_dialogs = &cfilter_dialogs;
+ filter_list_type_p = &cfilter_list_type;
+ list_type = CFILTER_EDITED_LIST;
+ list_name = "Capture Filter";
+ break;
+
+ case DFILTER_LIST:
+ filter_dialogs = &dfilter_dialogs;
+ filter_list_type_p = &dfilter_list_type;
+ list_type = DFILTER_EDITED_LIST;
+ list_name = "Display Filter";
+ break;
+
+ default:
+ g_assert_not_reached();
+ filter_dialogs = NULL;
+ filter_list_type_p = NULL;
+ break;
+ }
+
+ main_w = dlg_conf_window_new(construct_args->title);
+ gtk_window_set_default_size(GTK_WINDOW(main_w), 400, 400);
+ g_object_set_data(G_OBJECT(main_w), E_FILT_CONSTRUCT_ARGS_KEY, construct_args);
+
+ main_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(main_w), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* Make sure everything is set up */
+ if (parent_filter_te)
+ filter_te_str = gtk_entry_get_text(GTK_ENTRY(parent_filter_te));
+
+ /* Container for each row of widgets */
+ filter_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(filter_vb), 0);
+ gtk_container_add(GTK_CONTAINER(main_vb), filter_vb);
+ gtk_widget_show(filter_vb);
+
+ /* Top row: Buttons and filter list */
+ top_hb = gtk_hbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(filter_vb), top_hb);
+ gtk_widget_show(top_hb);
+
+ edit_fr = gtk_frame_new("Edit");
+ gtk_box_pack_start(GTK_BOX(top_hb), edit_fr, FALSE, FALSE, 0);
+ gtk_widget_show(edit_fr);
+
+ list_bb = gtk_vbox_new(TRUE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(list_bb), 5);
+ gtk_container_add(GTK_CONTAINER(edit_fr), list_bb);
+ gtk_widget_show(list_bb);
+
+ new_bt = gtk_button_new_from_stock(GTK_STOCK_NEW);
+ g_signal_connect(new_bt, "clicked", G_CALLBACK(filter_new_bt_clicked_cb), filter_list_type_p);
+ gtk_widget_show(new_bt);
+ gtk_box_pack_start (GTK_BOX (list_bb), new_bt, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text(new_bt, "Create a new filter at the end of the list (with the current properties)");
+
+ del_bt = gtk_button_new_from_stock(GTK_STOCK_DELETE);
+ gtk_widget_set_sensitive(del_bt, FALSE);
+ g_signal_connect(del_bt, "clicked", G_CALLBACK(filter_del_bt_clicked_cb), filter_list_type_p);
+ g_object_set_data(G_OBJECT(main_w), E_FILT_DEL_BT_KEY, del_bt);
+ gtk_widget_show(del_bt);
+ gtk_box_pack_start (GTK_BOX (list_bb), del_bt, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text(del_bt, "Delete the selected filter");
+
+ filter_fr = gtk_frame_new(list_name);
+ gtk_box_pack_start(GTK_BOX(top_hb), filter_fr, TRUE, TRUE, 0);
+ gtk_widget_show(filter_fr);
+
+ filter_sc = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(filter_sc),
+ GTK_SHADOW_IN);
+
+ gtk_container_set_border_width (GTK_CONTAINER (filter_sc), 5);
+ gtk_container_add(GTK_CONTAINER(filter_fr), filter_sc);
+ gtk_widget_show(filter_sc);
+
+ store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
+ filter_l = tree_view_new(GTK_TREE_MODEL(store));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(filter_l), FALSE);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("", renderer, "text",
+ 0, NULL);
+ gtk_tree_view_column_set_sort_column_id(column, 0);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(filter_l), column);
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+ g_signal_connect(sel, "changed", G_CALLBACK(filter_sel_list_cb), NULL);
+ g_signal_connect(filter_l, "button_press_event", G_CALLBACK(filter_sel_list_button_cb),
+ NULL);
+ g_object_set_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY, filter_l);
+ gtk_container_add(GTK_CONTAINER(filter_sc), filter_l);
+ gtk_widget_show(filter_l);
+
+ g_object_set_data(G_OBJECT(filter_l), E_FILT_DBLFUNC_KEY, filter_dlg_dclick);
+ g_object_set_data(G_OBJECT(filter_l), E_FILT_DBLARG_KEY, main_w);
+ /* This is a Boolean, but we make it a non-null pointer for TRUE
+ and a null pointer for FALSE, as object data is a pointer. */
+ g_object_set_data(G_OBJECT(filter_l), E_FILT_DBLACTIVATE_KEY,
+ construct_args->activate_on_ok ? "" : NULL);
+
+ /* fill in data */
+ l_select = fill_list(main_w, list_type, filter_te_str);
+
+ g_object_unref(G_OBJECT(store));
+
+
+ props_fr = gtk_frame_new("Properties");
+ gtk_box_pack_start(GTK_BOX(filter_vb), props_fr, FALSE, FALSE, 0);
+ gtk_widget_show(props_fr);
+
+ props_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(props_vb), 5);
+ gtk_container_add(GTK_CONTAINER(props_fr), props_vb);
+ gtk_widget_show(props_vb);
+
+ /* row: Filter name entry */
+ middle_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(props_vb), middle_hb);
+ gtk_widget_show(middle_hb);
+
+ name_lb = gtk_label_new("Filter name:");
+ gtk_box_pack_start(GTK_BOX(middle_hb), name_lb, FALSE, FALSE, 0);
+ gtk_widget_show(name_lb);
+
+ name_te = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(middle_hb), name_te, TRUE, TRUE, 0);
+ g_object_set_data(G_OBJECT(main_w), E_FILT_NAME_TE_KEY, name_te);
+ g_signal_connect(name_te, "changed", G_CALLBACK(filter_name_te_changed_cb), filter_list_type_p);
+ gtk_widget_show(name_te);
+
+ /* row: Filter text entry */
+ bottom_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(props_vb), bottom_hb);
+ gtk_widget_show(bottom_hb);
+
+ filter_lb = gtk_label_new("Filter string:");
+ gtk_box_pack_start(GTK_BOX(bottom_hb), filter_lb, FALSE, FALSE, 0);
+ gtk_widget_show(filter_lb);
+
+ filter_te = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(bottom_hb), filter_te, TRUE, TRUE, 0);
+ g_object_set_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY, filter_te);
+ g_signal_connect(filter_te, "changed", G_CALLBACK(filter_name_te_changed_cb), filter_list_type_p);
+ if (list_type == DFILTER_EDITED_LIST) {
+ colorize_filter_te_as_empty(filter_te);
+
+ g_object_set_data(G_OBJECT(main_w), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+ g_signal_connect(filter_te, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
+ g_signal_connect(main_w, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
+ }
+ gtk_widget_show(filter_te);
+
+ g_object_set_data(G_OBJECT(main_w), E_FILT_PARENT_FILTER_TE_KEY, parent_filter_te);
+
+ if (list_type == DFILTER_EDITED_LIST) {
+ gtk_widget_set_tooltip_text(filter_te,
+ "Enter a display filter. "
+ "The background color of this field is changed by a continuous syntax check"
+ " (green is valid, red is invalid, yellow may have unexpected results).");
+
+ /* Create the "Add Expression..." button, to pop up a dialog
+ for constructing filter comparison expressions. */
+ add_expression_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_ADD_EXPRESSION);
+ g_signal_connect(add_expression_bt, "clicked", G_CALLBACK(filter_add_expr_bt_cb), main_w);
+ gtk_box_pack_start(GTK_BOX(bottom_hb), add_expression_bt, FALSE, FALSE, 0);
+ gtk_widget_show(add_expression_bt);
+ gtk_widget_set_tooltip_text(add_expression_bt, "Add an expression to the filter string");
+ }
+
+
+ /* button row (create all possible buttons and hide the unrequired later - it's a lot easier) */
+ bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_SAVE, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 5);
+ gtk_widget_show(bbox);
+
+ ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(filter_dlg_ok_cb), filter_list_type_p);
+ gtk_widget_set_tooltip_text(ok_bt, "Apply the filters and close this dialog");
+
+ /* Catch the "activate" signal on the filter name and filter
+ expression text entries, so that if the user types Return
+ there, we act as if the "OK" button had been selected, as
+ happens if Return is typed if some widget that *doesn't*
+ handle the Return key has the input focus. */
+ if (parent_filter_te != NULL) {
+ dlg_set_activate(name_te, ok_bt);
+ dlg_set_activate(filter_te, ok_bt);
+ }
+
+ apply_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_APPLY);
+ g_signal_connect(apply_bt, "clicked", G_CALLBACK(filter_dlg_apply_cb), filter_list_type_p);
+ gtk_widget_set_tooltip_text(apply_bt, "Apply the filters and keep this dialog open");
+
+ save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
+ g_signal_connect(save_bt, "clicked", G_CALLBACK(filter_dlg_save_cb), filter_list_type_p);
+ gtk_widget_set_tooltip_text(save_bt, "Save the filters permanently and keep this dialog open");
+
+ cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ gtk_widget_set_tooltip_text(cancel_bt, "Cancel the changes");
+ g_signal_connect(cancel_bt, "clicked", G_CALLBACK(filter_dlg_cancel_cb), filter_list_type_p);
+ window_set_cancel_button(main_w, cancel_bt, NULL);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ if (list_type == CFILTER_EDITED_LIST) {
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_CAPTURE_FILTERS_DIALOG);
+ } else {
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_DISPLAY_FILTERS_DIALOG);
+ }
+ gtk_widget_set_tooltip_text(help_bt, "Show topic specific help");
+
+ if(ok_bt) {
+ gtk_widget_grab_default(ok_bt);
+ }
+
+ remember_filter_dialog(main_w, filter_dialogs);
+
+ if (button != NULL) {
+ /* This dialog box was created by a "Filter" button.
+ Set the E_FILT_BUTTON_PTR_KEY for the new dialog to point to
+ the button. */
+ g_object_set_data(G_OBJECT(main_w), E_FILT_BUTTON_PTR_KEY, button);
+
+ /* Set the E_FILT_DIALOG_PTR_KEY for the button to point to us */
+ g_object_set_data(G_OBJECT(button), E_FILT_DIALOG_PTR_KEY, main_w);
+ }
+
+ /* DO SELECTION THINGS *AFTER* SHOWING THE DIALOG! */
+ /* otherwise the updatings can get confused */
+ if (l_select) {
+ gtk_tree_selection_select_iter(sel, l_select);
+ g_free(l_select);
+ } else if (filter_te_str && filter_te_str[0]) {
+ gtk_entry_set_text(GTK_ENTRY(name_te), "New filter");
+ gtk_entry_set_text(GTK_ENTRY(filter_te), filter_te_str);
+ }
+
+ g_signal_connect(main_w, "delete_event", G_CALLBACK(filter_dlg_delete_event_cb), filter_list_type_p);
+ g_signal_connect(main_w, "destroy", G_CALLBACK(filter_dlg_destroy_cb), filter_list_type_p);
+
+ gtk_widget_show(main_w);
+
+ if(construct_args->modal_and_transient) {
+ parent = gtk_widget_get_parent_window(parent_filter_te);
+ gdk_window_set_transient_for(gtk_widget_get_window(main_w), parent);
+ gtk_window_set_modal(GTK_WINDOW(main_w), TRUE);
+ }
+
+ /* hide the Ok button, if we don't have to apply it and our caller wants a Save button */
+ if (parent_filter_te == NULL && prefs.gui_use_pref_save) {
+ gtk_widget_hide(ok_bt);
+ }
+
+ /* hide the Apply button, if our caller don't wants one */
+ if (!construct_args->wants_apply_button) {
+ gtk_widget_hide(apply_bt);
+ }
+
+ /* hide the Save button if the user uses implicit save */
+ if (!prefs.gui_use_pref_save) {
+ gtk_widget_hide(save_bt);
+ }
+
+ window_present(main_w);
+
+ return main_w;
+}
+
+static void
+filter_dlg_dclick(GtkWidget *filter_l, gpointer main_w_arg, gpointer activate)
+{
+ GtkWidget *main_w = GTK_WIDGET(main_w_arg);
+ GtkWidget *parent_filter_te =
+ g_object_get_data(G_OBJECT(main_w), E_FILT_PARENT_FILTER_TE_KEY);
+ GList *flp;
+ filter_def *filt;
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
+
+ if (parent_filter_te != NULL) {
+ /*
+ * We have a text entry widget associated with this dialog
+ * box; is one of the filters in the list selected?
+ */
+ if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ /*
+ * Yes. Is there a filter definition for that filter?
+ */
+ gtk_tree_model_get(model, &iter, 1, &flp, -1);
+ if (flp) {
+ /*
+ * Yes - put it in the text entry widget.
+ */
+ filt = (filter_def *) flp->data;
+ gtk_entry_set_text(GTK_ENTRY(parent_filter_te),
+ filt->strval);
+
+ /*
+ * Are we supposed to cause the filter we
+ * put there to be applied?
+ */
+ if (activate != NULL) {
+ /*
+ * Yes - do so.
+ */
+ g_signal_emit_by_name(G_OBJECT(parent_filter_te), "activate", NULL);
+ }
+ }
+ }
+ }
+
+ window_destroy(main_w);
+}
+
+static void
+filter_dlg_ok_cb(GtkWidget *ok_bt, gpointer data)
+{
+ filter_list_type_t list_type = *(filter_list_type_t *)data;
+
+ /*
+ * Destroy the dialog box and apply the filter.
+ */
+ filter_apply(gtk_widget_get_toplevel(ok_bt), TRUE);
+
+ /* if we don't have a Save button, just save the settings now */
+ if (!prefs.gui_use_pref_save) {
+ filter_dlg_save(list_type);
+ }
+}
+
+static void
+filter_dlg_apply_cb(GtkWidget *apply_bt, gpointer data)
+{
+ filter_list_type_t list_type = *(filter_list_type_t *)data;
+
+ /*
+ * Apply the filter, but don't destroy the dialog box.
+ */
+ filter_apply(gtk_widget_get_toplevel(apply_bt), FALSE);
+
+ /* if we don't have a Save button, just save the settings now */
+ if (!prefs.gui_use_pref_save) {
+ filter_dlg_save(list_type);
+ }
+}
+
+static void
+filter_apply(GtkWidget *main_w, gboolean destroy)
+{
+ construct_args_t *construct_args =
+ g_object_get_data(G_OBJECT(main_w), E_FILT_CONSTRUCT_ARGS_KEY);
+ GtkWidget *parent_filter_te =
+ g_object_get_data(G_OBJECT(main_w), E_FILT_PARENT_FILTER_TE_KEY);
+ GtkWidget *filter_te;
+ const gchar *filter_string;
+
+ if (parent_filter_te != NULL) {
+ /*
+ * We have a text entry widget associated with this dialog
+ * box; put the filter in our text entry widget into that
+ * text entry widget.
+ */
+ filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
+ filter_string =
+ (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
+ gtk_entry_set_text(GTK_ENTRY(parent_filter_te), filter_string);
+
+ }
+
+ if (destroy) {
+ /*
+ * Destroy the filter dialog box.
+ */
+ window_destroy(main_w);
+ }
+
+ if (parent_filter_te != NULL) {
+ /*
+ * We have a text entry widget associated with this dialog
+ * box; activate that widget to cause the filter we put
+ * there to be applied if we're supposed to do so.
+ *
+ * We do this after dismissing the filter dialog box,
+ * as activating the widget the dialog box to which
+ * it belongs to be dismissed, and that may cause it
+ * to destroy our dialog box if the filter succeeds.
+ * This means that our subsequent attempt to destroy
+ * it will fail.
+ *
+ * We don't know whether it'll destroy our dialog box,
+ * so we can't rely on it to do so. Instead, we
+ * destroy it ourselves, which will clear the
+ * E_FILT_DIALOG_PTR_KEY pointer for their dialog box,
+ * meaning they won't think it has one and won't try
+ * to destroy it.
+ */
+ if (construct_args->activate_on_ok) {
+ g_signal_emit_by_name(G_OBJECT(parent_filter_te), "activate", NULL);
+ }
+ }
+}
+
+
+static void
+filter_dlg_save(filter_list_type_t list_type)
+{
+ char *pf_dir_path;
+ char *f_path;
+ int f_save_errno;
+ const char *filter_type;
+
+ switch (list_type) {
+
+ case CFILTER_EDITED_LIST:
+ filter_type = "capture";
+ list_type = CFILTER_LIST;
+ copy_filter_list(CFILTER_LIST, CFILTER_EDITED_LIST);
+ break;
+
+ case DFILTER_EDITED_LIST:
+ filter_type = "display";
+ list_type = DFILTER_LIST;
+ copy_filter_list(DFILTER_LIST, DFILTER_EDITED_LIST);
+ break;
+
+ default:
+ g_assert_not_reached();
+ filter_type = NULL;
+ break;
+ }
+
+ /* Create the directory that holds personal configuration files,
+ if necessary. */
+ if (create_persconffile_dir(&pf_dir_path) == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't create directory\n\"%s\"\nfor filter files: %s.",
+ pf_dir_path, g_strerror(errno));
+ g_free(pf_dir_path);
+ return;
+ }
+
+ save_filter_list(list_type, &f_path, &f_save_errno);
+ if (f_path != NULL) {
+ /* We had an error saving the filter. */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Could not save to your %s filter file\n\"%s\": %s.",
+ filter_type, f_path, g_strerror(f_save_errno));
+ g_free(f_path);
+ }
+}
+
+
+static void
+filter_dlg_save_cb(GtkWidget *save_bt _U_, gpointer data)
+{
+ filter_list_type_t list_type = *(filter_list_type_t *)data;
+
+ filter_dlg_save(list_type);
+}
+
+#if 0
+/* update a remaining dialog if another one was cancelled */
+static void
+filter_dlg_update_list_cb(gpointer data, gpointer user_data)
+{
+ GtkWidget *main_w = data;
+ filter_list_type_t list_type = *(filter_list_type_t *)user_data;
+
+ /* refill the list */
+ clear_list(main_w);
+ fill_list(main_w, list_type, NULL);
+}
+#endif
+
+/* cancel button pressed, revert changes and exit dialog */
+static void
+filter_dlg_cancel_cb(GtkWidget *cancel_bt, gpointer data)
+{
+ filter_list_type_t list_type = *(filter_list_type_t *)data;
+ GtkWidget *main_w = gtk_widget_get_toplevel(cancel_bt);
+ static GList *filter_list;
+
+
+ window_destroy(GTK_WIDGET(main_w));
+
+ /* if this was the last open filter dialog, revert the changes made */
+ filter_list = get_filter_dialog_list(list_type);
+ if(g_list_length(filter_list) == 0) {
+ /* revert changes in the edited list */
+ switch (list_type) {
+ case CFILTER_EDITED_LIST:
+ copy_filter_list(CFILTER_EDITED_LIST, CFILTER_LIST);
+ break;
+ case DFILTER_EDITED_LIST:
+ copy_filter_list(DFILTER_EDITED_LIST, DFILTER_LIST);
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ }
+
+#if 0
+ /* update other open filter dialogs */
+ g_list_foreach(get_filter_dialog_list(list_type), filter_dlg_update_list_cb, &list_type);
+#endif
+}
+
+/* Treat this as a cancel, by calling "filter_dlg_cancel_cb()" */
+static gboolean
+filter_dlg_delete_event_cb(GtkWidget *main_w, GdkEvent *event _U_,
+ gpointer data)
+{
+ filter_dlg_cancel_cb(main_w, data);
+ return FALSE;
+}
+
+
+static void
+filter_dlg_destroy_cb(GtkWidget *win, gpointer data)
+{
+ filter_list_type_t list_type = *(filter_list_type_t *)data;
+ GtkWidget *button;
+
+ /* Get the button that requested that we be popped up, if any.
+ (It should arrange to destroy us if it's destroyed, so
+ that we don't get a pointer to a non-existent window here.) */
+ button = g_object_get_data(G_OBJECT(win), E_FILT_BUTTON_PTR_KEY);
+
+ if (button != NULL) {
+ /* Tell it we no longer exist. */
+ g_object_set_data(G_OBJECT(button), E_FILT_DIALOG_PTR_KEY, NULL);
+ } else {
+ /* This is an editing dialog popped up from, for example,
+ a menu item; note that we no longer have one. */
+ switch (list_type) {
+
+#ifdef HAVE_LIBPCAP
+ case CFILTER_EDITED_LIST:
+ g_assert(win == global_cfilter_w);
+ global_cfilter_w = NULL;
+ break;
+#endif
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ }
+
+ /* Remove this from the list of filter dialog windows. */
+ forget_filter_dialog(win, list_type);
+}
+
+static gboolean
+filter_sel_list_button_cb(GtkWidget *list, GdkEventButton *event,
+ gpointer data _U_)
+{
+ void (* func)(GtkWidget *, gpointer, gpointer);
+ gpointer func_arg;
+ gpointer func_activate;
+
+ if (event->type == GDK_2BUTTON_PRESS) {
+ func = g_object_get_data(G_OBJECT(list), E_FILT_DBLFUNC_KEY);
+ func_arg = g_object_get_data(G_OBJECT(list), E_FILT_DBLARG_KEY);
+ func_activate = g_object_get_data(G_OBJECT(list), E_FILT_DBLACTIVATE_KEY);
+
+ if (func)
+ (*func)(list, func_arg, func_activate);
+ }
+
+ return FALSE;
+}
+
+static void
+filter_sel_list_cb(GtkTreeSelection *sel, gpointer data _U_)
+{
+ GtkWidget *filter_l = GTK_WIDGET(gtk_tree_selection_get_tree_view(sel));
+ GtkWidget *main_w = gtk_widget_get_toplevel(filter_l);
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkWidget *name_te = g_object_get_data(G_OBJECT(main_w), E_FILT_NAME_TE_KEY);
+ GtkWidget *filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
+ GtkWidget *chg_bt = g_object_get_data(G_OBJECT(main_w), E_FILT_CHG_BT_KEY);
+ GtkWidget *copy_bt = g_object_get_data(G_OBJECT(main_w), E_FILT_COPY_BT_KEY);
+ GtkWidget *del_bt = g_object_get_data(G_OBJECT(main_w), E_FILT_DEL_BT_KEY);
+ filter_def *filt;
+ gchar *name = NULL, *strval = NULL;
+ GList *flp;
+ gint sensitivity = FALSE;
+
+ if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ gtk_tree_model_get(model, &iter, 1, &flp, -1);
+ if (flp) {
+ filt = (filter_def *) flp->data;
+ name = g_strdup(filt->name);
+ strval = g_strdup(filt->strval);
+ sensitivity = TRUE;
+ }
+ }
+
+ /*
+ * Did you know that this function is called when the window is destroyed?
+ * Funny, that.
+ * This means that we have to:
+ *
+ * attach to the top-level window data items containing pointers to
+ * the widgets we affect here;
+ *
+ * give each of those widgets their own destroy callbacks;
+ *
+ * clear that pointer when the widget is destroyed;
+ *
+ * don't do anything to the widget if the pointer we get back is
+ * null;
+ *
+ * so that if we're called after any of the widgets we'd affect are
+ * destroyed, we know that we shouldn't do anything to those widgets.
+ */
+ if (name_te != NULL)
+ gtk_entry_set_text(GTK_ENTRY(name_te), name ? name : "");
+ if (filter_te != NULL)
+ gtk_entry_set_text(GTK_ENTRY(filter_te), strval ? strval : "");
+ if (chg_bt != NULL)
+ gtk_widget_set_sensitive(chg_bt, sensitivity);
+ if (copy_bt != NULL)
+ gtk_widget_set_sensitive(copy_bt, sensitivity);
+ if (del_bt != NULL)
+ gtk_widget_set_sensitive(del_bt, sensitivity);
+ g_free(name);
+ g_free(strval);
+}
+
+/* To do: add input checking to each of these callbacks */
+
+/* Structure containing arguments to be passed to "new_filter_cb()".
+
+ "active_filter_l" is the list in the dialog box in which "New" or
+ "Copy" was clicked; in that dialog box, but not in any other dialog
+ box, we select the newly created list item.
+
+ "nflp" is the GList member in the model (filter list) for the new
+ filter. */
+typedef struct {
+ GtkWidget *active_filter_l;
+ GList *nflp;
+} new_filter_cb_args_t;
+
+static void
+new_filter_cb(gpointer data, gpointer user_data)
+{
+ GtkWidget *main_w = data;
+ GtkTreeView *filter_l;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ new_filter_cb_args_t *args = user_data;
+ filter_def *nfilt = args->nflp->data;
+
+ filter_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY));
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(filter_l));
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, 0, nfilt->name, 1, args->nflp, -1);
+ if (GTK_WIDGET(filter_l) == args->active_filter_l) {
+ /* Select the item. */
+ gtk_tree_selection_select_iter(gtk_tree_view_get_selection(filter_l),
+ &iter);
+ }
+}
+
+static void
+filter_new_bt_clicked_cb(GtkWidget *w, gpointer data)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(w);
+ GtkWidget *name_te = g_object_get_data(G_OBJECT(main_w), E_FILT_NAME_TE_KEY);
+ GtkWidget *filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
+ GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
+ filter_list_type_t list_type = *(filter_list_type_t *)data;
+ GList *fl_entry;
+ const gchar *name, *strval;
+ new_filter_cb_args_t args;
+
+ name = gtk_entry_get_text(GTK_ENTRY(name_te));
+ strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
+
+ /* if the user didn't entered a name, set default one */
+ if (strlen(name) == 0) {
+ name = "new";
+ }
+
+ /* if the user didn't entered a string value, set default one */
+ if (strlen(strval) == 0) {
+ strval = "new";
+ }
+
+ /* Add a new entry to the filter list. */
+ fl_entry = add_to_filter_list(list_type, name, strval);
+
+ /* Update all the filter list widgets, not just the one in
+ the dialog box in which we clicked on "Copy". */
+ args.active_filter_l = filter_l;
+ args.nflp = fl_entry;
+ g_list_foreach(get_filter_dialog_list(list_type), new_filter_cb, &args);
+
+}
+
+static gboolean
+chg_list_item_cb(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
+ gpointer data)
+{
+ GList *flp = data;
+ filter_def *filt = flp->data;
+ GList *nl_model;
+
+ gtk_tree_model_get(model, iter, 1, &nl_model, -1);
+ /* Is this the item corresponding to the filter list item in question? */
+ if (flp == nl_model) {
+ /* Yes - change the label to correspond to the new name for the
+ * filter. */
+ gtk_list_store_set(GTK_LIST_STORE(model), iter, 0, filt->name, -1);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+chg_filter_cb(gpointer data, gpointer user_data)
+{
+ GtkWidget *main_w = data;
+ GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
+
+ gtk_tree_model_foreach(gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l)),
+ chg_list_item_cb, user_data);
+}
+
+static void
+filter_name_te_changed_cb(GtkWidget *w, gpointer data)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(w);
+ GtkWidget *name_te = g_object_get_data(G_OBJECT(main_w), E_FILT_NAME_TE_KEY);
+ GtkWidget *filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
+ GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
+ filter_def *filt;
+ GList *fl_entry;
+ filter_list_type_t list_type = *(filter_list_type_t *)data;
+ const gchar *name = "";
+ const gchar *strval = "";
+
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
+ name = gtk_entry_get_text(GTK_ENTRY(name_te));
+ strval = gtk_entry_get_text(GTK_ENTRY(filter_te));
+
+ if (DFILTER_EDITED_LIST == list_type) {
+ /* colorize filter string entry */
+ filter_te_syntax_check_cb(filter_te, NULL);
+ }
+
+ /* if something was selected */
+ if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
+ if (fl_entry != NULL) {
+ filt = (filter_def *) fl_entry->data;
+
+ if (strlen(name) > 0 && strlen(strval) > 0 && filt) {
+ g_free(filt->name);
+ g_free(filt->strval);
+ filt->name = g_strdup(name);
+ filt->strval = g_strdup(strval);
+
+ /* Update all the filter list widgets, not just the one in
+ the dialog box in which we clicked on "Copy". */
+ g_list_foreach(get_filter_dialog_list(list_type), chg_filter_cb,
+ fl_entry);
+ }
+ }
+ }
+}
+
+static void
+delete_filter_cb(gpointer data, gpointer user_data)
+{
+ GtkWidget *main_w = data;
+ GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
+ gchar *pos = (gchar *)user_data;
+ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(filter_l));
+ GtkTreeIter iter;
+
+ gtk_tree_model_get_iter_from_string(model, &iter, pos);
+ gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
+}
+
+static void
+filter_del_bt_clicked_cb(GtkWidget *w, gpointer data)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(w);
+ GtkWidget *filter_l = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_L_KEY);
+ filter_list_type_t list_type = *(filter_list_type_t *)data;
+ GList *fl_entry;
+ gchar *pos;
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(filter_l));
+ /* If something was selected */
+ if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ gtk_tree_model_get(model, &iter, 1, &fl_entry, -1);
+ path = gtk_tree_model_get_path(model, &iter);
+ pos = gtk_tree_path_to_string(path);
+ gtk_tree_path_free(path);
+ if (fl_entry != NULL) {
+ /* Remove the entry from the filter list. */
+ remove_from_filter_list(list_type, fl_entry);
+
+ /* Update all the filter list widgets, not just the one in
+ the dialog box in which we clicked on "Delete". */
+ g_list_foreach(get_filter_dialog_list(list_type), delete_filter_cb, pos);
+ }
+ g_free(pos);
+ }
+}
+
+void
+filter_add_expr_bt_cb(GtkWidget *w _U_, gpointer main_w_arg)
+{
+ GtkWidget *main_w = GTK_WIDGET(main_w_arg);
+ GtkWidget *filter_te, *dfilter_w;
+
+ filter_te = g_object_get_data(G_OBJECT(main_w), E_FILT_FILTER_TE_KEY);
+ dfilter_w = dfilter_expr_dlg_new(filter_te);
+
+ /* If we're opening a series of modal dialogs (such as when going
+ * through file->open, make the latest dialog modal also so that it
+ * takes over "control" from the other modal dialogs. Also set
+ * the transient property of the new dialog so the user doesn't try
+ * to interact with the previous window when they can't.
+ * XXX: containing widget might be the Filter Toolbar */
+
+ if ( GTK_IS_WINDOW(main_w) && gtk_window_get_modal(GTK_WINDOW(main_w))) {
+ gtk_window_set_modal(GTK_WINDOW(dfilter_w), TRUE);
+ gtk_window_set_transient_for(GTK_WINDOW(dfilter_w),
+ GTK_WINDOW(main_w));
+ }
+}
+
+static void
+color_filter_te(GtkWidget *w, guint16 red, guint16 green, guint16 blue)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ static GdkRGBA black = { 0, 0, 0, 1.0 };
+ GdkRGBA bg;
+
+ bg.red = red / 65535.0;
+ bg.green = green / 65535.0;
+ bg.blue = blue / 65535.0;
+ bg.alpha = 1;
+
+ gtk_widget_override_color(w, GTK_STATE_NORMAL, &black);
+ gtk_widget_override_background_color(w, GTK_STATE_NORMAL, &bg);
+ gtk_widget_override_cursor(w, &black, &black);
+#else
+ static GdkColor black = { 0, 0, 0, 0 };
+ GdkColor bg;
+
+ bg.pixel = 0;
+ bg.red = red;
+ bg.green = green;
+ bg.blue = blue;
+
+ gtk_widget_modify_text(w, GTK_STATE_NORMAL, &black);
+ gtk_widget_modify_base(w, GTK_STATE_NORMAL, &bg);
+ gtk_widget_modify_cursor(w, &black, &black);
+#endif
+}
+
+void
+colorize_filter_te_as_empty(GtkWidget *w)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ /* use defaults */
+ gtk_widget_override_color(w, GTK_STATE_NORMAL, NULL);
+ gtk_widget_override_background_color(w, GTK_STATE_NORMAL, NULL);
+ gtk_widget_override_cursor(w, NULL, NULL);
+#else
+ /* use defaults */
+ gtk_widget_modify_text(w, GTK_STATE_NORMAL, NULL);
+ gtk_widget_modify_base(w, GTK_STATE_NORMAL, NULL);
+ gtk_widget_modify_cursor(w, NULL, NULL);
+#endif
+}
+
+void
+colorize_filter_te_as_invalid(GtkWidget *w)
+{
+ /* light red */
+ color_filter_te(w, 0xFFFF, 0xAFFF, 0xAFFF);
+}
+
+static void
+colorize_filter_te_as_deprecated(GtkWidget *w)
+{
+ /* light yellow */
+ color_filter_te(w, 0xFFFF, 0xFFFF, 0xAFFF);
+}
+
+void
+colorize_filter_te_as_valid(GtkWidget *w)
+{
+ /* light green */
+ color_filter_te(w, 0xAFFF, 0xFFFF, 0xAFFF);
+}
+
+/*
+ * XXX This calls dfilter_compile, which might call get_host_ipaddr or
+ * get_host_ipaddr6. Either of of these will freeze the UI if the host
+ * name resolution takes a long time to complete. We need to work
+ * around this, either by disabling host name resolution or by doing
+ * the resolution asynchronously.
+ *
+ * We could use a separate thread but we have be careful to only call
+ * GTK+/GDK routines from the main thread. From the GDK threads
+ * documentation:
+ *
+ * "With the Win32 backend, GDK calls should not be attempted from
+ * multiple threads at all."
+ */
+
+void
+filter_te_syntax_check_cb(GtkWidget *w, gpointer user_data _U_)
+{
+ const gchar *strval;
+ dfilter_t *dfp;
+ GPtrArray *depr = NULL;
+ gboolean use_statusbar;
+ guchar c;
+
+ strval = gtk_entry_get_text(GTK_ENTRY(w));
+ use_statusbar = g_object_get_data(G_OBJECT(w), E_FILT_FIELD_USE_STATUSBAR_KEY) ? TRUE : FALSE;
+
+ if (use_statusbar) {
+ statusbar_pop_filter_msg();
+ }
+
+ /* colorize filter string entry */
+ if (g_object_get_data(G_OBJECT(w), E_FILT_FIELD_NAME_ONLY_KEY) &&
+ strval && (c = proto_check_field_name(strval)) != 0)
+ {
+ colorize_filter_te_as_invalid(w);
+ if (use_statusbar) {
+ statusbar_push_filter_msg(" Illegal character in field name: '%c'", c);
+ }
+ } else if (strval && dfilter_compile(strval, &dfp)) {
+ if (dfp != NULL) {
+ depr = dfilter_deprecated_tokens(dfp);
+ }
+ if (strlen(strval) == 0) {
+ colorize_filter_te_as_empty(w);
+ } else if (depr) {
+ /* You keep using that word. I do not think it means what you think it means. */
+ colorize_filter_te_as_deprecated(w);
+ if (use_statusbar) {
+ /*
+ * We're being lazy and only printing the first "problem" token.
+ * Would it be better to print all of them?
+ */
+ statusbar_push_temporary_msg(" \"%s\" may have unexpected results (see the User's Guide)",
+ (const char *) g_ptr_array_index(depr, 0));
+ }
+ } else {
+ colorize_filter_te_as_valid(w);
+ }
+ dfilter_free(dfp);
+ } else {
+ colorize_filter_te_as_invalid(w);
+ if (use_statusbar) {
+ if (dfilter_error_msg) {
+ statusbar_push_filter_msg(" Invalid filter: %s", dfilter_error_msg);
+ } else {
+ statusbar_push_filter_msg(" Invalid filter");
+ }
+ }
+ }
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/gtk/filter_dlg.h b/ui/gtk/filter_dlg.h
new file mode 100644
index 0000000000..84c4387bae
--- /dev/null
+++ b/ui/gtk/filter_dlg.h
@@ -0,0 +1,131 @@
+/* filter_dlg.h
+ * Definitions for dialog boxes for filter editing
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __FILTER_DLG_H__
+#define __FILTER_DLG_H__
+
+/** @file
+ * "Capture Filter" / "Display Filter" / "Add expression" dialog boxes.
+ * (This used to be a notebook page under "Preferences", hence the
+ * "prefs" in the file name.)
+ * @ingroup dialog_group
+ */
+
+/**
+ * Structure giving properties of the filter editing dialog box to be
+ * created.
+ */
+typedef struct {
+ const gchar *title; /**< title of dialog box */
+ gboolean wants_apply_button; /**< dialog should have an Apply button */
+ gboolean activate_on_ok; /**< if parent text widget should be
+ activated on "Ok" or "Apply" */
+ gboolean modal_and_transient; /**< dialog is modal and transient to the
+ parent window (e.g. to gtk_file_chooser) */
+} construct_args_t;
+
+/** Create a "Capture Filter" dialog box caused by a button click.
+ *
+ * @param widget parent widget
+ * @param user_data unused
+ */
+void capture_filter_construct_cb(GtkWidget *widget, gpointer user_data);
+
+/** Create a "Display Filter" dialog box caused by a button click.
+ *
+ * @param widget parent widget
+ * @param construct_args_ptr parameters to construct the dialog (construct_args_t)
+ */
+void display_filter_construct_cb(GtkWidget *widget, gpointer construct_args_ptr);
+
+/** Should be called when the widget (usually a button) that creates filters
+ * is destroyed. It destroys any filter dialog created by that widget.
+ *
+ * @param widget parent widget
+ * @param user_data unused
+ */
+void filter_button_destroy_cb(GtkWidget *widget, gpointer user_data);
+
+/** User requested the "Capture Filter" dialog box by menu or toolbar.
+ *
+ * @param widget parent widget
+ */
+void cfilter_dialog_cb(GtkWidget *widget);
+
+/** User requested the "Display Filter" dialog box by menu or toolbar.
+ *
+ * @param widget parent widget
+ */
+void dfilter_dialog_cb(GtkWidget *widget);
+
+/** Create an "Add expression" dialog box caused by a button click.
+ *
+ * @param widget unused
+ * @param main_w_arg parent widget
+ */
+void filter_add_expr_bt_cb(GtkWidget *widget, gpointer main_w_arg);
+
+/** Colorize a text entry as empty.
+ *
+ * @param widget the text entry to colorize
+ */
+void colorize_filter_te_as_empty(GtkWidget *widget);
+
+/** Colorize a text entry as a invalid.
+ *
+ * @param widget the text entry to colorize
+ */
+void colorize_filter_te_as_invalid(GtkWidget *widget);
+
+/** Colorize a text entry as a valid.
+ *
+ * @param widget the text entry to colorize
+ */
+void colorize_filter_te_as_valid(GtkWidget *widget);
+
+/** Colorize a filter text entry depending on "validity".
+ *
+ * @param widget the text entry to colorize
+ * @param user_data Callback User Data pointer (unused)
+ */
+void filter_te_syntax_check_cb(GtkWidget *widget, gpointer user_data _U_);
+
+/** The filter button of the top_level window. */
+#define E_FILT_BT_PTR_KEY "filter_bt_ptr"
+
+/** The filter text entry. */
+#define E_FILT_TE_PTR_KEY "filter_te_ptr"
+
+/** The filter text entry.
+ * @todo Check the usage of all the text entry keys.
+ */
+#define E_FILT_FILTER_TE_KEY "filter_filter_te"
+
+/** Only validate a single field entry. */
+#define E_FILT_FIELD_NAME_ONLY_KEY "filter_field_name_only"
+
+/** Update statusbar when changing the filter entry. */
+#define E_FILT_FIELD_USE_STATUSBAR_KEY "filter_field_use_statusbar"
+
+#endif /* filter_dlg.h */
diff --git a/ui/gtk/filter_expression_save_dlg.c b/ui/gtk/filter_expression_save_dlg.c
new file mode 100644
index 0000000000..aa9e693e63
--- /dev/null
+++ b/ui/gtk/filter_expression_save_dlg.c
@@ -0,0 +1,363 @@
+/* filter_expression_save_dlg.c
+ * Routines for "Filter Save" window
+ * Submitted by Edwin Groothuis <wireshark@mavetju.org>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/proto.h>
+#include <epan/dfilter/dfilter.h>
+#include <epan/nstime.h>
+#include <epan/strutil.h>
+#include <epan/prefs.h>
+#include <epan/filter_expressions.h>
+
+#include "../globals.h"
+#include "../alert_box.h"
+#include "../simple_dialog.h"
+#include "../main_statusbar.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/filter_expression_save_dlg.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/prefs_dlg.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/filter_autocomplete.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui_util.h"
+#include "main.h"
+
+#include "main_filter_toolbar.h"
+
+/* Capture callback data keys */
+#define E_FILTER_SAVE_EXPR_KEY "filter_save_offset_expression"
+#define E_FILTER_SAVE_LABEL_KEY "filter_save_offset_label"
+
+static void filter_save_ok_cb(GtkWidget *ok_bt, GtkWindow *parent_w);
+static void filter_save_close_cb(GtkWidget *close_bt, gpointer parent_w);
+static void filter_save_frame_destroy_cb(GtkWidget *win, gpointer user_data);
+static void filter_button_cb(GtkWidget *close_bt, gpointer parent_w);
+static int filter_button_add(const char *label, const char *expr, struct filter_expression *newbutton);
+
+/*
+ * Keep a static pointer to the current "Filter Save" window, if any, so
+ * that if somebody tries to do "Filter Save" while there's already a
+ * "Filter Save" window up, we just pop up the existing one, rather than
+ * creating a new one.
+ */
+static GtkWidget *filter_save_frame_w;
+
+GtkWidget *_filter_tb = NULL;
+GtkWidget *_filter_te = NULL;
+
+/*
+ * This does do two things:
+ * - Keep track of the various elements of the Filter Toolbar which will
+ * be needed later when a new button has to be added.
+ * - Since it is called after the preferences are read from the configfile,
+ * this is the one also which creates the initial buttons when the
+ * Filter Toolbar has been created.
+ */
+void
+filter_expression_save_dlg_init(gpointer filter_tb, gpointer filter_te)
+{
+ struct filter_expression *fe;
+
+ _filter_tb = (GtkWidget *)filter_tb;
+ _filter_te = (GtkWidget *)filter_te;
+
+ fe = *pfilter_expression_head;
+ while (fe != NULL) {
+ filter_button_add(NULL, NULL, fe);
+ fe = fe->next;
+ }
+}
+
+void
+filter_expression_nuke(struct filter_expression *fe)
+{
+ if (fe == NULL)
+ return;
+ filter_expression_nuke(fe->next);
+ g_free(fe->label);
+ g_free(fe->expression);
+}
+
+void
+filter_expression_reinit(int what)
+{
+ struct filter_expression *fe, *prevhead;
+
+ if ((what & FILTER_EXPRESSION_REINIT_DESTROY) != 0) {
+ fe = *pfilter_expression_head;
+ while (fe != NULL) {
+ if (fe->button != NULL) {
+ gtk_widget_destroy(fe->button);
+ fe->button = NULL;
+ }
+ fe = fe->next;
+ }
+ }
+ if (what == FILTER_EXPRESSION_REINIT_DESTROY) {
+ filter_expression_nuke(*pfilter_expression_head);
+ *pfilter_expression_head = NULL;
+ return;
+ }
+
+ if ((what & FILTER_EXPRESSION_REINIT_CREATE) != 0) {
+ gint maxindex = -1, index;
+ fe = *pfilter_expression_head;
+ while (fe != NULL) {
+ maxindex = MAX(maxindex, fe->index);
+ fe = fe->next;
+ }
+
+ prevhead = *pfilter_expression_head;
+ *pfilter_expression_head = NULL;
+
+ /*
+ * The list should be in the order identified by the
+ * index member.
+ */
+ for (index = 0; index <= maxindex; index++) {
+ if (prevhead != NULL) {
+ fe = prevhead;
+ while (fe != NULL && fe->index != index)
+ fe = fe->next;
+ }
+ if (fe == NULL)
+ continue; /* Shouldn't happen */
+ if (fe->deleted)
+ continue; /* Could happen */
+ filter_expression_new(fe->label, fe->expression,
+ fe->enabled);
+ }
+ filter_expression_nuke(prevhead);
+
+ /* Create the buttons again */
+ fe = *pfilter_expression_head;
+ while (fe != NULL) {
+ if (fe->enabled && !fe->deleted)
+ filter_button_add(NULL, NULL, fe);
+ fe = fe->next;
+ }
+ }
+}
+
+static int
+filter_button_add(const char *label, const char *expr, struct filter_expression *newfe)
+{
+ struct filter_expression *fe;
+
+ /* No duplicate buttons when adding a new one */
+ if (newfe == NULL)
+ fe = filter_expression_new(label, expr, TRUE);
+ else
+ fe = newfe;
+
+ if (fe->enabled == FALSE)
+ return(0);
+
+ /* Create the "Label" button */
+ fe->button = gtk_tool_button_new(NULL, fe->label);
+ g_signal_connect(fe->button, "clicked", G_CALLBACK(filter_button_cb),
+ NULL);
+ gtk_widget_set_sensitive(GTK_WIDGET(fe->button), FALSE);
+ gtk_widget_show(GTK_WIDGET(fe->button));
+
+ gtk_toolbar_insert(GTK_TOOLBAR(_filter_tb), fe->button, -1);
+ gtk_widget_set_sensitive(GTK_WIDGET(fe->button), TRUE);
+ gtk_widget_set_tooltip_text(GTK_WIDGET(fe->button), fe->expression);
+
+ return(0);
+}
+
+static void
+filter_button_cb(GtkWidget *this_button, gpointer parent_w _U_)
+{
+ struct filter_expression *fe;
+
+ fe = *pfilter_expression_head;
+ while (fe != NULL) {
+ if ((void *)fe->button == (void *)this_button) {
+ gtk_entry_set_text(GTK_ENTRY(_filter_te),
+ fe->expression);
+ main_filter_packets(&cfile, fe->expression, FALSE);
+ return;
+ }
+ fe = fe->next;
+ }
+ printf("No Callback\n");
+}
+
+void
+filter_expression_save_dlg(gpointer data)
+{
+ GtkWidget *main_vb, *main_filter_save_hb, *filter_save_frame,
+ *filter_save_type_vb, *filter_save_type_hb, *entry_hb,
+ *bbox, *ok_bt, *cancel_bt, *help_bt, *filter_text_box,
+ *label_text_box;
+
+ const char *expr;
+
+ /* The filter requested */
+ expr = gtk_entry_get_text(GTK_ENTRY(data));
+
+ if (filter_save_frame_w != NULL) {
+ /* There's already a "Filter Save" dialog box; reactivate it. */
+ reactivate_window(filter_save_frame_w);
+ return;
+ }
+
+ filter_save_frame_w = dlg_window_new("Wireshark: Save Filter");
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(filter_save_frame_w), main_vb);
+ gtk_widget_show(main_vb);
+
+
+ /* */
+ main_filter_save_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(main_vb), main_filter_save_hb);
+ gtk_widget_show(main_filter_save_hb);
+
+ /* Filter Save frame */
+ filter_save_frame = gtk_frame_new("Save Filter as...");
+ gtk_box_pack_start(GTK_BOX(main_filter_save_hb), filter_save_frame,
+ TRUE, TRUE, 0);
+ gtk_widget_show(filter_save_frame);
+
+ filter_save_type_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(filter_save_type_vb), 3);
+ gtk_container_add(GTK_CONTAINER(filter_save_frame),
+ filter_save_type_vb);
+ gtk_widget_show(filter_save_type_vb);
+
+ /* filter_save type row */
+ filter_save_type_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(filter_save_type_vb),
+ filter_save_type_hb);
+ gtk_widget_show(filter_save_type_hb);
+
+ /* filter_save row */
+ entry_hb = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(filter_save_type_vb), entry_hb, FALSE,
+ FALSE, 0);
+ gtk_widget_show(entry_hb);
+
+ filter_text_box = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(entry_hb), filter_text_box, TRUE, TRUE, 0);
+ g_signal_connect(filter_text_box, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
+ g_signal_connect(filter_text_box, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
+ g_signal_connect(filter_save_frame_w, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
+
+ gtk_entry_set_text(GTK_ENTRY(filter_text_box), expr);
+ gtk_widget_show(filter_text_box);
+
+ label_text_box = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(entry_hb), label_text_box, TRUE, TRUE, 0);
+ gtk_entry_set_text(GTK_ENTRY(label_text_box), "Filter");
+ gtk_widget_show(label_text_box);
+
+ /* Button row */
+ bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_CANCEL,
+ GTK_STOCK_HELP, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+
+ ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(filter_save_ok_cb),
+ filter_save_frame_w);
+
+ cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ g_signal_connect(cancel_bt, "clicked", G_CALLBACK(filter_save_close_cb),
+ filter_save_frame_w);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb),
+ (gpointer)HELP_FILTER_SAVE_DIALOG);
+
+ g_object_set_data(G_OBJECT(filter_save_frame_w),
+ E_FILTER_SAVE_EXPR_KEY, filter_text_box);
+ g_object_set_data(G_OBJECT(filter_save_frame_w),
+ E_FILTER_SAVE_LABEL_KEY, label_text_box);
+
+ dlg_set_activate(label_text_box, ok_bt);
+
+ /* Give the initial focus to the "offset" entry box. */
+ gtk_widget_grab_focus(label_text_box);
+
+ g_signal_connect(filter_save_frame_w, "delete_event",
+ G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(filter_save_frame_w, "destroy",
+ G_CALLBACK(filter_save_frame_destroy_cb), NULL);
+
+ gtk_widget_show(filter_save_frame_w);
+ window_present(filter_save_frame_w);
+}
+
+static void
+filter_save_ok_cb(GtkWidget *ok_bt _U_, GtkWindow *parent_w)
+{
+ GtkWidget *expr_te, *label_te;
+ const char *expr, *label;
+
+ /* The filter requested */
+ expr_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w),
+ E_FILTER_SAVE_EXPR_KEY);
+ label_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w),
+ E_FILTER_SAVE_LABEL_KEY);
+ expr = gtk_entry_get_text(GTK_ENTRY(expr_te));
+ label = gtk_entry_get_text(GTK_ENTRY(label_te));
+
+ if (filter_button_add(label, expr, NULL) == 0) {
+ prefs_main_write();
+ filter_save_close_cb(NULL, parent_w);
+ }
+}
+
+static void
+filter_save_close_cb(GtkWidget *close_bt _U_, gpointer parent_w)
+{
+ gtk_grab_remove(GTK_WIDGET(parent_w));
+ window_destroy(GTK_WIDGET(parent_w));
+}
+
+static void
+filter_save_frame_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+ /* Note that we no longer have a "Filter Save" dialog box. */
+ filter_save_frame_w = NULL;
+}
diff --git a/ui/gtk/filter_expression_save_dlg.h b/ui/gtk/filter_expression_save_dlg.h
new file mode 100644
index 0000000000..8c36e7e060
--- /dev/null
+++ b/ui/gtk/filter_expression_save_dlg.h
@@ -0,0 +1,47 @@
+/* filter_expression_save_dlg.h
+ * Submitted by Edwin Groothuis <wireshark@mavetju.org>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __FILTER_EXPRESSIONS_SAVE_DLG_H__
+#define __FILTER_EXPRESSIONS_SAVE_DLG_H__
+
+#include "globals.h"
+#include "epan/filter_expressions.h"
+
+enum {
+ FILTER_EXPRESSION_REINIT_DESTROY = 1,
+ FILTER_EXPRESSION_REINIT_CREATE = 2
+};
+
+/** User requested to shift the time of the trace
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ * @param action the function to use
+ */
+
+extern void filter_expression_save_dlg(gpointer data);
+void filter_expression_save_dlg_init(gpointer filter_tb, gpointer filter_te);
+void filter_expression_reinit(int what);
+
+#endif /* __FILTER_EXPRESSIONS_SAVE_DLG_H__ */
diff --git a/ui/gtk/filter_utils.c b/ui/gtk/filter_utils.c
new file mode 100644
index 0000000000..7aa30ccfbf
--- /dev/null
+++ b/ui/gtk/filter_utils.c
@@ -0,0 +1,107 @@
+/* filter_utils.c
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+#include <string.h>
+
+#include "ui/gtk/main.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/find_dlg.h"
+#include "ui/gtk/color_dlg.h"
+#include "ui/gtk/filter_utils.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+void
+apply_selected_filter (guint callback_action, char *filter)
+{
+ int action, type;
+ char *str = NULL;
+ const char *current_filter;
+
+ action = FILTER_ACTION(callback_action);
+ type = FILTER_ACTYPE(callback_action);
+
+ current_filter = gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
+
+ switch(type){
+ case ACTYPE_SELECTED:
+ str = g_strdup_printf("%s", filter);
+ break;
+ case ACTYPE_NOT_SELECTED:
+ str = g_strdup_printf("!(%s)", filter);
+ break;
+ case ACTYPE_AND_SELECTED:
+ if ((!current_filter) || (0 == strlen(current_filter)))
+ str = g_strdup_printf("%s", filter);
+ else
+ str = g_strdup_printf("(%s) && (%s)", current_filter, filter);
+ break;
+ case ACTYPE_OR_SELECTED:
+ if ((!current_filter) || (0 == strlen(current_filter)))
+ str = g_strdup_printf("%s", filter);
+ else
+ str = g_strdup_printf("(%s) || (%s)", current_filter, filter);
+ break;
+ case ACTYPE_AND_NOT_SELECTED:
+ if ((!current_filter) || (0 == strlen(current_filter)))
+ str = g_strdup_printf("!(%s)", filter);
+ else
+ str = g_strdup_printf("(%s) && !(%s)", current_filter, filter);
+ break;
+ case ACTYPE_OR_NOT_SELECTED:
+ if ((!current_filter) || (0 == strlen(current_filter)))
+ str = g_strdup_printf("!(%s)", filter);
+ else
+ str = g_strdup_printf("(%s) || !(%s)", current_filter, filter);
+ break;
+ }
+
+ switch(action){
+ case ACTION_MATCH:
+ gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), str);
+ main_filter_packets(&cfile, str, FALSE);
+ gdk_window_raise(gtk_widget_get_window(top_level));
+ break;
+ case ACTION_PREPARE:
+ gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), str);
+ break;
+ case ACTION_FIND_FRAME:
+ find_frame_with_filter(str);
+ break;
+ case ACTION_FIND_NEXT:
+ cf_find_packet_dfilter_string(&cfile, str, SD_FORWARD);
+ break;
+ case ACTION_FIND_PREVIOUS:
+ cf_find_packet_dfilter_string(&cfile, str, SD_BACKWARD);
+ break;
+ case ACTION_COLORIZE:
+ color_display_with_filter(str);
+ break;
+ }
+ g_free (str);
+}
diff --git a/ui/gtk/filter_utils.h b/ui/gtk/filter_utils.h
new file mode 100644
index 0000000000..9b8d31a5b5
--- /dev/null
+++ b/ui/gtk/filter_utils.h
@@ -0,0 +1,67 @@
+/* filter_utils.h
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __FILTER_UTILS_H__
+#define __FILTER_UTILS_H__
+
+#define GTK_MENU_FUNC(a) ((GtkItemFactoryCallback)(a))
+
+/* Filter actions */
+#define ACTION_MATCH 0
+#define ACTION_PREPARE 1
+#define ACTION_FIND_FRAME 2
+#define ACTION_FIND_NEXT 3
+#define ACTION_FIND_PREVIOUS 4
+#define ACTION_COLORIZE 5
+#define ACTION_WEB_LOOKUP 6
+#define ACTION_COPY 7
+
+
+/* Action type - says what to do with the filter */
+#define ACTYPE_SELECTED 0
+#define ACTYPE_NOT_SELECTED 1
+#define ACTYPE_AND_SELECTED 2
+#define ACTYPE_OR_SELECTED 3
+#define ACTYPE_AND_NOT_SELECTED 4
+#define ACTYPE_OR_NOT_SELECTED 5
+
+/* Encoded callback arguments */
+#define CALLBACK_MATCH(type, extra) ((ACTION_MATCH<<16) | ((type)<<8) | (extra))
+#define CALLBACK_PREPARE(type, extra) ((ACTION_PREPARE<<16) | ((type)<<8) | (extra))
+#define CALLBACK_FIND_FRAME(type, extra) ((ACTION_FIND_FRAME<<16) | ((type)<<8) | (extra))
+#define CALLBACK_FIND_NEXT(type, extra) ((ACTION_FIND_NEXT<<16) | ((type)<<8) | (extra))
+#define CALLBACK_FIND_PREVIOUS(type, extra) ((ACTION_FIND_PREVIOUS<<16) | ((type)<<8) | (extra))
+#define CALLBACK_COLORIZE(type, extra) ((ACTION_COLORIZE<<16) | ((type)<<8) | (extra))
+#define CALLBACK_WEB_LOOKUP (ACTION_WEB_LOOKUP<<16)
+#define CALLBACK_COPY (ACTION_COPY<<16)
+
+
+/* Extract components of callback argument */
+#define FILTER_ACTION(cb_arg) (((cb_arg)>>16) & 0xff)
+#define FILTER_ACTYPE(cb_arg) (((cb_arg)>>8) & 0xff)
+#define FILTER_EXTRA(cb_arg) ((cb_arg) & 0xff)
+
+
+extern void apply_selected_filter (guint callback_action, char *filter);
+
+#endif /* __FILTER_UTILS_H__ */
diff --git a/ui/gtk/find_dlg.c b/ui/gtk/find_dlg.c
new file mode 100644
index 0000000000..c690f16dab
--- /dev/null
+++ b/ui/gtk/find_dlg.c
@@ -0,0 +1,787 @@
+/* find_dlg.c
+ * Routines for "find frame" window
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <ctype.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/proto.h>
+#include <epan/dfilter/dfilter.h>
+#include <epan/strutil.h>
+#include <epan/prefs.h>
+
+#include "../globals.h"
+#include "../alert_box.h"
+#include "../simple_dialog.h"
+#include "../main_statusbar.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/find_dlg.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/prefs_dlg.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/filter_autocomplete.h"
+#include "ui/gtk/old-gtk-compat.h"
+
+/* Capture callback data keys */
+#define E_FIND_FILT_KEY "find_filter_te"
+#define E_FIND_BACKWARD_KEY "find_backward"
+#define E_FIND_HEXDATA_KEY "find_hex"
+#define E_FIND_STRINGDATA_KEY "find_string"
+#define E_FIND_FILTERDATA_KEY "find_filter"
+#define E_FIND_STRINGTYPE_KEY "find_string_type"
+#define E_FIND_STRINGTYPE_LABEL_KEY "find_string_type_label"
+#define E_CASE_SEARCH_KEY "case_insensitive_search"
+#define E_SOURCE_DATA_KEY "packet_data_source"
+#define E_SOURCE_DECODE_KEY "decode_data_source"
+#define E_SOURCE_SUMMARY_KEY "summary_data_source"
+#define E_FILT_TE_BUTTON_KEY "find_filter_button"
+
+static gboolean case_type = TRUE;
+static gboolean summary_data = FALSE;
+static gboolean decode_data = FALSE;
+static gboolean packet_data = FALSE;
+
+static void
+find_filter_te_syntax_check_cb(GtkWidget *w, gpointer parent_w);
+
+static void
+find_frame_ok_cb(GtkWidget *ok_bt, gpointer parent_w);
+
+static void
+find_frame_close_cb(GtkWidget *close_bt, gpointer parent_w);
+
+static void
+find_frame_destroy_cb(GtkWidget *win, gpointer user_data);
+
+static void
+hex_selected_cb(GtkWidget *button_rb _U_, gpointer parent_w);
+
+static void
+string_selected_cb(GtkWidget *button_rb _U_, gpointer parent_w);
+
+static void
+filter_selected_cb(GtkWidget *button_rb _U_, gpointer parent_w);
+
+/*
+ * Keep a static pointer to the current "Find Packet" window, if any, so
+ * that if somebody tries to do "Find Packet" while there's already a
+ * "Find Packet" window up, we just pop up the existing one, rather than
+ * creating a new one.
+ */
+static GtkWidget *find_frame_w;
+static GtkWidget *filter_text_box;
+
+/*
+ * Save the presskey handlers to be able to dissable the auto-completion
+ * feature for hex and string searches.
+ */
+static gulong te_presskey_handler_id;
+static gulong win_presskey_handler_id;
+
+void
+find_frame_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ GtkWidget *main_vb, *main_find_hb, *main_options_hb,
+
+ *find_type_frame, *find_type_vb,
+ *find_type_hb, *find_type_lb, *hex_rb, *string_rb, *filter_rb,
+ *filter_hb, *filter_bt,
+
+ *direction_frame, *direction_vb,
+ *up_rb, *down_rb,
+
+ *data_frame, *data_vb,
+ *packet_data_rb, *decode_data_rb, *summary_data_rb,
+
+ *string_opt_frame, *string_opt_vb,
+ *case_cb, *combo_lb, *combo_cb,
+
+ *bbox, *ok_bt, *cancel_bt, *help_bt;
+
+
+ /* No Apply button, but "OK" not only sets our text widget, it
+ activates it (i.e., it causes us to do the search). */
+ static construct_args_t args = {
+ "Wireshark: Search Filter",
+ FALSE,
+ TRUE,
+ FALSE
+ };
+
+ if (find_frame_w != NULL) {
+ /* There's already a "Find Packet" dialog box; reactivate it. */
+ reactivate_window(find_frame_w);
+ return;
+ }
+
+ find_frame_w = dlg_window_new("Wireshark: Find Packet");
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(find_frame_w), main_vb);
+ gtk_widget_show(main_vb);
+
+
+ /* */
+ main_find_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(main_vb), main_find_hb);
+ gtk_widget_show(main_find_hb);
+
+
+ /* find frame */
+ find_type_frame = gtk_frame_new("Find");
+ gtk_box_pack_start(GTK_BOX(main_find_hb), find_type_frame, TRUE, TRUE, 0);
+ gtk_widget_show(find_type_frame);
+
+ find_type_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(find_type_vb), 3);
+ gtk_container_add(GTK_CONTAINER(find_type_frame), find_type_vb);
+ gtk_widget_show(find_type_vb);
+
+ /* find type row */
+ find_type_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(find_type_vb), find_type_hb);
+ gtk_widget_show(find_type_hb);
+
+ find_type_lb = gtk_label_new("By:");
+ gtk_box_pack_start(GTK_BOX(find_type_hb), find_type_lb, FALSE, FALSE, 0);
+ gtk_widget_show(find_type_lb);
+
+ /* Filter */
+ filter_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "_Display filter");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(filter_rb), !cfile.hex && !cfile.string);
+ gtk_box_pack_start(GTK_BOX(find_type_hb), filter_rb, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text(filter_rb, "Search for data by display filter syntax.\ne.g. ip.addr==10.1.1.1");
+ gtk_widget_show(filter_rb);
+
+ /* Hex */
+ hex_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(filter_rb), "_Hex value");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hex_rb), cfile.hex);
+ gtk_box_pack_start(GTK_BOX(find_type_hb), hex_rb, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text(hex_rb, "Search for data by hex string.\ne.g. fffffda5");
+ gtk_widget_show(hex_rb);
+
+ /* ASCII Search */
+ string_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(filter_rb), "_String");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(string_rb), cfile.string);
+ gtk_box_pack_start(GTK_BOX(find_type_hb), string_rb, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text(string_rb, "Search for data by string value.\ne.g. My String");
+ gtk_widget_show(string_rb);
+
+ /* Filter row */
+ filter_hb = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(find_type_vb), filter_hb, FALSE, FALSE, 0);
+ gtk_widget_show(filter_hb);
+
+ filter_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
+ g_signal_connect(filter_bt, "clicked", G_CALLBACK(display_filter_construct_cb), &args);
+ g_signal_connect(filter_bt, "destroy", G_CALLBACK(filter_button_destroy_cb), NULL);
+ g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_BUTTON_KEY, filter_bt);
+ gtk_box_pack_start(GTK_BOX(filter_hb), filter_bt, FALSE, TRUE, 0);
+ gtk_widget_set_tooltip_text(filter_bt, "Click on the filter button to select a display filter,\nor enter your search criteria into the text box");
+ gtk_widget_show(filter_bt);
+
+ filter_text_box = gtk_entry_new();
+ if (cfile.sfilter) gtk_entry_set_text(GTK_ENTRY(filter_text_box), cfile.sfilter);
+ g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_text_box);
+ g_object_set_data(G_OBJECT(find_frame_w), E_FILT_TE_PTR_KEY, filter_text_box);
+ gtk_box_pack_start(GTK_BOX(filter_hb), filter_text_box, TRUE, TRUE, 0);
+ g_signal_connect(filter_text_box, "changed", G_CALLBACK(find_filter_te_syntax_check_cb), find_frame_w);
+ g_object_set_data(G_OBJECT(find_frame_w), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+ te_presskey_handler_id = g_signal_connect(filter_text_box, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
+ win_presskey_handler_id = g_signal_connect(find_frame_w, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
+ gtk_widget_show(filter_text_box);
+
+
+ /* */
+ main_options_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(main_vb), main_options_hb);
+ gtk_widget_show(main_options_hb);
+
+
+ /* search in frame */
+ data_frame = gtk_frame_new("Search In");
+ gtk_box_pack_start(GTK_BOX(main_options_hb), data_frame, TRUE, TRUE, 0);
+ gtk_widget_show(data_frame);
+
+ /* search in row */
+ data_vb = gtk_vbox_new(TRUE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(data_vb), 3);
+ gtk_container_add(GTK_CONTAINER(data_frame), data_vb);
+ gtk_widget_show(data_vb);
+
+ /* Packet list */
+ summary_data_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "Packet list");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(summary_data_rb), summary_data);
+ gtk_box_pack_start(GTK_BOX(data_vb), summary_data_rb, TRUE, TRUE, 0);
+ gtk_widget_set_tooltip_text(summary_data_rb, "Search for string in the Info column of the packet summary (summary pane)");
+ gtk_widget_show(summary_data_rb);
+
+ /* Packet details */
+ decode_data_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(summary_data_rb), "Packet details");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(decode_data_rb), decode_data);
+ gtk_box_pack_start(GTK_BOX(data_vb), decode_data_rb, TRUE, TRUE, 0);
+ gtk_widget_set_tooltip_text(decode_data_rb, "Search for string among the decoded packet display labels (tree view pane)");
+ gtk_widget_show(decode_data_rb);
+
+ /* Packet bytes */
+ packet_data_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(summary_data_rb), "Packet bytes");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(packet_data_rb), packet_data);
+ gtk_box_pack_start(GTK_BOX(data_vb), packet_data_rb, TRUE, TRUE, 0);
+ gtk_widget_set_tooltip_text(packet_data_rb, "Search for string in the ASCII-converted packet data (hex view pane)");
+ gtk_widget_show(packet_data_rb);
+
+ /* string options frame */
+ string_opt_frame = gtk_frame_new("String Options");
+ gtk_box_pack_start(GTK_BOX(main_options_hb), string_opt_frame, TRUE, TRUE, 0);
+ gtk_widget_show(string_opt_frame);
+
+ string_opt_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(string_opt_frame), string_opt_vb);
+ gtk_container_set_border_width(GTK_CONTAINER(string_opt_vb), 3);
+ gtk_widget_show(string_opt_vb);
+
+ case_cb = gtk_check_button_new_with_mnemonic("Case sensitive");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(case_cb), !case_type);
+ gtk_container_add(GTK_CONTAINER(string_opt_vb), case_cb);
+ gtk_widget_set_tooltip_text(case_cb, "Search by mixed upper/lower case?");
+ gtk_widget_show(case_cb);
+
+ combo_lb = gtk_label_new("Character set:");
+ gtk_container_add(GTK_CONTAINER(string_opt_vb), combo_lb);
+ gtk_misc_set_alignment(GTK_MISC(combo_lb), 0.0f, 0.5f);
+ gtk_widget_show(combo_lb);
+
+ /* Character Type Selection Dropdown Box
+ These only apply to the string find option */
+ /* Create Combo Box */
+
+ combo_cb = gtk_combo_box_text_new();
+
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(combo_cb), "ASCII Unicode & Non-Unicode");
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(combo_cb), "ASCII Non-Unicode");
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(combo_cb), "ASCII Unicode");
+
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo_cb),0);
+ gtk_container_add(GTK_CONTAINER(string_opt_vb), combo_cb);
+
+ gtk_widget_show(combo_cb);
+
+
+ /* direction frame */
+ direction_frame = gtk_frame_new("Direction");
+ gtk_box_pack_start(GTK_BOX(main_options_hb), direction_frame, FALSE, FALSE, 0);
+ gtk_widget_show(direction_frame);
+
+ /* Direction row: Forward and reverse radio buttons */
+ direction_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(direction_vb), 3);
+ gtk_container_add(GTK_CONTAINER(direction_frame), direction_vb);
+ gtk_widget_show(direction_vb);
+
+ up_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "_Up");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(up_rb), cfile.dir == SD_BACKWARD);
+ gtk_box_pack_start(GTK_BOX(direction_vb), up_rb, FALSE, FALSE, 0);
+ gtk_widget_show(up_rb);
+
+ down_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(up_rb), "_Down");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(down_rb), cfile.dir == SD_FORWARD);
+ gtk_box_pack_start(GTK_BOX(direction_vb), down_rb, FALSE, FALSE, 0);
+ gtk_widget_show(down_rb);
+
+
+ /* Button row */
+ bbox = dlg_button_row_new(GTK_STOCK_FIND, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+
+ ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_FIND);
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(find_frame_ok_cb), find_frame_w);
+
+ cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ g_signal_connect(cancel_bt, "clicked", G_CALLBACK(find_frame_close_cb), find_frame_w);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_FIND_DIALOG);
+
+ /* Attach pointers to needed widgets to the capture prefs window/object */
+ g_object_set_data(G_OBJECT(find_frame_w), E_FIND_FILT_KEY, filter_text_box);
+ g_object_set_data(G_OBJECT(find_frame_w), E_FIND_BACKWARD_KEY, up_rb);
+ g_object_set_data(G_OBJECT(find_frame_w), E_FIND_FILTERDATA_KEY, filter_rb);
+ g_object_set_data(G_OBJECT(find_frame_w), E_FIND_HEXDATA_KEY, hex_rb);
+ g_object_set_data(G_OBJECT(find_frame_w), E_FIND_STRINGDATA_KEY, string_rb);
+ g_object_set_data(G_OBJECT(find_frame_w), E_FIND_STRINGTYPE_LABEL_KEY, combo_lb);
+ g_object_set_data(G_OBJECT(find_frame_w), E_FIND_STRINGTYPE_KEY, combo_cb);
+ g_object_set_data(G_OBJECT(find_frame_w), E_CASE_SEARCH_KEY, case_cb);
+ g_object_set_data(G_OBJECT(find_frame_w), E_SOURCE_DATA_KEY, packet_data_rb);
+ g_object_set_data(G_OBJECT(find_frame_w), E_SOURCE_DECODE_KEY, decode_data_rb);
+ g_object_set_data(G_OBJECT(find_frame_w), E_SOURCE_SUMMARY_KEY, summary_data_rb);
+ g_object_set_data(G_OBJECT(find_frame_w), E_FILT_TE_BUTTON_KEY, filter_bt);
+
+ /*
+ * Now that we've attached the pointers, connect the signals - if
+ * we do so before we've attached the pointers, the signals may
+ * be delivered before the pointers are attached; the signal
+ * handlers expect the pointers to be attached, and won't be happy.
+ */
+ g_signal_connect(hex_rb, "clicked", G_CALLBACK(hex_selected_cb), find_frame_w);
+ g_signal_connect(string_rb, "clicked", G_CALLBACK(string_selected_cb), find_frame_w);
+ g_signal_connect(filter_rb, "clicked", G_CALLBACK(filter_selected_cb), find_frame_w);
+
+ string_selected_cb(NULL, find_frame_w);
+ filter_selected_cb(NULL, find_frame_w);
+
+ window_set_cancel_button(find_frame_w, cancel_bt, window_cancel_button_cb);
+
+ gtk_widget_grab_default(ok_bt);
+
+ /* Catch the "activate" signal on the filter text entry, so that
+ if the user types Return there, we act as if the "OK" button
+ had been selected, as happens if Return is typed if some widget
+ that *doesn't* handle the Return key has the input focus. */
+ dlg_set_activate(filter_text_box, ok_bt);
+
+ /* Give the initial focus to the "Filter" entry box. */
+ gtk_widget_grab_focus(filter_text_box);
+
+ g_signal_connect(find_frame_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(find_frame_w, "destroy", G_CALLBACK(find_frame_destroy_cb), NULL);
+
+ gtk_widget_show(find_frame_w);
+ window_present(find_frame_w);
+}
+
+/* this function opens the find frame dialogue and sets the filter string */
+void
+find_frame_with_filter(char *filter)
+{
+ find_frame_cb(NULL, NULL);
+ gtk_entry_set_text(GTK_ENTRY(filter_text_box), filter);
+}
+
+/*
+ * Check the filter syntax based on the type of search we're doing.
+ */
+static void
+find_filter_te_syntax_check_cb(GtkWidget *w, gpointer parent_w)
+{
+ const gchar *strval;
+ GtkWidget *hex_rb, *string_rb;
+ guint8 *bytes = NULL;
+ size_t nbytes;
+
+ hex_rb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_FIND_HEXDATA_KEY);
+ string_rb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_FIND_STRINGDATA_KEY);
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (hex_rb))) {
+ /*
+ * Hex search - scan the search string to make sure it's valid hex.
+ */
+ strval = gtk_entry_get_text(GTK_ENTRY(w));
+ if (strval == NULL) {
+ /* XXX - can this happen? */
+ colorize_filter_te_as_invalid(w);
+ } else {
+ bytes = convert_string_to_hex(strval, &nbytes);
+ if (bytes == NULL)
+ colorize_filter_te_as_invalid(w);
+ else {
+ g_free(bytes);
+ colorize_filter_te_as_valid(w);
+ }
+ }
+ } else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (string_rb))) {
+ /*
+ * String search. Make sure the string isn't empty.
+ */
+ strval = gtk_entry_get_text(GTK_ENTRY(w));
+ if (strval == NULL) {
+ /* XXX - can this happen? */
+ colorize_filter_te_as_invalid(w);
+ } else {
+ if (strcmp(strval, "") == 0)
+ colorize_filter_te_as_invalid(w);
+ else
+ colorize_filter_te_as_valid(w);
+ }
+ } else {
+ /*
+ * Display filter search; check it with "filter_te_syntax_check_cb()".
+ */
+ filter_te_syntax_check_cb(w, NULL);
+ }
+}
+
+/*
+ * This function will re-check the search text syntax.
+ */
+static void
+hex_selected_cb(GtkWidget *button_rb _U_, gpointer parent_w)
+{
+ GtkWidget *filter_tb, *hex_rb;
+
+ filter_tb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_FILT_TE_PTR_KEY);
+ hex_rb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_FIND_HEXDATA_KEY);
+
+ /* Disable AutoCompletion feature */
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hex_rb)) && g_signal_handler_is_connected(filter_tb, te_presskey_handler_id)) {
+ g_signal_handler_disconnect(filter_tb, te_presskey_handler_id);
+ g_signal_handler_disconnect(parent_w, win_presskey_handler_id);
+ }
+
+ /* Re-check the display filter. */
+ find_filter_te_syntax_check_cb(filter_tb, parent_w);
+ return;
+}
+
+/*
+ * This function will disable the string options until
+ * the string search is selected.
+ */
+static void
+string_selected_cb(GtkWidget *button_rb _U_, gpointer parent_w)
+{
+ GtkWidget *string_rb, *packet_data_rb, *decode_data_rb, *summary_data_rb,
+ *data_combo_lb, *data_combo_cb, *data_case_cb, *filter_tb;
+
+ string_rb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_FIND_STRINGDATA_KEY);
+ packet_data_rb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_SOURCE_DATA_KEY);
+ decode_data_rb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_SOURCE_DECODE_KEY);
+ summary_data_rb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_SOURCE_SUMMARY_KEY);
+
+ data_combo_lb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_FIND_STRINGTYPE_LABEL_KEY);
+ data_combo_cb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_FIND_STRINGTYPE_KEY);
+ data_case_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CASE_SEARCH_KEY);
+ filter_tb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_FILT_TE_PTR_KEY);
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(string_rb))) {
+ gtk_widget_set_sensitive(GTK_WIDGET(packet_data_rb), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(decode_data_rb), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(summary_data_rb), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(data_combo_lb), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(data_combo_cb), TRUE);
+ gtk_widget_set_sensitive(GTK_WIDGET(data_case_cb), TRUE);
+
+ /* Disable AutoCompletion feature */
+ if(g_signal_handler_is_connected(filter_tb, te_presskey_handler_id)) {
+ g_signal_handler_disconnect(filter_tb, te_presskey_handler_id);
+ g_signal_handler_disconnect(parent_w, win_presskey_handler_id);
+ }
+
+ } else {
+ gtk_widget_set_sensitive(GTK_WIDGET(packet_data_rb), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(decode_data_rb), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(summary_data_rb), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(data_combo_lb), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(data_combo_cb), FALSE);
+ gtk_widget_set_sensitive(GTK_WIDGET(data_case_cb), FALSE);
+ }
+ /* Re-check the display filter. */
+ find_filter_te_syntax_check_cb(filter_tb, parent_w);
+ return;
+}
+
+/*
+ * This function will disable the filter button until
+ * the filter search is selected.
+ */
+static void
+filter_selected_cb(GtkWidget *button_rb _U_, gpointer parent_w)
+{
+ GtkWidget *filter_bt, *filter_rb, *filter_te;
+
+ filter_bt = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_FILT_TE_BUTTON_KEY);
+ filter_rb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_FIND_FILTERDATA_KEY);
+ filter_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_FILT_TE_PTR_KEY);
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(filter_rb)))
+ {
+ gtk_widget_set_sensitive(GTK_WIDGET(filter_bt), TRUE);
+ /* Enable AutoCompletion feature */
+ if(!g_signal_handler_is_connected(filter_te, te_presskey_handler_id)) {
+ te_presskey_handler_id = g_signal_connect(filter_te, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
+ win_presskey_handler_id = g_signal_connect(parent_w, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
+ }
+ }
+ else
+ {
+ gtk_widget_set_sensitive(GTK_WIDGET(filter_bt), FALSE);
+ }
+ return;
+}
+
+static void
+find_frame_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w)
+{
+ GtkWidget *filter_te, *up_rb, *hex_rb, *string_rb, *combo_cb,
+ *case_cb, *packet_data_rb, *decode_data_rb, *summary_data_rb;
+ const gchar *filter_text;
+ search_charset_t scs_type = SCS_ASCII_AND_UNICODE;
+ guint8 *bytes = NULL;
+ size_t nbytes = 0;
+ char *string = NULL;
+ dfilter_t *sfcode = NULL;
+ gboolean found_packet=FALSE;
+ gboolean hex_search;
+ gboolean string_search;
+ int string_type;
+
+ filter_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_FIND_FILT_KEY);
+ up_rb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_FIND_BACKWARD_KEY);
+ hex_rb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_FIND_HEXDATA_KEY);
+ string_rb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_FIND_STRINGDATA_KEY);
+ combo_cb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_FIND_STRINGTYPE_KEY);
+ case_cb = (GtkWidget *) g_object_get_data(G_OBJECT(parent_w), E_CASE_SEARCH_KEY);
+ packet_data_rb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_SOURCE_DATA_KEY);
+ decode_data_rb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_SOURCE_DECODE_KEY);
+ summary_data_rb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_SOURCE_SUMMARY_KEY);
+
+ filter_text = gtk_entry_get_text(GTK_ENTRY(filter_te));
+
+ /* Corresponds to the enum in file.c
+ * Character set for text search.
+ * typedef enum {
+ * SCS_ASCII_AND_UNICODE,
+ * SCS_ASCII,
+ * SCS_UNICODE
+ * / * add EBCDIC when it's implemented * /
+ * } search_charset_t;
+ */
+ string_type = gtk_combo_box_get_active (GTK_COMBO_BOX(combo_cb));
+
+ case_type = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(case_cb));
+ packet_data = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(packet_data_rb));
+ decode_data = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(decode_data_rb));
+ summary_data = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(summary_data_rb));
+ hex_search = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (hex_rb));
+ string_search = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (string_rb));
+ /*
+ * Process the search criterion.
+ */
+ if (hex_search) {
+ /*
+ * Hex search - scan the search string to make sure it's valid hex
+ * and to find out how many bytes there are.
+ */
+ bytes = convert_string_to_hex(filter_text, &nbytes);
+ if (bytes == NULL) {
+ statusbar_push_temporary_msg("That's not a valid hex string.");
+ return;
+ }
+ } else if (string_search) {
+ /*
+ * String search.
+ * Make sure we're searching for something, first.
+ */
+ if (strcmp(filter_text, "") == 0) {
+ statusbar_push_temporary_msg("You didn't specify any text for which to search.");
+ return;
+ }
+
+ /*
+ * We are - get the character set type.
+ */
+ if (string_type == SCS_ASCII_AND_UNICODE)
+ scs_type = SCS_ASCII_AND_UNICODE;
+ else if (string_type == SCS_ASCII)
+ scs_type = SCS_ASCII;
+ else if (string_type == SCS_UNICODE)
+ scs_type = SCS_UNICODE;
+ else {
+ statusbar_push_temporary_msg("You didn't choose a valid character set.");
+ return;
+ }
+ string = convert_string_case(filter_text, case_type);
+ } else {
+ /*
+ * Display filter search - try to compile the filter.
+ */
+ if (!dfilter_compile(filter_text, &sfcode)) {
+ /* The attempt failed; report an error. */
+ bad_dfilter_alert_box(filter_text);
+ return;
+ }
+
+ /* Was it empty? */
+ if (sfcode == NULL) {
+ /* Yes - complain. */
+ statusbar_push_temporary_msg("That filter doesn't test anything.");
+ return;
+ }
+ }
+
+ /*
+ * Remember the search parameters.
+ */
+ g_free(cfile.sfilter);
+ cfile.sfilter = g_strdup(filter_text);
+ cfile.dir = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (up_rb)) ? SD_BACKWARD : SD_FORWARD;
+ cfile.hex = hex_search;
+ cfile.string = string_search;
+ cfile.scs_type = scs_type;
+ cfile.case_type = case_type;
+ cfile.packet_data = packet_data;
+ cfile.decode_data = decode_data;
+ cfile.summary_data = summary_data;
+
+ if (cfile.hex) {
+ /* Hex value in packet data */
+ found_packet = cf_find_packet_data(&cfile, bytes, nbytes, cfile.dir);
+ g_free(bytes);
+ if (!found_packet) {
+ /* We didn't find a packet */
+ statusbar_push_temporary_msg("No packet contained those bytes.");
+ return;
+ }
+ } else if (cfile.string) {
+ if (cfile.summary_data) {
+ /* String in the Info column of the summary line */
+ found_packet = cf_find_packet_summary_line(&cfile, string, cfile.dir);
+ g_free(string);
+ if (!found_packet) {
+ statusbar_push_temporary_msg("No packet contained that string in its Info column.");
+ return;
+ }
+ } else if (cfile.decode_data) {
+ /* String in the protocol tree headings */
+ found_packet = cf_find_packet_protocol_tree(&cfile, string, cfile.dir);
+ g_free(string);
+ if (!found_packet) {
+ statusbar_push_temporary_msg("No packet contained that string in its dissected display.");
+ return;
+ }
+ } else if (cfile.packet_data && string) {
+ /* String in the ASCII-converted packet data */
+ found_packet = cf_find_packet_data(&cfile, string, strlen(string), cfile.dir);
+ g_free(string);
+ if (!found_packet) {
+ statusbar_push_temporary_msg("No packet contained that string in its ASCII-converted data.");
+ return;
+ }
+ }
+ } else {
+ /* Search via display filter */
+ found_packet = cf_find_packet_dfilter(&cfile, sfcode, cfile.dir);
+ dfilter_free(sfcode);
+ if (!found_packet) {
+ statusbar_push_temporary_msg("No packet matched that filter.");
+ g_free(bytes);
+ return;
+ }
+ }
+ window_destroy(GTK_WIDGET(parent_w));
+}
+
+static void
+find_frame_close_cb(GtkWidget *close_bt _U_, gpointer parent_w)
+{
+ gtk_grab_remove(GTK_WIDGET(parent_w));
+ window_destroy(GTK_WIDGET(parent_w));
+}
+
+static void
+find_frame_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+ /* Note that we no longer have a "Find Packet" dialog box. */
+ find_frame_w = NULL;
+}
+
+static void
+find_previous_next(GtkWidget *w, gpointer d, search_direction dir)
+{
+ guint8 *bytes;
+ size_t nbytes;
+ char *string;
+ dfilter_t *sfcode;
+
+ if (cfile.sfilter) {
+ cfile.dir = dir;
+ if (cfile.hex) {
+ bytes = convert_string_to_hex(cfile.sfilter, &nbytes);
+ if (bytes == NULL) {
+ /*
+ * XXX - this shouldn't happen, as we've already successfully
+ * translated the string once.
+ */
+ return;
+ }
+ cf_find_packet_data(&cfile, bytes, nbytes, dir);
+ g_free(bytes);
+ } else if (cfile.string) {
+ string = convert_string_case(cfile.sfilter, cfile.case_type);
+ /* OK, what are we searching? */
+ if (cfile.decode_data) {
+ /* The text in the protocol tree */
+ cf_find_packet_protocol_tree(&cfile, string, dir);
+ } else if (cfile.summary_data) {
+ /* The text in the summary line */
+ cf_find_packet_summary_line(&cfile, string, dir);
+ } else {
+ /* The raw packet data */
+ cf_find_packet_data(&cfile, string, strlen(string), dir);
+ }
+ g_free(string);
+ } else {
+ if (!dfilter_compile(cfile.sfilter, &sfcode)) {
+ /*
+ * XXX - this shouldn't happen, as we've already successfully
+ * translated the string once.
+ */
+ return;
+ }
+ if (sfcode == NULL) {
+ /*
+ * XXX - this shouldn't happen, as we've already found that the
+ * string wasn't null.
+ */
+ return;
+ }
+ cf_find_packet_dfilter(&cfile, sfcode, dir);
+ dfilter_free(sfcode);
+ }
+ } else
+ find_frame_cb(w, d);
+}
+
+void
+find_next_cb(GtkWidget *w , gpointer d)
+{
+ find_previous_next(w, d, SD_FORWARD);
+}
+
+void
+find_previous_cb(GtkWidget *w , gpointer d)
+{
+ find_previous_next(w, d, SD_BACKWARD);
+}
diff --git a/ui/gtk/find_dlg.h b/ui/gtk/find_dlg.h
new file mode 100644
index 0000000000..d71f711372
--- /dev/null
+++ b/ui/gtk/find_dlg.h
@@ -0,0 +1,61 @@
+/* find_dlg.h
+ * Definitions for "find frame" window
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __FIND_DLG_H__
+#define __FIND_DLG_H__
+
+/** @file
+ * "Find" dialog box and related functions.
+ * @ingroup dialog_group
+ */
+
+/** User requested the "Find" dialog box by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void find_frame_cb(GtkWidget *widget, gpointer data);
+
+/** User requested the "Find Next" function.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void find_next_cb(GtkWidget *widget, gpointer data);
+
+/** User requested the "Find Previous" function.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void find_previous_cb(GtkWidget *widget, gpointer data);
+
+/** Find frame by filter.
+ *
+ * @param filter the filter string
+ */
+extern void find_frame_with_filter(char *filter);
+
+#endif /* find_dlg.h */
diff --git a/ui/gtk/firewall_dlg.c b/ui/gtk/firewall_dlg.c
new file mode 100644
index 0000000000..72ffcfa405
--- /dev/null
+++ b/ui/gtk/firewall_dlg.c
@@ -0,0 +1,794 @@
+/* firewall_rules_dlg.c
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Generate firewall ACL rules based on packet addresses and ports.
+ * For directional rules, an outside interface is assumed.
+ *
+ * There may be better ways to present the information, e.g. all rules
+ * in one huge text window, or some sort of tree view.
+ */
+
+/*
+ * To add a new product, add syntax functions modify the products[] array.
+ *
+ * To add a new syntax function, add its prototype above the products[]
+ * array, and add the function below with all the others.
+ */
+
+/* Copied from ssl-dlg.c */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/addr_resolv.h>
+#include <epan/epan_dissect.h>
+#include <epan/filesystem.h>
+#include <epan/dissectors/packet-ipv6.h>
+
+#include <../alert_box.h>
+#include <../simple_dialog.h>
+#include <wsutil/file_util.h>
+
+#include "ui/gtk/main.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/file_dlg.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/old-gtk-compat.h"
+#include "ui/gtk/firewall_dlg.h"
+
+#define MAX_RULE_LEN 200
+
+/* Rule types */
+typedef enum {
+ RT_NONE,
+ RT_MAC_SRC,
+ RT_MAC_DST,
+ RT_IPv4_SRC,
+ RT_IPv4_DST,
+ RT_PORT_SRC,
+ RT_PORT_DST,
+ RT_IPv4_PORT_SRC,
+ RT_IPv4_PORT_DST,
+ NUM_RULE_TYPES
+} rule_type_t;
+
+
+/* Copied from packet_info struct */
+typedef struct _rule_info_t {
+ gint product;
+ address dl_src;
+ address dl_dst;
+ address net_src;
+ address net_dst;
+ port_type ptype;
+ guint32 srcport;
+ guint32 destport;
+ GtkWidget *text;
+ GtkWidget *filter_combo_box;
+ GtkWidget *deny_cb;
+ GtkWidget *inbound_cb;
+ GtkWidget *firewall_save_as_w;
+ gboolean inbound;
+ gboolean deny;
+ rule_type_t rule_type;
+} rule_info_t;
+
+/* Syntax function prototypes */
+typedef void (*syntax_func)(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+
+static void sf_dummy(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+
+static void sf_ipfw_mac(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+static void sf_netfilter_mac(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+
+static void sf_ios_std_ipv4(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+static void sf_ios_ext_ipv4(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+static void sf_ipfilter_ipv4(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+static void sf_ipfw_ipv4(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+static void sf_netfilter_ipv4(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+static void sf_pf_ipv4(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+/* XXX - Can you addresses-only filters using WFW/netsh? */
+
+static void sf_ios_ext_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+static void sf_ipfilter_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+static void sf_ipfw_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+static void sf_netfilter_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+static void sf_pf_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+static void sf_netsh_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+
+static void sf_ios_ext_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+static void sf_ipfilter_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+static void sf_ipfw_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+static void sf_netfilter_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+static void sf_pf_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+static void sf_netsh_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny);
+
+typedef struct _fw_product_t {
+ gchar *name;
+ gchar *comment_pfx;
+ syntax_func mac_func;
+ syntax_func ipv4_func;
+ syntax_func port_func;
+ syntax_func ipv4_port_func;
+ gboolean does_inbound;
+} fw_product;
+
+static fw_product products[] = {
+ { "Cisco IOS (standard)", "!", NULL, sf_ios_std_ipv4, NULL, NULL, FALSE },
+ { "Cisco IOS (extended)", "!",
+ NULL, sf_ios_ext_ipv4, sf_ios_ext_port, sf_ios_ext_ipv4_port, TRUE },
+ { "IP Filter (ipfilter)", "#",
+ NULL, sf_ipfilter_ipv4, sf_ipfilter_port, sf_ipfilter_ipv4_port, TRUE },
+ { "IPFirewall (ipfw)", "#",
+ sf_ipfw_mac, sf_ipfw_ipv4, sf_ipfw_port, sf_ipfw_ipv4_port, TRUE },
+ { "Netfilter (iptables)", "#",
+ sf_netfilter_mac, sf_netfilter_ipv4, sf_netfilter_port,
+ sf_netfilter_ipv4_port, TRUE },
+ { "Packet Filter (pf)", "#",
+ NULL, sf_pf_ipv4, sf_pf_port, sf_pf_ipv4_port, TRUE },
+ { "Windows Firewall (netsh)", "#",
+ NULL, NULL, sf_netsh_port, sf_netsh_ipv4_port, FALSE }
+};
+#define NUM_PRODS (sizeof(products) / sizeof(fw_product))
+
+
+static void select_product(GtkWidget * win, gpointer data);
+static void select_filter(GtkWidget * win, gpointer data);
+static void toggle_inbound(GtkToggleButton *t, gpointer data);
+static void toggle_deny(GtkToggleButton *t, gpointer data);
+static void set_rule_text(rule_info_t *rule_info);
+static void firewall_destroy_cb(GtkWidget * win, gpointer data);
+static void firewall_copy_cmd_cb(GtkWidget * w, gpointer data);
+static void firewall_save_as_cmd_cb(GtkWidget * w, gpointer data);
+static gboolean firewall_save_as_ok_cb(GtkWidget * w, gpointer fs);
+static void firewall_save_as_destroy_cb(GtkWidget * win, gpointer user_data);
+
+#define WS_RULE_INFO_KEY "rule_info_key"
+
+#if 0
+/* List of "rule_info_t" structures for all rule windows. */
+static GList *rule_infos;
+
+/* Remove a "rule_info_t" structure from the list. */
+static void
+forget_rule_info(rule_info_t *rule_info)
+{
+ rule_infos = g_list_remove(rule_infos, rule_info);
+}
+#endif
+
+void
+firewall_rule_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ GtkWidget *rule_w, *vbox, *txt_scrollw, *text;
+ GtkWidget *label, *product_combo_box;
+ GtkWidget *hbox, *button_hbox, *button;
+ rule_info_t *rule_info;
+ packet_info *pinfo = &cfile.edt->pi;
+ guint i;
+
+ rule_info = g_new0(rule_info_t, 1);
+ COPY_ADDRESS(&(rule_info->dl_src), &(pinfo->dl_src));
+ COPY_ADDRESS(&(rule_info->dl_dst), &(pinfo->dl_dst));
+ COPY_ADDRESS(&(rule_info->net_src), &(pinfo->net_src));
+ COPY_ADDRESS(&(rule_info->net_dst), &(pinfo->net_dst));
+ rule_info->ptype = pinfo->ptype;
+ rule_info->srcport = pinfo->srcport;
+ rule_info->destport = pinfo->destport;
+ rule_info->inbound = TRUE;
+ rule_info->deny = TRUE;
+ rule_info->product = 0;
+
+ rule_w = dlg_window_new("Firewall ACL Rules");
+
+ gtk_widget_set_name(rule_w, "Firewall ACL rule window");
+ gtk_container_set_border_width(GTK_CONTAINER(rule_w), 6);
+
+ /* setup the container */
+ vbox = gtk_vbox_new(FALSE, 6);
+ gtk_container_add(GTK_CONTAINER(rule_w), vbox);
+
+ /* rule type selectors hbox */
+ hbox = gtk_hbox_new(FALSE, 1);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+ /* product selector */
+ label = gtk_label_new("Product");
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+ product_combo_box = gtk_combo_box_text_new();
+ for (i = 0; i < NUM_PRODS; i++) {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(product_combo_box), products[i].name);
+ }
+ g_object_set_data(G_OBJECT(product_combo_box), WS_RULE_INFO_KEY, rule_info);
+ g_signal_connect(product_combo_box, "changed", G_CALLBACK(select_product), NULL);
+ gtk_box_pack_start(GTK_BOX(hbox), product_combo_box, FALSE, FALSE, 5);
+
+ /* type selector */
+ label = gtk_label_new("Filter");
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 10);
+
+ rule_info->filter_combo_box = ws_combo_box_new_text_and_pointer();
+ g_object_set_data(G_OBJECT(rule_info->filter_combo_box), WS_RULE_INFO_KEY, rule_info); \
+ g_signal_connect(rule_info->filter_combo_box, "changed", G_CALLBACK(select_filter), NULL);
+ gtk_box_pack_start(GTK_BOX(hbox), rule_info->filter_combo_box, FALSE, FALSE, 5);
+
+ /* inbound selector */
+ rule_info->inbound_cb = gtk_check_button_new_with_label("Inbound");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rule_info->inbound_cb),
+ rule_info->inbound);
+ gtk_box_pack_start(GTK_BOX(hbox), rule_info->inbound_cb, FALSE, FALSE, 10);
+ g_signal_connect(rule_info->inbound_cb, "toggled", G_CALLBACK(toggle_inbound), rule_info);
+
+ /* deny selector */
+ rule_info->deny_cb = gtk_check_button_new_with_label("Deny");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rule_info->deny_cb),
+ rule_info->deny);
+ gtk_box_pack_start(GTK_BOX(hbox), rule_info->deny_cb, FALSE, FALSE, 10);
+ g_signal_connect(rule_info->deny_cb, "toggled", G_CALLBACK(toggle_deny), rule_info);
+
+ /* create a scrolled window for the text */
+ txt_scrollw = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
+ GTK_SHADOW_IN);
+ gtk_box_pack_start(GTK_BOX(vbox), txt_scrollw, TRUE, TRUE, 0);
+
+ /* create a text box */
+ text = gtk_text_view_new();
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
+ gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
+ rule_info->text = text;
+
+ /* Button row */
+ button_hbox = dlg_button_row_new(GTK_STOCK_HELP, GTK_STOCK_COPY, GTK_STOCK_SAVE, GTK_STOCK_CANCEL, NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), button_hbox, FALSE, FALSE, 0);
+
+ /* Create Copy Button */
+ button = g_object_get_data(G_OBJECT(button_hbox), GTK_STOCK_COPY);
+ g_signal_connect(button, "clicked", G_CALLBACK(firewall_copy_cmd_cb), rule_info);
+ gtk_widget_set_tooltip_text(button, "Copy rule to clipboard");
+
+ /* Create Save Button */
+ button = g_object_get_data(G_OBJECT(button_hbox), GTK_STOCK_SAVE);
+ g_signal_connect(button, "clicked", G_CALLBACK(firewall_save_as_cmd_cb), rule_info);
+ gtk_widget_set_tooltip_text(button, "Save the rule as currently displayed");
+
+ button = g_object_get_data(G_OBJECT(button_hbox), GTK_STOCK_CANCEL);
+ gtk_widget_set_tooltip_text(button, "Cancel the dialog");
+ window_set_cancel_button(rule_w, button, window_cancel_button_cb);
+
+ button = g_object_get_data(G_OBJECT(button_hbox), GTK_STOCK_HELP);
+ g_signal_connect(button, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_FIREWALL_DIALOG);
+
+ /* Tuck away the rule_info object into the window */
+ g_object_set_data(G_OBJECT(rule_w), WS_RULE_INFO_KEY, rule_info);
+
+ g_signal_connect(rule_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(rule_w, "destroy", G_CALLBACK(firewall_destroy_cb), NULL);
+
+ /* Make sure this widget gets destroyed if we quit the main loop,
+ so that if we exit, we clean up any temporary files we have
+ for "Follow SSL Stream" windows.
+ gtk_quit_add_destroy is deprecated and should not be used in newly-written code. This function is going to be removed in GTK+ 3.0
+
+ gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(rule_w));
+
+ */
+
+ gtk_combo_box_set_active(GTK_COMBO_BOX(product_combo_box), 0); /* invokes select_product callback */
+ gtk_widget_show_all(rule_w);
+ window_present(rule_w);
+}
+
+/* Set the current product. */
+#define ADD_TO_FILTER_MENU(rt) \
+ ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(rule_info->filter_combo_box), name, GUINT_TO_POINTER(rt)); \
+ if (rule_type == RT_NONE) { \
+ rule_type = rt; \
+ }
+
+#define NAME_TCP_UDP (rule_info->ptype == PT_TCP ? "TCP" : "UDP")
+
+static void
+select_product(GtkWidget *w, gpointer data _U_)
+{
+ guint prod = gtk_combo_box_get_active(GTK_COMBO_BOX(w));
+ rule_info_t *rule_info;
+ gchar name[MAX_RULE_LEN], addr_str[MAX_RULE_LEN];
+ address *addr;
+ rule_type_t rule_type = RT_NONE;
+ gboolean sensitive = FALSE;
+
+ rule_info = g_object_get_data(G_OBJECT(w), WS_RULE_INFO_KEY);
+
+ if (prod >= NUM_PRODS || !rule_info)
+ return;
+
+ rule_info->product = prod;
+
+ /* Clear the list store (ie: the como_box list items) */
+ ws_combo_box_clear_text_and_pointer(GTK_COMBO_BOX(rule_info->filter_combo_box));
+
+ /* Fill in valid combo_box list items (in the list store). */
+ if (products[prod].mac_func && rule_info->dl_src.type == AT_ETHER) {
+ addr = &(rule_info->dl_src);
+ address_to_str_buf(addr, name, MAX_RULE_LEN);
+ ADD_TO_FILTER_MENU(RT_MAC_SRC);
+
+ addr = &(rule_info->dl_dst);
+ address_to_str_buf(addr, name, MAX_RULE_LEN);
+ ADD_TO_FILTER_MENU(RT_MAC_DST);
+ }
+
+ if (products[prod].ipv4_func && rule_info->net_src.type == AT_IPv4) {
+ addr = &(rule_info->net_src);
+ address_to_str_buf(addr, name, MAX_RULE_LEN);
+ ADD_TO_FILTER_MENU(RT_IPv4_SRC);
+
+ addr = &(rule_info->net_dst);
+ address_to_str_buf(addr, name, MAX_RULE_LEN);
+ ADD_TO_FILTER_MENU(RT_IPv4_DST);
+ }
+
+ if (products[prod].port_func && (rule_info->ptype == PT_TCP || rule_info->ptype == PT_UDP)) {
+ g_snprintf(name, MAX_RULE_LEN, "%s port %u", NAME_TCP_UDP,
+ rule_info->srcport);
+ ADD_TO_FILTER_MENU(RT_PORT_SRC);
+ if (rule_info->srcport != rule_info->destport) {
+ g_snprintf(name, MAX_RULE_LEN, "%s port %u", NAME_TCP_UDP,
+ rule_info->destport);
+ ADD_TO_FILTER_MENU(RT_PORT_DST);
+ }
+ }
+
+ if (products[prod].ipv4_port_func && rule_info->net_src.type == AT_IPv4 &&
+ (rule_info->ptype == PT_TCP || rule_info->ptype == PT_UDP)) {
+ addr = &(rule_info->net_src);
+ address_to_str_buf(addr, addr_str, MAX_RULE_LEN);
+ g_snprintf(name, MAX_RULE_LEN, "%s + %s port %u", addr_str,
+ NAME_TCP_UDP, rule_info->srcport);
+ ADD_TO_FILTER_MENU(RT_IPv4_PORT_SRC);
+
+ addr = &(rule_info->net_dst);
+ address_to_str_buf(addr, addr_str, MAX_RULE_LEN);
+ g_snprintf(name, MAX_RULE_LEN, "%s + %s port %u", addr_str,
+ NAME_TCP_UDP, rule_info->destport);
+ ADD_TO_FILTER_MENU(RT_IPv4_PORT_DST);
+ }
+
+ if (rule_type != RT_NONE) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(rule_info->filter_combo_box), 0); /* invokes select_filter callback */
+ sensitive = TRUE;
+ } else {
+ select_filter(rule_info->filter_combo_box, NULL); /* Call if RT_NONE [with nothing selected] */
+ }
+
+ gtk_widget_set_sensitive(rule_info->filter_combo_box, sensitive);
+ gtk_widget_set_sensitive(rule_info->inbound_cb, products[prod].does_inbound && sensitive);
+ gtk_widget_set_sensitive(rule_info->deny_cb, sensitive);
+}
+
+/* Set the rule text based upon the current product and current filter. */
+static void
+select_filter(GtkWidget *w, gpointer data _U_)
+{
+ rule_type_t cur_type;
+ rule_info_t *rule_info;
+ gpointer ptr;
+
+ rule_info = g_object_get_data(G_OBJECT(w), WS_RULE_INFO_KEY);
+ if (!rule_info)
+ return;
+
+
+ if (ws_combo_box_get_active_pointer(GTK_COMBO_BOX(w), &ptr))
+ cur_type = GPOINTER_TO_UINT(ptr);
+ else
+ cur_type = RT_NONE; /* If nothing selected (eg: nothing in filter list) */
+
+ if (cur_type >= NUM_RULE_TYPES)
+ return;
+
+ rule_info->rule_type = cur_type;
+
+ set_rule_text(rule_info);
+}
+
+/* Set inbound/outbound */
+static void
+toggle_inbound(GtkToggleButton *t, gpointer data)
+{
+ rule_info_t *rule_info = (rule_info_t *) data;
+
+ rule_info->inbound = gtk_toggle_button_get_active(t);
+
+ set_rule_text(rule_info);
+}
+
+/* Set deny/allow. */
+static void
+toggle_deny(GtkToggleButton *t, gpointer data)
+{
+ rule_info_t *rule_info = (rule_info_t *) data;
+
+ rule_info->deny = gtk_toggle_button_get_active(t);
+
+ set_rule_text(rule_info);
+}
+
+/* Set the rule text */
+#define DL_ADDR (rt == RT_MAC_SRC ? &(rule_info->dl_src) : &(rule_info->dl_dst))
+#define NET_ADDR (rt == RT_IPv4_SRC ? &(rule_info->net_src) : &(rule_info->net_dst))
+#define NET_PORT (rt == RT_PORT_SRC ? rule_info->srcport : rule_info->destport)
+static void
+set_rule_text(rule_info_t *rule_info) {
+ GString *rtxt = g_string_new("");
+ gchar addr_str[MAX_RULE_LEN];
+ rule_type_t rt = rule_info->rule_type;
+ guint prod = rule_info->product;
+ address *addr = NULL;
+ guint32 port = 0;
+ syntax_func rt_func = NULL;
+
+ GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(rule_info->text));
+
+ if (prod < NUM_PRODS) {
+ g_string_printf(rtxt, "%s %s\n", products[prod].comment_pfx, products[prod].name);
+ switch(rt) {
+ case RT_NONE:
+ g_string_append_printf(rtxt, "%s Not supported", products[prod].comment_pfx);
+ rt_func = sf_dummy;
+ break;
+ case RT_MAC_SRC:
+ case RT_MAC_DST:
+ addr = DL_ADDR;
+ address_to_str_buf(addr, addr_str, MAX_RULE_LEN);
+ rt_func = products[prod].mac_func;
+ break;
+ case RT_IPv4_SRC:
+ case RT_IPv4_DST:
+ addr = NET_ADDR;
+ address_to_str_buf(addr, addr_str, MAX_RULE_LEN);
+ rt_func = products[prod].ipv4_func;
+ break;
+ case RT_PORT_SRC:
+ case RT_PORT_DST:
+ port = NET_PORT;
+ rt_func = products[prod].port_func;
+ break;
+ case RT_IPv4_PORT_SRC:
+ case RT_IPv4_PORT_DST:
+ addr = NET_ADDR;
+ address_to_str_buf(addr, addr_str, MAX_RULE_LEN);
+ port = NET_PORT;
+ rt_func = products[prod].ipv4_port_func;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (rt_func) {
+ rt_func(rtxt, addr_str, port, rule_info->ptype, rule_info->inbound, rule_info->deny);
+ } else {
+ g_string_append_printf(rtxt, "ERROR: Unable to create rule");
+ }
+
+ gtk_text_buffer_set_text(buf, rtxt->str, (gint) rtxt->len);
+
+ g_string_free(rtxt, TRUE);
+}
+
+
+/* Rule text functions */
+/* Dummy */
+static void sf_dummy(GString *rtxt _U_, gchar *addr _U_, guint32 port _U_, port_type ptype _U_, gboolean inbound _U_, gboolean deny _U_) {
+}
+
+/* MAC */
+#define IPFW_DENY (deny ? "deny" : "allow")
+#define IPFW_IN (inbound ? "in" : "out")
+static void sf_ipfw_mac(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
+ g_string_append_printf(rtxt, "add %s MAC %s any %s",
+ IPFW_DENY, addr, IPFW_IN);
+}
+
+#define NF_DROP (deny ? "DROP" : "ACCEPT")
+#define NF_INPUT (inbound ? "INPUT" : "OUTPUT")
+static void sf_netfilter_mac(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
+ g_string_append_printf(rtxt, "iptables -A %s --mac-source %s -j %s",
+ NF_INPUT, addr, NF_DROP);
+}
+
+/* IPv4 */
+#define IOS_DENY (deny ? "deny" : "permit")
+static void sf_ios_std_ipv4(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound _U_, gboolean deny) {
+ g_string_append_printf(rtxt, "access-list NUMBER %s host %s", IOS_DENY, addr);
+}
+
+static void sf_ios_ext_ipv4(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
+ if (inbound)
+ g_string_append_printf(rtxt, "access-list NUMBER %s ip host %s any", IOS_DENY, addr);
+ else
+ g_string_append_printf(rtxt, "access-list NUMBER %s ip any host %s", IOS_DENY, addr);
+}
+
+#define IPFILTER_DENY (deny ? "block" : "pass")
+#define IPFILTER_IN (inbound ? "in" : "out")
+static void sf_ipfilter_ipv4(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
+ g_string_append_printf(rtxt, "%s %s on le0 from %s to any",
+ IPFILTER_DENY, IPFILTER_IN, addr);
+}
+
+static void sf_ipfw_ipv4(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
+ g_string_append_printf(rtxt, "add %s ip from %s to any %s",
+ IPFW_DENY, addr, IPFW_IN);
+}
+
+static void sf_netfilter_ipv4(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
+ g_string_append_printf(rtxt, "iptables -A %s -i eth0 -d %s/32 -j %s",
+ NF_INPUT, addr, NF_DROP);
+}
+
+#define PF_DENY (deny ? "block" : "pass")
+#define PF_IN (inbound ? "in" : "out")
+static void sf_pf_ipv4(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
+ g_string_append_printf(rtxt, "%s %s quick on $ext_if from %s to any",
+ PF_DENY, PF_IN, addr);
+}
+
+/* Port */
+#define RT_TCP_UDP (ptype == PT_TCP ? "tcp" : "udp")
+static void sf_ios_ext_port(GString *rtxt, gchar *addr _U_, guint32 port, port_type ptype, gboolean inbound _U_, gboolean deny) {
+ g_string_append_printf(rtxt, "access-list NUMBER %s %s any any eq %u",
+ IOS_DENY, RT_TCP_UDP, port);
+}
+
+static void sf_ipfilter_port(GString *rtxt, gchar *addr _U_, guint32 port, port_type ptype _U_, gboolean inbound, gboolean deny) {
+ g_string_append_printf(rtxt, "%s %s on le0 proto %s from any to any port = %u",
+ IPFILTER_DENY, IPFILTER_IN, RT_TCP_UDP, port);
+}
+
+static void sf_ipfw_port(GString *rtxt, gchar *addr _U_, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
+ g_string_append_printf(rtxt, "add %s %s from any to any %u %s",
+ IPFW_DENY, RT_TCP_UDP, port, IPFW_IN);
+}
+
+static void sf_netfilter_port(GString *rtxt, gchar *addr _U_, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
+ g_string_append_printf(rtxt, "iptables -A %s -p %s --destination-port %u -j %s",
+ NF_INPUT, RT_TCP_UDP, port, NF_DROP);
+}
+
+static void sf_pf_port(GString *rtxt, gchar *addr _U_, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
+ g_string_append_printf(rtxt, "%s %s quick on $ext_if proto %s from any to any port %u",
+ PF_DENY, PF_IN, RT_TCP_UDP, port);
+}
+
+#define NETSH_DENY (deny ? "DISABLE" : "ENABLE")
+static void sf_netsh_port(GString *rtxt, gchar *addr _U_, guint32 port, port_type ptype, gboolean inbound _U_, gboolean deny) {
+ g_string_append_printf(rtxt, "add portopening %s %u Wireshark %s",
+ RT_TCP_UDP, port, NETSH_DENY);
+}
+
+/* IPv4 + port */
+static void sf_ios_ext_ipv4_port(GString *rtxt, gchar *addr, guint32 port _U_, port_type ptype _U_, gboolean inbound, gboolean deny) {
+ if (inbound)
+ g_string_append_printf(rtxt, "access-list NUMBER %s %s host %s any eq %u", IOS_DENY, RT_TCP_UDP, addr, port);
+ else
+ g_string_append_printf(rtxt, "access-list NUMBER %s %s any host %s eq %u", IOS_DENY, RT_TCP_UDP, addr, port);
+}
+
+static void sf_ipfilter_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
+ if (inbound)
+ g_string_append_printf(rtxt, "%s %s on le0 proto %s from %s to any port = %u",
+ IPFILTER_DENY, IPFILTER_IN, RT_TCP_UDP, addr, port);
+ else
+ g_string_append_printf(rtxt, "%s %s on le0 proto %s from any to %s port = %u",
+ IPFILTER_DENY, IPFILTER_IN, RT_TCP_UDP, addr, port);
+}
+
+static void sf_ipfw_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
+ g_string_append_printf(rtxt, "add %s %s from %s to any %u %s",
+ IPFW_DENY, RT_TCP_UDP, addr, port, IPFW_IN);
+}
+
+static void sf_pf_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
+ g_string_append_printf(rtxt, "%s %s quick on $ext_if proto %s from %s to any port %u",
+ PF_DENY, PF_IN, RT_TCP_UDP, addr, port);
+}
+
+static void sf_netfilter_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound, gboolean deny) {
+ g_string_append_printf(rtxt, "iptables -A %s -p %s -d %s/32 --destination-port %u -j %s",
+ NF_INPUT, RT_TCP_UDP, addr, port, NF_DROP);
+}
+
+static void sf_netsh_ipv4_port(GString *rtxt, gchar *addr, guint32 port, port_type ptype, gboolean inbound _U_, gboolean deny) {
+ g_string_append_printf(rtxt, "add portopening %s %u Wireshark %s %s",
+ RT_TCP_UDP, port, NETSH_DENY, addr);
+}
+
+/* The destroy call back has the responsibility of
+ * unlinking the temporary file
+ * and freeing the filter_out_filter */
+static void
+firewall_destroy_cb(GtkWidget *w, gpointer data _U_)
+{
+ rule_info_t *rule_info;
+
+ rule_info = g_object_get_data(G_OBJECT(w), WS_RULE_INFO_KEY);
+#if 0
+ forget_rule_info(rule_info);
+#endif
+ g_free(rule_info);
+ gtk_widget_destroy(w);
+}
+
+static void
+firewall_copy_cmd_cb(GtkWidget *w _U_, gpointer data)
+{
+ rule_info_t *rule_info = data;
+
+ GtkTextIter start, end;
+ GtkTextBuffer *buf;
+
+ buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(rule_info->text));
+ gtk_text_buffer_get_start_iter(buf, &start);
+ gtk_text_buffer_get_end_iter(buf, &end);
+ gtk_text_buffer_select_range(buf, &start, &end);
+ gtk_text_buffer_copy_clipboard(buf, gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
+}
+
+/*
+ * Keep a static pointer to the current "Save SSL Follow Stream As" window, if
+ * any, so that if somebody tries to do "Save"
+ * while there's already a "Save SSL Follow Stream" window up, we just pop
+ * up the existing one, rather than creating a new one.
+ */
+static void
+firewall_save_as_cmd_cb(GtkWidget *w _U_, gpointer data)
+{
+ GtkWidget *new_win;
+ rule_info_t *rule_info = data;
+
+#if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
+ if (rule_info->firewall_save_as_w != NULL) {
+ /* There's already a dialog box; reactivate it. */
+ reactivate_window(rule_info->firewall_save_as_w);
+ return;
+ }
+#endif
+ new_win = file_selection_new("Wireshark: Save Firewall ACL Rule",
+ FILE_SELECTION_SAVE);
+ rule_info->firewall_save_as_w = new_win;
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(new_win), TRUE);
+
+ /* Tuck away the rule_info object into the window */
+ g_object_set_data(G_OBJECT(new_win), WS_RULE_INFO_KEY, rule_info);
+
+ g_signal_connect(new_win, "destroy", G_CALLBACK(firewall_save_as_destroy_cb), rule_info);
+
+#if 0
+ if (gtk_dialog_run(GTK_DIALOG(new_win)) == GTK_RESPONSE_ACCEPT)
+ {
+ firewall_save_as_ok_cb(new_win, new_win);
+ } else {
+ window_destroy(new_win);
+ }
+#else
+ /* "Run" the GtkFileChooserDialog. */
+ /* Upon exit: If "Accept" run the OK callback. */
+ /* If the OK callback returns with a FALSE status, re-run the dialog.*/
+ /* If not accept (ie: cancel) destroy the window. */
+ /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
+ /* return with a TRUE status so that the dialog window will be destroyed. */
+ /* Trying to re-run the dialog after popping up an alert box will not work */
+ /* since the user will not be able to dismiss the alert box. */
+ /* The (somewhat unfriendly) effect: the user must re-invoke the */
+ /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
+ /* */
+ /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
+ /* GtkFileChooserDialog. */
+ while (gtk_dialog_run(GTK_DIALOG(new_win)) == GTK_RESPONSE_ACCEPT) {
+ if (firewall_save_as_ok_cb(NULL, new_win)) {
+ break; /* we're done */
+ }
+ }
+ window_destroy(new_win);
+#endif
+}
+
+
+static gboolean
+firewall_save_as_ok_cb(GtkWidget * w _U_, gpointer fs)
+{
+ gchar *to_name, *rule;
+ rule_info_t *rule_info;
+ FILE *fh;
+ gchar *dirname;
+
+ GtkTextIter start, end;
+ GtkTextBuffer *buf;
+
+ to_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
+
+ /* Perhaps the user specified a directory instead of a file.
+ Check whether they did. */
+ if (test_for_directory(to_name) == EISDIR) {
+ /* It's a directory - set the file selection box to display that
+ directory, and leave the selection box displayed. */
+ set_last_open_dir(to_name);
+ g_free(to_name);
+ file_selection_set_current_folder(fs, get_last_open_dir());
+ gtk_file_chooser_set_current_name(fs, "");
+ return FALSE; /* run the dialog again */
+ }
+
+ rule_info = g_object_get_data(G_OBJECT(fs), WS_RULE_INFO_KEY);
+ fh = ws_fopen(to_name, "w");
+ if (fh == NULL) {
+ open_failure_alert_box(to_name, errno, TRUE);
+ g_free(to_name);
+ return TRUE;
+ }
+
+ buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(rule_info->text));
+ gtk_text_buffer_get_start_iter(buf, &start);
+ gtk_text_buffer_get_end_iter(buf, &end);
+ rule = gtk_text_buffer_get_text(buf, &start, &end, FALSE);
+
+ fputs(rule, fh);
+ fclose(fh);
+
+#if 0 /* handled by caller (for now) */
+ gtk_widget_hide(GTK_WIDGET(fs));
+ window_destroy(GTK_WIDGET(fs));
+#endif
+ /* Save the directory name for future file dialogs. */
+ dirname = get_dirname(to_name); /* Overwrites to_name */
+ set_last_open_dir(dirname);
+ g_free(to_name);
+
+ return TRUE;
+}
+
+static void
+firewall_save_as_destroy_cb(GtkWidget * win _U_, gpointer data)
+{
+ rule_info_t *rule_info = data;
+
+ /* Note that we no longer have a dialog box. */
+ rule_info->firewall_save_as_w = NULL;
+}
diff --git a/ui/gtk/firewall_dlg.h b/ui/gtk/firewall_dlg.h
new file mode 100644
index 0000000000..b457f7078b
--- /dev/null
+++ b/ui/gtk/firewall_dlg.h
@@ -0,0 +1,32 @@
+/* firewall_dlg.h
+ * Produce ACL rules for various products from a packet.
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2006 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __FIREWALL_DLG_H__
+#define __FIREWALL_DLG_H__
+
+/* Generate ACL / firewall rules from different fields in the
+ selected packet. */
+void firewall_rule_cb(GtkWidget * w, gpointer data _U_);
+
+#endif /* __FIREWALL_DLG_H__ */
diff --git a/ui/gtk/flow_graph.c b/ui/gtk/flow_graph.c
new file mode 100644
index 0000000000..f8b62adeae
--- /dev/null
+++ b/ui/gtk/flow_graph.c
@@ -0,0 +1,673 @@
+/* flow_graph.c
+ * $Id$
+ * Allows to display a flow graph of the currently displayed packets
+ *
+ * Copyright 2004, Ericsson , Spain
+ * By Francisco Alcoba <francisco.alcoba@ericsson.com>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/epan.h>
+#include <epan/packet.h>
+#include <epan/filesystem.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/to_str.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-tcp.h>
+#include <epan/strutil.h>
+
+#include "../stat_menu.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/graph_analysis.h"
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/main.h"
+
+
+
+#define TYPE_OF_PACKETS_DISPLAYED 0
+#define TYPE_OF_PACKETS_ALL 1
+
+#define TYPE_OF_FLOW_GENERAL 0
+#define TYPE_OF_FLOW_TCP 1
+
+#define NODE_ADDR_TYPE_SRCDST 0
+#define NODE_ADDR_TYPE_NET_SRCDST 1
+
+static int type_of_packets = TYPE_OF_PACKETS_DISPLAYED;
+static int type_of_flow = TYPE_OF_FLOW_GENERAL;
+static int node_addr_type = NODE_ADDR_TYPE_SRCDST;
+
+static int tap_identifier;
+static gboolean have_frame_tap_listener=FALSE;
+static gboolean have_tcp_tap_listener=FALSE;
+static graph_analysis_info_t *graph_analysis = NULL;
+static graph_analysis_data_t *graph_analysis_data = NULL;
+
+static GtkWidget *flow_graph_dlg = NULL;
+
+static GtkWidget *select_all_rb;
+static GtkWidget *select_displayed_rb;
+static GtkWidget *select_general_rb;
+static GtkWidget *select_tcp_rb;
+static GtkWidget *src_dst_rb;
+static GtkWidget *net_src_dst_rb;
+
+/****************************************************************************/
+/* free up memory and initialize the pointers */
+
+static void
+flow_graph_reset(void *ptr _U_)
+{
+ graph_analysis_item_t *graph_item;
+
+ GList* list;
+
+ if (graph_analysis !=NULL){
+
+ /* free the graph data items */
+ list = g_list_first(graph_analysis->list);
+ while (list)
+ {
+ graph_item = list->data;
+ g_free(graph_item->frame_label);
+ g_free(graph_item->comment);
+ g_free(list->data);
+ list = g_list_next(list);
+ }
+ g_list_free(graph_analysis->list);
+ graph_analysis->nconv = 0;
+ graph_analysis->list = NULL;
+ }
+}
+
+/****************************************************************************/
+static void
+flow_graph_data_init(void) {
+ graph_analysis = g_malloc(sizeof(graph_analysis_info_t));
+ graph_analysis->nconv = 0;
+ graph_analysis->list = NULL;
+}
+
+
+/****************************************************************************/
+static void
+remove_tap_listener_flow_graph(void)
+{
+ protect_thread_critical_region();
+ remove_tap_listener(&(tap_identifier));
+ unprotect_thread_critical_region();
+
+ have_frame_tap_listener=FALSE;
+ have_tcp_tap_listener=FALSE;
+}
+
+
+/****************************************************************************/
+/* CALLBACKS */
+/****************************************************************************/
+static void
+flow_graph_on_destroy(GObject *object _U_, gpointer user_data _U_)
+{
+ /* remove_tap_listeners */
+ remove_tap_listener_flow_graph();
+
+ /* Clean up memory used by tap */
+ flow_graph_reset(NULL);
+
+ g_assert(graph_analysis != NULL);
+ g_assert(graph_analysis_data != NULL);
+
+ g_free(graph_analysis);
+ graph_analysis = NULL;
+
+ g_free(graph_analysis_data);
+ graph_analysis_data = NULL;
+
+ /* Note that we no longer have a "Flow Graph" dialog box. */
+ flow_graph_dlg = NULL;
+}
+
+
+/****************************************************************************/
+static void
+toggle_select_all(GtkWidget *widget _U_, gpointer user_data _U_)
+{
+ /* is the button now active? */
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_all_rb))) {
+ type_of_packets = TYPE_OF_PACKETS_ALL;
+ }
+}
+
+/****************************************************************************/
+static void
+toggle_select_displayed(GtkWidget *widget _U_, gpointer user_data _U_)
+{
+ /* is the button now active? */
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_displayed_rb))) {
+ type_of_packets = TYPE_OF_PACKETS_DISPLAYED;
+ }
+}
+
+/****************************************************************************/
+static void
+toggle_select_general(GtkWidget *widget _U_, gpointer user_data _U_)
+{
+ /* is the button now active? */
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_general_rb))) {
+ type_of_flow = TYPE_OF_FLOW_GENERAL;
+ }
+}
+
+/****************************************************************************/
+static void
+toggle_select_tcp(GtkWidget *widget _U_, gpointer user_data _U_)
+{
+ /* is the button now active? */
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(select_tcp_rb))) {
+ type_of_flow = TYPE_OF_FLOW_TCP;
+ }
+}
+
+/****************************************************************************/
+static void
+toggle_select_srcdst(GtkWidget *widget _U_, gpointer user_data _U_)
+{
+ /* is the button now active? */
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(src_dst_rb))) {
+ node_addr_type = NODE_ADDR_TYPE_SRCDST;
+ }
+}
+
+/****************************************************************************/
+static void
+toggle_select_netsrcdst(GtkWidget *widget _U_, gpointer user_data _U_)
+{
+ /* is the button now active? */
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(net_src_dst_rb))) {
+ node_addr_type = NODE_ADDR_TYPE_NET_SRCDST;
+ }
+}
+
+/****************************************************************************/
+/* Add a new frame into the graph */
+static int
+flow_graph_frame_add_to_graph(packet_info *pinfo)
+{
+ graph_analysis_item_t *gai;
+ int i;
+ gchar *protocol;
+ gchar *colinfo;
+
+ protocol=NULL;
+ colinfo=NULL;
+
+ if (node_addr_type == NODE_ADDR_TYPE_NET_SRCDST) {
+ if (pinfo->net_src.type!=AT_NONE && pinfo->net_dst.type!=AT_NONE) {
+ gai = g_malloc(sizeof(graph_analysis_item_t));
+ COPY_ADDRESS(&(gai->src_addr),&(pinfo->net_src));
+ COPY_ADDRESS(&(gai->dst_addr),&(pinfo->net_dst));
+ }
+ else return 0;
+
+ } else {
+ if (pinfo->src.type!=AT_NONE && pinfo->dst.type!=AT_NONE) {
+ gai = g_malloc(sizeof(graph_analysis_item_t));
+ COPY_ADDRESS(&(gai->src_addr),&(pinfo->src));
+ COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst));
+ }
+ else return 0;
+ }
+
+ gai->fd = pinfo->fd;
+
+ gai->port_src=pinfo->srcport;
+ gai->port_dst=pinfo->destport;
+ gai->comment=NULL;
+ gai->frame_label=NULL;
+
+ /* this code doesn't make sense.
+ g_free(gai->comment);
+ g_free(gai->frame_label);
+ */
+
+ if(pinfo->cinfo) {
+ if (pinfo->cinfo->col_first[COL_INFO]>=0){
+
+ for (i = pinfo->cinfo->col_first[COL_INFO]; i <= pinfo->cinfo->col_last[COL_INFO]; i++) {
+ if (pinfo->cinfo->fmt_matx[i][COL_INFO]) {
+ colinfo = g_strdup(pinfo->cinfo->col_data[i]);
+ /* break; ? or g_free(colinfo); before g_strdup() */
+ }
+ }
+ }
+
+ if (pinfo->cinfo->col_first[COL_PROTOCOL]>=0){
+
+ for (i = pinfo->cinfo->col_first[COL_PROTOCOL]; i <= pinfo->cinfo->col_last[COL_PROTOCOL]; i++) {
+ if (pinfo->cinfo->fmt_matx[i][COL_PROTOCOL]) {
+ protocol = g_strdup(pinfo->cinfo->col_data[i]);
+ /* break; ? or g_free(protocol); before g_strdup() */
+ }
+ }
+ }
+ }
+
+ if (colinfo != NULL) {
+ if (protocol != NULL) {
+ gai->frame_label = g_strdup_printf("%.19s", colinfo);
+ gai->comment = g_strdup_printf("%s: %s", protocol, colinfo);
+ } else {
+ gai->frame_label = g_strdup_printf("%.19s", colinfo);
+ gai->comment = g_strdup_printf("%s", colinfo);
+ }
+ } else {
+ /* This will probably never happen...*/
+ if (protocol != NULL) {
+ gai->frame_label = g_strdup_printf("%.19s", protocol);
+ gai->comment = g_strdup_printf("%s", protocol);
+ }
+ }
+
+ g_free(protocol);
+ g_free(colinfo);
+
+ gai->line_style=1;
+ gai->conv_num=0;
+ gai->display=TRUE;
+
+ graph_analysis->list = g_list_append(graph_analysis->list, gai);
+
+ return 1;
+}
+
+/****************************************************************************/
+/* Add a new tcp frame into the graph */
+static int
+flow_graph_tcp_add_to_graph(packet_info *pinfo, const struct tcpheader *tcph)
+{
+ graph_analysis_item_t *gai;
+ /* copied from packet-tcp */
+ const gchar *fstr[] = {"FIN", "SYN", "RST", "PSH", "ACK", "URG", "ECN", "CWR" };
+ guint i, bpos;
+ gboolean flags_found = FALSE;
+ gchar flags[64];
+
+ gai = g_malloc(sizeof(graph_analysis_item_t));
+ gai->fd = pinfo->fd;
+ if (node_addr_type == NODE_ADDR_TYPE_NET_SRCDST) {
+ COPY_ADDRESS(&(gai->src_addr),&(pinfo->net_src));
+ COPY_ADDRESS(&(gai->dst_addr),&(pinfo->net_dst));
+ } else {
+ COPY_ADDRESS(&(gai->src_addr),&(pinfo->src));
+ COPY_ADDRESS(&(gai->dst_addr),&(pinfo->dst));
+ }
+ gai->port_src=pinfo->srcport;
+ gai->port_dst=pinfo->destport;
+
+ flags[0] = '\0';
+ for (i = 0; i < 8; i++) {
+ bpos = 1 << i;
+ if (tcph->th_flags & bpos) {
+ if (flags_found) {
+ g_strlcat(flags, ", ", sizeof(flags));
+ }
+ g_strlcat(flags, fstr[i], sizeof(flags));
+ flags_found = TRUE;
+ }
+ }
+ if (flags[0] == '\0') {
+ g_snprintf (flags, sizeof(flags), "<None>");
+ }
+
+ if ((tcph->th_have_seglen)&&(tcph->th_seglen!=0)){
+ gai->frame_label = g_strdup_printf("%s - Len: %u",flags, tcph->th_seglen);
+ }
+ else{
+ gai->frame_label = g_strdup(flags);
+ }
+
+ if (tcph->th_flags & TH_ACK)
+ gai->comment = g_strdup_printf("Seq = %u Ack = %u",tcph->th_seq, tcph->th_ack);
+ else
+ gai->comment = g_strdup_printf("Seq = %u",tcph->th_seq);
+
+ gai->line_style=1;
+ gai->conv_num=0;
+ gai->display=TRUE;
+
+ graph_analysis->list = g_list_append(graph_analysis->list, gai);
+
+ return 1;
+}
+
+
+
+/****************************************************************************/
+/* whenever a frame packet is seen by the tap listener */
+static gboolean
+flow_graph_frame_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *dummy _U_)
+{
+ if ((type_of_packets == TYPE_OF_PACKETS_ALL)||(pinfo->fd->flags.passed_dfilter==1)){
+ flow_graph_frame_add_to_graph(pinfo);
+ }
+
+ return TRUE;
+}
+
+/****************************************************************************/
+/* whenever a TCP packet is seen by the tap listener */
+static gboolean
+flow_graph_tcp_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *tcp_info)
+{
+ const struct tcpheader *tcph = tcp_info;
+
+ if ((type_of_packets == TYPE_OF_PACKETS_ALL)||(pinfo->fd->flags.passed_dfilter==1)){
+ flow_graph_tcp_add_to_graph(pinfo,tcph);
+ }
+
+ return TRUE;
+}
+
+
+static void
+flow_graph_packet_draw(void *prs _U_)
+{
+ return;
+}
+
+/****************************************************************************/
+static void
+flow_graph_on_ok (GtkButton *button _U_,
+ gpointer user_data)
+{
+ if ((have_frame_tap_listener==TRUE)
+ ||(have_tcp_tap_listener==TRUE))
+ {
+ /* remove_tap_listeners */
+ remove_tap_listener_flow_graph();
+ }
+
+ /* Scan for displayed packets (retap all packets) */
+
+ if (type_of_flow == TYPE_OF_FLOW_GENERAL){
+ /* Register the tap listener */
+
+ if(have_frame_tap_listener==FALSE)
+ {
+ /* don't register tap listener, if we have it already */
+ register_tap_listener("frame", &tap_identifier, NULL,
+ TL_REQUIRES_COLUMNS,
+ flow_graph_reset,
+ flow_graph_frame_packet,
+ flow_graph_packet_draw
+ );
+ have_frame_tap_listener=TRUE;
+ }
+
+ cf_retap_packets(&cfile);
+ }
+ else if (type_of_flow == TYPE_OF_FLOW_TCP){
+ /* Register the tap listener */
+
+ if(have_tcp_tap_listener==FALSE)
+ {
+ /* don't register tap listener, if we have it already */
+ register_tap_listener("tcp", &tap_identifier, NULL,
+ 0,
+ flow_graph_reset,
+ flow_graph_tcp_packet,
+ flow_graph_packet_draw
+ );
+ have_tcp_tap_listener=TRUE;
+ }
+
+ cf_retap_packets(&cfile);
+ }
+
+ if (graph_analysis_data->dlg.window != NULL){ /* if we still have a window */
+ graph_analysis_update(graph_analysis_data); /* refresh it xxx */
+ }
+ else{
+ graph_analysis_data->dlg.parent_w = user_data;
+ graph_analysis_create(graph_analysis_data);
+ }
+}
+
+
+/****************************************************************************/
+/* INTERFACE */
+/****************************************************************************/
+
+static void
+flow_graph_dlg_create (void)
+{
+ GtkWidget *flow_graph_dlg_w;
+ GtkWidget *main_vb;
+ GtkWidget *hbuttonbox;
+ GtkWidget *bt_cancel, *bt_ok;
+#if 0
+ GtkWidget *top_label = NULL;
+#endif
+ GtkWidget *flow_type_fr, *range_fr, *range_tb, *flow_type_tb, *node_addr_fr, *node_addr_tb;
+
+ flow_graph_dlg_w = dlg_window_new("Wireshark: Flow Graph"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(flow_graph_dlg_w), TRUE);
+
+ gtk_window_set_default_size(GTK_WINDOW(flow_graph_dlg_w), 250, 150);
+
+ main_vb = gtk_vbox_new (FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(flow_graph_dlg_w), main_vb);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vb), 7);
+
+#if 0
+ top_label = gtk_label_new ("Choose packets to include in the graph");
+ gtk_box_pack_start (GTK_BOX (main_vb), top_label, FALSE, FALSE, 8);
+#endif
+
+ gtk_widget_show(flow_graph_dlg_w);
+
+ /*** Packet range frame ***/
+ range_fr = gtk_frame_new("Choose packets");
+ gtk_box_pack_start(GTK_BOX(main_vb), range_fr, FALSE, FALSE, 5);
+
+ range_tb = gtk_table_new(4, 4, FALSE);
+ gtk_container_set_border_width(GTK_CONTAINER(range_tb), 5);
+ gtk_container_add(GTK_CONTAINER(range_fr), range_tb);
+
+ /* Process all packets */
+ select_all_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "_All packets");
+ gtk_widget_set_tooltip_text (select_all_rb, ("Process all packets"));
+ g_signal_connect(select_all_rb, "toggled", G_CALLBACK(toggle_select_all), NULL);
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), select_all_rb, 0, 1, 0, 1);
+ if (type_of_packets == TYPE_OF_PACKETS_ALL) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_all_rb),TRUE);
+ }
+ gtk_widget_show(select_all_rb);
+
+ /* Process displayed packets */
+ select_displayed_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(select_all_rb),
+ "_Displayed packets");
+ gtk_widget_set_tooltip_text (select_displayed_rb, ("Process displayed packets"));
+ g_signal_connect(select_displayed_rb, "toggled", G_CALLBACK(toggle_select_displayed), NULL);
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), select_displayed_rb, 0, 1, 1, 2);
+ if (type_of_packets == TYPE_OF_PACKETS_DISPLAYED) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_displayed_rb),TRUE);
+ }
+ gtk_widget_show(select_displayed_rb);
+
+ gtk_widget_show(range_tb);
+ gtk_widget_show(range_fr);
+
+ /*** Flow type frame ***/
+ flow_type_fr = gtk_frame_new("Choose flow type");
+ gtk_box_pack_start(GTK_BOX(main_vb), flow_type_fr, FALSE, FALSE, 5);
+
+ flow_type_tb = gtk_table_new(4, 4, FALSE);
+ gtk_container_set_border_width(GTK_CONTAINER(flow_type_tb), 5);
+ gtk_container_add(GTK_CONTAINER(flow_type_fr), flow_type_tb);
+
+ /* General information */
+ select_general_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "_General flow");
+ gtk_widget_set_tooltip_text (select_general_rb, ("Show all packets, with general information"));
+ g_signal_connect(select_general_rb, "toggled", G_CALLBACK(toggle_select_general), NULL);
+ gtk_table_attach_defaults(GTK_TABLE(flow_type_tb), select_general_rb, 0, 1, 0, 1);
+ if (type_of_flow == TYPE_OF_FLOW_GENERAL) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_general_rb),TRUE);
+ }
+ gtk_widget_show(select_general_rb);
+
+ /* TCP specific information */
+ select_tcp_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(select_general_rb),
+ "_TCP flow");
+ gtk_widget_set_tooltip_text (select_tcp_rb, ("Show only TCP packets, with TCP specific information"));
+ g_signal_connect(select_tcp_rb, "toggled", G_CALLBACK(toggle_select_tcp), NULL);
+ gtk_table_attach_defaults(GTK_TABLE(flow_type_tb), select_tcp_rb, 0, 1, 1, 2);
+ if (type_of_flow == TYPE_OF_FLOW_TCP) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_tcp_rb),TRUE);
+ }
+ gtk_widget_show(select_tcp_rb);
+
+ gtk_widget_show(flow_type_tb);
+ gtk_widget_show(flow_type_fr);
+
+ /*** Node address type frame ***/
+ node_addr_fr = gtk_frame_new("Choose node address type");
+ gtk_box_pack_start(GTK_BOX(main_vb), node_addr_fr, FALSE, FALSE, 5);
+
+ node_addr_tb = gtk_table_new(4, 4, FALSE);
+ gtk_container_set_border_width(GTK_CONTAINER(node_addr_tb), 5);
+ gtk_container_add(GTK_CONTAINER(node_addr_fr), node_addr_tb);
+
+ /* Source / Dest address */
+ src_dst_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "_Standard source/destination addresses");
+ gtk_widget_set_tooltip_text (src_dst_rb,
+ ("Nodes in the diagram are identified with source and destination addresses"));
+ g_signal_connect(src_dst_rb, "toggled", G_CALLBACK(toggle_select_srcdst), NULL);
+ gtk_table_attach_defaults(GTK_TABLE(node_addr_tb), src_dst_rb, 0, 1, 0, 1);
+ if (node_addr_type == NODE_ADDR_TYPE_SRCDST) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(src_dst_rb),TRUE);
+ }
+ gtk_widget_show(src_dst_rb);
+
+ /* Network source / dest address */
+ net_src_dst_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(src_dst_rb),
+ "_Network source/destination addresses");
+ gtk_widget_set_tooltip_text (net_src_dst_rb,
+ ("Nodes in the diagram are identified with network source and destination addresses"));
+ g_signal_connect(net_src_dst_rb, "toggled", G_CALLBACK(toggle_select_netsrcdst), NULL);
+ gtk_table_attach_defaults(GTK_TABLE(node_addr_tb), net_src_dst_rb, 0, 1, 1, 2);
+ if (node_addr_type == NODE_ADDR_TYPE_NET_SRCDST) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(net_src_dst_rb),TRUE);
+ }
+ gtk_widget_show(net_src_dst_rb);
+
+ gtk_widget_show(node_addr_tb);
+ gtk_widget_show(node_addr_fr);
+
+ /* button row */
+ hbuttonbox = gtk_hbutton_box_new ();
+ gtk_box_pack_start (GTK_BOX (main_vb), hbuttonbox, FALSE, FALSE, 5);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_SPREAD);
+ gtk_box_set_spacing (GTK_BOX (hbuttonbox), 30);
+
+ bt_ok = gtk_button_new_from_stock(GTK_STOCK_OK);
+ gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_ok);
+ gtk_widget_set_tooltip_text (bt_ok, "Show the flow graph");
+ g_signal_connect(bt_ok, "clicked", G_CALLBACK(flow_graph_on_ok), flow_graph_dlg_w);
+ gtk_widget_show(bt_ok);
+
+ bt_cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_cancel);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(bt_cancel, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(bt_cancel, GTK_CAN_DEFAULT);
+#endif
+ gtk_widget_set_tooltip_text (bt_cancel, "Cancel this dialog");
+ window_set_cancel_button(flow_graph_dlg_w, bt_cancel, window_cancel_button_cb);
+
+ g_signal_connect(flow_graph_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(flow_graph_dlg_w, "destroy", G_CALLBACK(flow_graph_on_destroy), NULL);
+
+ gtk_widget_show_all(flow_graph_dlg_w);
+ window_present(flow_graph_dlg_w);
+
+ flow_graph_dlg = flow_graph_dlg_w;
+}
+
+/****************************************************************************/
+/* PUBLIC */
+/****************************************************************************/
+
+/* init function for tap */
+static void
+flow_graph_init_tap(const char *dummy _U_, void* userdata _U_)
+{
+ /* The storage allocated by flow_graph_data_init() and graph_analysis_init() */
+ /* will be considered to be "associated with" the flow_graph_dlg dialog box. */
+ /* It will be freed when the flow_graph_dlg dialog box is destroyed. */
+ if (flow_graph_dlg != NULL) {
+ g_assert(graph_analysis != NULL);
+ g_assert(graph_analysis_data != NULL);
+ /* There's already a dialog box; reactivate it. */
+ reactivate_window(flow_graph_dlg);
+ } else {
+ g_assert(graph_analysis == NULL);
+ g_assert(graph_analysis_data == NULL);
+
+ /* initialize graph items store */
+ flow_graph_data_init();
+
+ /* init the Graph Analysis */
+ graph_analysis_data = graph_analysis_init();
+ graph_analysis_data->graph_info = graph_analysis;
+
+ flow_graph_dlg_create();
+ }
+}
+
+
+/****************************************************************************/
+/* entry point when called via the GTK menu */
+void
+flow_graph_launch(GtkAction *action _U_, gpointer user_data _U_)
+{
+ flow_graph_init_tap("",NULL);
+}
+
+/****************************************************************************/
+void
+register_tap_listener_flow_graph(void)
+{
+ register_stat_cmd_arg("flow_graph",flow_graph_init_tap,NULL);
+}
+
diff --git a/ui/gtk/follow_ssl.c b/ui/gtk/follow_ssl.c
new file mode 100644
index 0000000000..cee99305db
--- /dev/null
+++ b/ui/gtk/follow_ssl.c
@@ -0,0 +1,348 @@
+/* follow_ssl.c
+ * SSL specific routines for following traffic streams
+ *
+ * $Id$
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <ctype.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/follow.h>
+#include <epan/dissectors/packet-ipv6.h>
+#include <epan/prefs.h>
+#include <epan/addr_resolv.h>
+#include <epan/epan_dissect.h>
+#include <epan/filesystem.h>
+#include <epan/tap.h>
+
+#include <../alert_box.h>
+#include <../simple_dialog.h>
+#include <../util.h>
+
+#include "gtkglobals.h"
+#include "ui/gtk/color_utils.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/file_dlg.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/font_utils.h"
+#include "ui/gtk/follow_ssl.h"
+#include "ui/gtk/follow_stream.h"
+#include "ui/gtk/utf8_entities.h"
+
+#ifdef SSL_PLUGIN
+#include "packet-ssl-utils.h"
+#else
+#include <epan/dissectors/packet-ssl-utils.h>
+#endif
+
+
+typedef struct {
+ gboolean is_server;
+ StringInfo data;
+} SslDecryptedRecord;
+
+static int
+ssl_queue_packet_data(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *ssl)
+{
+ follow_info_t* follow_info = tapdata;
+ SslDecryptedRecord* rec;
+ SslDataInfo* appl_data;
+ gint total_len;
+ guchar *p;
+ int proto_ssl = (long) ssl;
+ SslPacketInfo* pi = p_get_proto_data(pinfo->fd, proto_ssl);
+
+ /* skip packet without decrypted data payload*/
+ if (!pi || !pi->appl_data)
+ return 0;
+
+ /* compute total length */
+ total_len = 0;
+ appl_data = pi->appl_data;
+ do {
+ total_len += appl_data->plain_data.data_len;
+ appl_data = appl_data->next;
+ } while (appl_data);
+
+ /* compute packet direction */
+ rec = g_malloc(sizeof(SslDecryptedRecord) + total_len);
+
+ if (follow_info->client_port == 0) {
+ follow_info->client_port = pinfo->srcport;
+ COPY_ADDRESS(&follow_info->client_ip, &pinfo->src);
+ }
+ if (ADDRESSES_EQUAL(&follow_info->client_ip, &pinfo->src) &&
+ follow_info->client_port == pinfo->srcport)
+ rec->is_server = 0;
+ else
+ rec->is_server = 1;
+
+ /* update stream counter */
+ follow_info->bytes_written[rec->is_server] += total_len;
+
+ /* extract decrypted data and queue it locally */
+ rec->data.data = (guchar*)(rec + 1);
+ rec->data.data_len = total_len;
+ appl_data = pi->appl_data;
+ p = rec->data.data;
+ do {
+ memcpy(p, appl_data->plain_data.data, appl_data->plain_data.data_len);
+ p += appl_data->plain_data.data_len;
+ appl_data = appl_data->next;
+ } while (appl_data);
+ follow_info->payload = g_list_append(
+ follow_info->payload,rec);
+
+ return 0;
+}
+
+extern gboolean
+packet_is_ssl(epan_dissect_t* edt);
+
+
+/* Follow the SSL stream, if any, to which the last packet that we called
+ a dissection routine on belongs (this might be the most recently
+ selected packet, or it might be the last packet in the file). */
+void
+follow_ssl_stream_cb(GtkWidget * w _U_, gpointer data _U_)
+{
+ GtkWidget *filter_te, *filter_cm;
+ gchar *follow_filter;
+ const gchar *previous_filter;
+ int filter_out_filter_len, previous_filter_len;
+ const char *hostname0, *hostname1;
+ char *port0, *port1;
+ gchar *server_to_client_string = NULL;
+ gchar *client_to_server_string = NULL;
+ gchar *both_directions_string = NULL;
+ follow_stats_t stats;
+ follow_info_t *follow_info;
+ GString* msg;
+
+ /* we got ssl so we can follow */
+ if (!packet_is_ssl(cfile.edt)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Error following stream. Please make\n"
+ "sure you have an SSL packet selected.");
+ return;
+ }
+
+ follow_info = g_new0(follow_info_t, 1);
+ follow_info->follow_type = FOLLOW_SSL;
+
+ /* Create a new filter that matches all packets in the SSL stream,
+ and set the display filter entry accordingly */
+ reset_tcp_reassembly();
+ follow_filter = build_follow_filter(&cfile.edt->pi);
+ if (!follow_filter)
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Error creating filter for this stream.\n"
+ "A network layer header is needed");
+ g_free(follow_info);
+ return;
+ }
+
+ /* Set the display filter entry accordingly */
+ filter_cm = g_object_get_data(G_OBJECT(top_level), E_DFILTER_CM_KEY);
+ filter_te = gtk_bin_get_child(GTK_BIN(filter_cm));
+
+ /* needed in follow_filter_out_stream(), is there a better way? */
+ follow_info->filter_te = filter_te;
+
+ /* save previous filter, const since we're not supposed to alter */
+ previous_filter =
+ (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
+
+ /* allocate our new filter. API claims g_malloc terminates program on failure */
+ /* my calc for max alloc needed is really +10 but when did a few extra bytes hurt ? */
+ previous_filter_len = previous_filter?(int)strlen(previous_filter):0;
+ filter_out_filter_len = (int)strlen(follow_filter) + previous_filter_len + 16;
+ follow_info->filter_out_filter = (gchar *)g_malloc(filter_out_filter_len);
+
+ /* append the negation */
+ if(previous_filter_len) {
+ g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
+ "%s and !(%s)", previous_filter, follow_filter);
+ } else {
+ g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
+ "!(%s)", follow_filter);
+ }
+
+ /* data will be passed via tap callback*/
+ msg = register_tap_listener("ssl", follow_info, follow_filter, 0,
+ NULL, ssl_queue_packet_data, NULL);
+ if (msg)
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't register ssl tap: %s\n",msg->str);
+ g_free(follow_info->filter_out_filter);
+ g_free(follow_info);
+ g_free(follow_filter);
+ return;
+ }
+ gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter);
+
+ /* Run the display filter so it goes in effect - even if it's the
+ same as the previous display filter. */
+ main_filter_packets(&cfile, follow_filter, TRUE);
+
+ /* Free the filter string, as we're done with it. */
+ g_free(follow_filter);
+
+ remove_tap_listener(follow_info);
+
+ /* Stream to show */
+ follow_stats(&stats);
+
+ if (stats.is_ipv6) {
+ struct e_in6_addr ipaddr;
+ memcpy(&ipaddr, stats.ip_address[0], 16);
+ hostname0 = get_hostname6(&ipaddr);
+ memcpy(&ipaddr, stats.ip_address[0], 16);
+ hostname1 = get_hostname6(&ipaddr);
+ } else {
+ guint32 ipaddr;
+ memcpy(&ipaddr, stats.ip_address[0], 4);
+ hostname0 = get_hostname(ipaddr);
+ memcpy(&ipaddr, stats.ip_address[1], 4);
+ hostname1 = get_hostname(ipaddr);
+ }
+
+ port0 = get_tcp_port(stats.port[0]);
+ port1 = get_tcp_port(stats.port[1]);
+
+ follow_info->is_ipv6 = stats.is_ipv6;
+
+ /* Both Stream Directions */
+ both_directions_string = g_strdup_printf("Entire conversation (%u bytes)", follow_info->bytes_written[0] + follow_info->bytes_written[1]);
+
+ if(follow_info->client_port == stats.port[0]) {
+ server_to_client_string =
+ g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
+ hostname0, port0,
+ hostname1, port1,
+ follow_info->bytes_written[0]);
+
+ client_to_server_string =
+ g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
+ hostname1, port1,
+ hostname0, port0,
+ follow_info->bytes_written[1]);
+ } else {
+ server_to_client_string =
+ g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
+ hostname1, port1,
+ hostname0, port0,
+ follow_info->bytes_written[0]);
+
+ client_to_server_string =
+ g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
+ hostname0, port0,
+ hostname1, port1,
+ follow_info->bytes_written[1]);
+ }
+
+ follow_stream("Follow SSL Stream", follow_info, both_directions_string,
+ server_to_client_string, client_to_server_string);
+
+ g_free(both_directions_string);
+ g_free(server_to_client_string);
+ g_free(client_to_server_string);
+}
+
+#define FLT_BUF_SIZE 1024
+
+/*
+ * XXX - the routine pointed to by "print_line_fcn_p" doesn't get handed lines,
+ * it gets handed bufferfuls. That's fine for "follow_write_raw()"
+ * and "follow_add_to_gtk_text()", but, as "follow_print_text()" calls
+ * the "print_line()" routine from "print.c", and as that routine might
+ * genuinely expect to be handed a line (if, for example, it's using
+ * some OS or desktop environment's printing API, and that API expects
+ * to be handed lines), "follow_print_text()" should probably accumulate
+ * lines in a buffer and hand them "print_line()". (If there's a
+ * complete line in a buffer - i.e., there's nothing of the line in
+ * the previous buffer or the next buffer - it can just hand that to
+ * "print_line()" after filtering out non-printables, as an
+ * optimization.)
+ *
+ * This might or might not be the reason why C arrays display
+ * correctly but get extra blank lines very other line when printed.
+ */
+frs_return_t
+follow_read_ssl_stream(follow_info_t *follow_info,
+ gboolean (*print_line_fcn_p)(char *, size_t, gboolean, void *),
+ void *arg)
+{
+ guint32 global_client_pos = 0, global_server_pos = 0;
+ guint32 server_packet_count = 0;
+ guint32 client_packet_count = 0;
+ guint32 *global_pos;
+ gboolean skip;
+ GList* cur;
+ frs_return_t frs_return;
+
+ for (cur = follow_info->payload; cur; cur = g_list_next(cur)) {
+ SslDecryptedRecord* rec = cur->data;
+ skip = FALSE;
+ if (!rec->is_server) {
+ global_pos = &global_client_pos;
+ if (follow_info->show_stream == FROM_SERVER) {
+ skip = TRUE;
+ }
+ } else {
+ global_pos = &global_server_pos;
+ if (follow_info->show_stream == FROM_CLIENT) {
+ skip = TRUE;
+ }
+ }
+
+ if (!skip) {
+ size_t nchars = rec->data.data_len;
+ gchar *buffer = g_memdup(rec->data.data, (guint) nchars);
+
+ frs_return = follow_show(follow_info, print_line_fcn_p, buffer, nchars,
+ rec->is_server, arg, global_pos,
+ &server_packet_count, &client_packet_count);
+ g_free(buffer);
+ if(frs_return == FRS_PRINT_ERROR)
+ return frs_return;
+ }
+ }
+
+ return FRS_OK;
+}
diff --git a/ui/gtk/follow_ssl.h b/ui/gtk/follow_ssl.h
new file mode 100644
index 0000000000..4802f30168
--- /dev/null
+++ b/ui/gtk/follow_ssl.h
@@ -0,0 +1,34 @@
+/* follow_ssl.h
+ * SSL specific routines for following traffic streams
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __FOLLOW_SSL_H__
+#define __FOLLOW_SSL_H__
+
+/* Follow the SSL stream, if any, to which the last packet that we called
+ a dissection routine on belongs (this might be the most recently
+ selected packet, or it might be the last packet in the file). */
+void follow_ssl_stream_cb(GtkWidget * w, gpointer data _U_);
+
+#endif /* __FOLLOW_SSL_H__ */
+
diff --git a/ui/gtk/follow_stream.c b/ui/gtk/follow_stream.c
new file mode 100644
index 0000000000..6e08ae280c
--- /dev/null
+++ b/ui/gtk/follow_stream.c
@@ -0,0 +1,1109 @@
+/* follow_stream.c
+ * Common routines for following data streams
+ *
+ * $Id$
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <ctype.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/addr_resolv.h>
+#include <epan/follow.h>
+#include <epan/filesystem.h>
+#include <epan/prefs.h>
+#include <epan/charsets.h>
+
+#include <../alert_box.h>
+#include <../isprint.h>
+#include <../print.h>
+#include <../simple_dialog.h>
+#include <wsutil/file_util.h>
+
+#include "ui/gtk/color_utils.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/follow_stream.h"
+#include "ui/gtk/font_utils.h"
+#include "ui/gtk/file_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/old-gtk-compat.h"
+
+#ifdef _WIN32
+#include "../tempfile.h"
+#include "win32/print_win32.h"
+#endif
+
+/* static variable declarations to speed up the performance
+ * of follow_load_text and follow_add_to_gtk_text
+ */
+static GdkColor server_fg, server_bg;
+static GdkColor client_fg, client_bg;
+static GtkTextTag *server_tag, *client_tag;
+
+static void follow_find_destroy_cb(GtkWidget * win _U_, gpointer data);
+static void follow_find_button_cb(GtkWidget * w, gpointer data);
+static gboolean follow_save_as_ok_cb(GtkWidget * w _U_, gpointer fs);
+static void follow_destroy_cb(GtkWidget *w, gpointer data _U_);
+static void follow_save_as_destroy_cb(GtkWidget * win _U_, gpointer data);
+
+GList *follow_infos = NULL;
+
+static frs_return_t
+follow_read_stream(follow_info_t *follow_info,
+ gboolean (*print_line_fcn_p)(char *, size_t, gboolean, void *),
+ void *arg)
+{
+ switch(follow_info->follow_type) {
+
+ case FOLLOW_TCP :
+ return follow_read_tcp_stream(follow_info, print_line_fcn_p, arg);
+
+ case FOLLOW_UDP :
+ return follow_read_udp_stream(follow_info, print_line_fcn_p, arg);
+
+ case FOLLOW_SSL :
+ return follow_read_ssl_stream(follow_info, print_line_fcn_p, arg);
+
+ default :
+ g_assert_not_reached();
+ return 0;
+ }
+}
+
+gboolean
+follow_add_to_gtk_text(char *buffer, size_t nchars, gboolean is_server,
+ void *arg)
+{
+ GtkWidget *text = arg;
+ GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
+ GtkTextIter iter;
+
+ /* While our isprint() hack is in place, we
+ * have to convert some chars to '.' in order
+ * to be able to see the data we *should* see
+ * in the GtkText widget.
+ */
+ size_t i;
+
+ for (i = 0; i < nchars; i++) {
+ if (buffer[i] == '\n' || buffer[i] == '\r')
+ continue;
+ if (! isprint((guchar)buffer[i])) {
+ buffer[i] = '.';
+ }
+ }
+
+ gtk_text_buffer_get_end_iter(buf, &iter);
+ if (is_server) {
+ gtk_text_buffer_insert_with_tags(buf, &iter, buffer, (gint) nchars,
+ server_tag, NULL);
+ } else {
+ gtk_text_buffer_insert_with_tags(buf, &iter, buffer, (gint) nchars,
+ client_tag, NULL);
+ }
+ return TRUE;
+}
+
+/*
+ * XXX - for text printing, we probably want to wrap lines at 80 characters;
+ * (PostScript printing is doing this already), and perhaps put some kind of
+ * dingbat (to use the technical term) to indicate a wrapped line, along the
+ * lines of what's done when displaying this in a window, as per Warren Young's
+ * suggestion.
+ */
+static gboolean
+follow_print_text(char *buffer, size_t nchars, gboolean is_server _U_,
+ void *arg)
+{
+ print_stream_t *stream = arg;
+ size_t i;
+ char *str;
+
+ /* convert non printable characters */
+ for (i = 0; i < nchars; i++) {
+ if (buffer[i] == '\n' || buffer[i] == '\r')
+ continue;
+ if (! isprint((guchar)buffer[i])) {
+ buffer[i] = '.';
+ }
+ }
+
+ /* convert unterminated char array to a zero terminated string */
+ str = g_malloc(nchars + 1);
+ memcpy(str, buffer, nchars);
+ str[nchars] = 0;
+ print_line(stream, /*indent*/ 0, str);
+ g_free(str);
+
+ return TRUE;
+}
+
+static gboolean
+follow_write_raw(char *buffer, size_t nchars, gboolean is_server _U_, void *arg)
+{
+ FILE *fh = arg;
+ size_t nwritten;
+
+ nwritten = fwrite(buffer, 1, nchars, fh);
+ if (nwritten != nchars)
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Handles the display style toggling */
+static void
+follow_charset_toggle_cb(GtkWidget * w _U_, gpointer data)
+{
+ follow_info_t *follow_info = data;
+
+ /*
+ * A radio button toggles when it goes on and when it goes
+ * off, so when you click a radio button two signals are
+ * delivered. We only want to reprocess the display once,
+ * so we do it only when the button goes on.
+ */
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) {
+ if (w == follow_info->ebcdic_bt)
+ follow_info->show_type = SHOW_EBCDIC;
+ else if (w == follow_info->hexdump_bt)
+ follow_info->show_type = SHOW_HEXDUMP;
+ else if (w == follow_info->carray_bt)
+ follow_info->show_type = SHOW_CARRAY;
+ else if (w == follow_info->ascii_bt)
+ follow_info->show_type = SHOW_ASCII;
+ else if (w == follow_info->raw_bt)
+ follow_info->show_type = SHOW_RAW;
+ follow_load_text(follow_info);
+ }
+}
+
+void
+follow_load_text(follow_info_t *follow_info)
+{
+ GtkTextBuffer *buf;
+
+ buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
+
+ /* prepare colors one time for repeated use by follow_add_to_gtk_text */
+ color_t_to_gdkcolor(&server_fg, &prefs.st_server_fg);
+ color_t_to_gdkcolor(&server_bg, &prefs.st_server_bg);
+ color_t_to_gdkcolor(&client_fg, &prefs.st_client_fg);
+ color_t_to_gdkcolor(&client_bg, &prefs.st_client_bg);
+
+ /* prepare tags one time for repeated use by follow_add_to_gtk_text */
+ server_tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk",
+ &server_fg, "background-gdk",
+ &server_bg, "font-desc",
+ user_font_get_regular(), NULL);
+ client_tag = gtk_text_buffer_create_tag(buf, NULL, "foreground-gdk",
+ &client_fg, "background-gdk",
+ &client_bg, "font-desc",
+ user_font_get_regular(), NULL);
+
+ /* Delete any info already in text box */
+ gtk_text_buffer_set_text(buf, "", -1);
+
+ follow_read_stream(follow_info, follow_add_to_gtk_text,
+ follow_info->text);
+}
+
+void
+follow_filter_out_stream(GtkWidget * w _U_, gpointer data)
+{
+ follow_info_t *follow_info = data;
+
+ /* Lock out user from messing with us. (ie. don't free our data!) */
+ gtk_widget_set_sensitive(follow_info->streamwindow, FALSE);
+
+ /* Set the display filter. */
+ gtk_entry_set_text(GTK_ENTRY(follow_info->filter_te),
+ follow_info->filter_out_filter);
+
+ /* Run the display filter so it goes in effect. */
+ main_filter_packets(&cfile, follow_info->filter_out_filter, FALSE);
+
+ /* we force a subsequent close */
+ window_destroy(follow_info->streamwindow);
+
+ return;
+}
+
+static void
+follow_find_cb(GtkWidget * w _U_, gpointer data)
+{
+ follow_info_t *follow_info = data;
+ GtkWidget *find_dlg_w, *main_vb, *buttons_row, *find_lb;
+ GtkWidget *find_hb, *find_text_box, *find_bt, *cancel_bt;
+
+ if (follow_info->find_dlg_w != NULL) {
+ /* There's already a dialog box; reactivate it. */
+ reactivate_window(follow_info->find_dlg_w);
+ return;
+ }
+
+ /* Create the find box */
+ find_dlg_w = dlg_window_new("Wireshark: Find text");
+ gtk_window_set_transient_for(GTK_WINDOW(find_dlg_w),
+ GTK_WINDOW(follow_info->streamwindow));
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(find_dlg_w), TRUE);
+ follow_info->find_dlg_w = find_dlg_w;
+
+ g_signal_connect(find_dlg_w, "destroy", G_CALLBACK(follow_find_destroy_cb),
+ follow_info);
+ g_signal_connect(find_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb),
+ NULL);
+
+ /* Main vertical box */
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(find_dlg_w), main_vb);
+
+ /* Horizontal box for find label, entry field and up/down radio
+ buttons */
+ find_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(main_vb), find_hb);
+ gtk_widget_show(find_hb);
+
+ /* Find label */
+ find_lb = gtk_label_new("Find text:");
+ gtk_box_pack_start(GTK_BOX(find_hb), find_lb, FALSE, FALSE, 0);
+ gtk_widget_show(find_lb);
+
+ /* Find field */
+ find_text_box = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(find_hb), find_text_box, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text(find_text_box, "Text to search for (case sensitive)");
+ gtk_widget_show(find_text_box);
+
+ /* Buttons row */
+ buttons_row = dlg_button_row_new(GTK_STOCK_FIND, GTK_STOCK_CANCEL,
+ NULL);
+ gtk_container_add(GTK_CONTAINER(main_vb), buttons_row);
+ find_bt = g_object_get_data(G_OBJECT(buttons_row), GTK_STOCK_FIND);
+ cancel_bt = g_object_get_data(G_OBJECT(buttons_row), GTK_STOCK_CANCEL);
+
+ g_signal_connect(find_bt, "clicked", G_CALLBACK(follow_find_button_cb), follow_info);
+ g_object_set_data(G_OBJECT(find_bt), "find_string", find_text_box);
+ window_set_cancel_button(find_dlg_w, cancel_bt,
+ window_cancel_button_cb);
+
+ /* Hitting return in the find field "clicks" the find button */
+ dlg_set_activate(find_text_box, find_bt);
+
+ /* Show the dialog */
+ gtk_widget_show_all(find_dlg_w);
+ window_present(find_dlg_w);
+}
+
+static void
+follow_find_button_cb(GtkWidget * w, gpointer data)
+{
+ gboolean found;
+ const gchar *find_string;
+ follow_info_t *follow_info = data;
+ GtkTextBuffer *buffer;
+ GtkTextIter iter, match_start, match_end;
+ GtkTextMark *last_pos_mark;
+ GtkWidget *find_string_w;
+
+ /* Get the text the user typed into the find field */
+ find_string_w = (GtkWidget *)g_object_get_data(G_OBJECT(w), "find_string");
+ find_string = gtk_entry_get_text(GTK_ENTRY(find_string_w));
+
+ /* Get the buffer associated with the follow stream */
+ buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(follow_info->text));
+ gtk_text_buffer_get_start_iter(buffer, &iter);
+
+ /* Look for the search string in the buffer */
+ last_pos_mark = gtk_text_buffer_get_mark(buffer, "last_position");
+ if(last_pos_mark)
+ gtk_text_buffer_get_iter_at_mark(buffer, &iter, last_pos_mark);
+
+ found = gtk_text_iter_forward_search(&iter, find_string, 0,
+ &match_start,
+ &match_end,
+ NULL);
+
+ if(found) {
+ gtk_text_buffer_select_range(buffer, &match_start, &match_end);
+ last_pos_mark = gtk_text_buffer_create_mark (buffer,
+ "last_position",
+ &match_end, FALSE);
+ gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(follow_info->text), last_pos_mark);
+ } else {
+ /* We didn't find a match */
+ simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK,
+ "%sFind text has reached the end of the followed "
+ "stream%s\n\nThe next search will start from the "
+ "beginning", simple_dialog_primary_start(),
+ simple_dialog_primary_end());
+ if(last_pos_mark)
+ gtk_text_buffer_delete_mark(buffer, last_pos_mark);
+ }
+
+}
+
+static void
+follow_find_destroy_cb(GtkWidget * win _U_, gpointer data)
+{
+ follow_info_t *follow_info = data;
+
+ /* Note that we no longer have a dialog box. */
+ follow_info->find_dlg_w = NULL;
+}
+
+static void
+follow_print_stream(GtkWidget * w _U_, gpointer data)
+{
+ print_stream_t *stream;
+ gboolean to_file;
+ char *print_dest;
+ follow_info_t *follow_info = data;
+#ifdef _WIN32
+ gboolean win_printer = FALSE;
+ int tmp_fd;
+ char *tmp_namebuf;
+#endif
+
+ switch (prefs.pr_dest) {
+ case PR_DEST_CMD:
+#ifdef _WIN32
+ win_printer = TRUE;
+ /* (The code for creating a temp filename is adapted from print_dlg.c). */
+ /* We currently don't have a function in util.h to create just a tempfile */
+ /* name, so simply create a tempfile using the "official" function, */
+ /* then delete this file again. After this, the name MUST be available. */
+ /* */
+ /* Don't use tmpnam() or such, as this will fail under some ACL */
+ /* circumstances: http://bugs.wireshark.org/bugzilla/show_bug.cgi?id=358 */
+ /* Also: tmpnam is "insecure" and should not be used. */
+ tmp_fd = create_tempfile(&tmp_namebuf, "wshprint");
+ if(tmp_fd == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Couldn't create temporary file for printing:\n%s", tmp_namebuf);
+ return;
+ }
+ ws_close(tmp_fd);
+ ws_unlink(tmp_namebuf);
+ print_dest = tmp_namebuf;
+ to_file = TRUE;
+#else
+ print_dest = prefs.pr_cmd;
+ to_file = FALSE;
+#endif
+ break;
+ case PR_DEST_FILE:
+ print_dest = prefs.pr_file;
+ to_file = TRUE;
+ break;
+ default: /* "Can't happen" */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Couldn't figure out where to send the print "
+ "job. Check your preferences.");
+ return;
+ }
+
+ switch (prefs.pr_format) {
+
+ case PR_FMT_TEXT:
+ stream = print_stream_text_new(to_file, print_dest);
+ break;
+
+ case PR_FMT_PS:
+ stream = print_stream_ps_new(to_file, print_dest);
+ break;
+
+ default:
+ g_assert_not_reached();
+ stream = NULL;
+ }
+ if (stream == NULL) {
+ if (to_file) {
+ open_failure_alert_box(print_dest, errno, TRUE);
+ } else {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Couldn't run print command %s.",
+ prefs.pr_cmd);
+ }
+ return;
+ }
+
+ if (!print_preamble(stream, cfile.filename))
+ goto print_error;
+
+ switch (follow_read_stream(follow_info, follow_print_text, stream)) {
+ case FRS_OK:
+ break;
+ case FRS_OPEN_ERROR:
+ case FRS_READ_ERROR:
+ /* XXX - cancel printing? */
+ destroy_print_stream(stream);
+ return;
+ case FRS_PRINT_ERROR:
+ goto print_error;
+ }
+
+ if (!print_finale(stream))
+ goto print_error;
+
+ if (!destroy_print_stream(stream)) {
+ if (to_file) {
+ write_failure_alert_box(print_dest, errno);
+ } else {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Error closing print destination.");
+ }
+ }
+#ifdef _WIN32
+ if (win_printer) {
+ print_mswin(print_dest);
+
+ /* trash temp file */
+ ws_remove(print_dest);
+ }
+#endif
+ return;
+
+ print_error:
+ if (to_file) {
+ write_failure_alert_box(print_dest, errno);
+ } else {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Error writing to print command: %s",
+ g_strerror(errno));
+ }
+ /* XXX - cancel printing? */
+ destroy_print_stream(stream);
+
+#ifdef _WIN32
+ if (win_printer) {
+ /* trash temp file */
+ ws_remove(print_dest);
+ }
+#endif
+}
+
+/*
+ * Keep a static pointer to the current "Save Follow Stream As" window, if
+ * any, so that if somebody tries to do "Save"
+ * while there's already a "Save Follow Stream" window up, we just pop
+ * up the existing one, rather than creating a new one.
+ */
+
+static void
+follow_save_as_cmd_cb(GtkWidget *w _U_, gpointer data)
+{
+ GtkWidget *new_win;
+ follow_info_t *follow_info = data;
+
+#if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
+ if (follow_info->follow_save_as_w != NULL) {
+ /* There's already a dialog box; reactivate it. */
+ reactivate_window(follow_info->follow_save_as_w);
+ return;
+ }
+#endif
+ new_win = file_selection_new("Wireshark: Save Follow Stream As",
+ FILE_SELECTION_SAVE);
+ follow_info->follow_save_as_w = new_win;
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(new_win), TRUE);
+
+ /* Tuck away the follow_info object into the window */
+ g_object_set_data(G_OBJECT(new_win), E_FOLLOW_INFO_KEY, follow_info);
+
+ g_signal_connect(new_win, "destroy", G_CALLBACK(follow_save_as_destroy_cb),
+ follow_info);
+
+#if 0
+ if (gtk_dialog_run(GTK_DIALOG(new_win)) == GTK_RESPONSE_ACCEPT)
+ {
+ follow_save_as_ok_cb(new_win, new_win);
+ } else {
+ window_destroy(new_win);
+ }
+#endif
+ /* "Run" the GtkFileChooserDialog. */
+ /* Upon exit: If "Accept" run the OK callback. */
+ /* If the OK callback returns with a FALSE status, re-run the dialog.*/
+ /* If not accept (ie: cancel) destroy the window. */
+ /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
+ /* return with a TRUE status so that the dialog window will be destroyed. */
+ /* Trying to re-run the dialog after popping up an alert box will not work */
+ /* since the user will not be able to dismiss the alert box. */
+ /* The (somewhat unfriendly) effect: the user must re-invoke the */
+ /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
+ /* */
+ /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
+ /* GtkFileChooserDialog. */
+ while (gtk_dialog_run(GTK_DIALOG(new_win)) == GTK_RESPONSE_ACCEPT) {
+ if (follow_save_as_ok_cb(NULL, new_win)) {
+ break; /* we're done */
+ }
+ }
+ window_destroy(new_win);
+}
+
+
+static gboolean
+follow_save_as_ok_cb(GtkWidget * w _U_, gpointer fs)
+{
+ gchar *to_name;
+ follow_info_t *follow_info;
+ FILE *fh;
+ print_stream_t *stream = NULL;
+ gchar *dirname;
+
+ to_name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
+
+ /* Perhaps the user specified a directory instead of a file.
+ Check whether they did. */
+ if (test_for_directory(to_name) == EISDIR) {
+ /* It's a directory - set the file selection box to display that
+ directory, and leave the selection box displayed. */
+ set_last_open_dir(to_name);
+ g_free(to_name);
+ file_selection_set_current_folder(fs, get_last_open_dir());
+ gtk_file_chooser_set_current_name(fs, "");
+ return FALSE; /* do gtk_dialog_run again */
+ }
+
+ follow_info = g_object_get_data(G_OBJECT(fs), E_FOLLOW_INFO_KEY);
+
+ if (follow_info->show_type == SHOW_RAW) {
+ /* Write the data out as raw binary data */
+ fh = ws_fopen(to_name, "wb");
+ } else {
+ /* Write it out as text */
+ fh = ws_fopen(to_name, "w");
+ }
+ if (fh == NULL) {
+ open_failure_alert_box(to_name, errno, TRUE);
+ g_free(to_name);
+ return TRUE;
+ }
+
+#if 0 /* handled by caller (for now) .... */
+ gtk_widget_hide(GTK_WIDGET(fs));
+ window_destroy(GTK_WIDGET(fs));
+#endif
+ if (follow_info->show_type == SHOW_RAW) {
+ switch (follow_read_stream(follow_info, follow_write_raw, fh)) {
+ case FRS_OK:
+ if (fclose(fh) == EOF)
+ write_failure_alert_box(to_name, errno);
+ break;
+
+ case FRS_OPEN_ERROR:
+ case FRS_READ_ERROR:
+ fclose(fh);
+ break;
+
+ case FRS_PRINT_ERROR:
+ write_failure_alert_box(to_name, errno);
+ fclose(fh);
+ break;
+ }
+ } else {
+ stream = print_stream_text_stdio_new(fh);
+ switch (follow_read_stream(follow_info, follow_print_text,
+ stream)) {
+ case FRS_OK:
+ if (!destroy_print_stream(stream))
+ write_failure_alert_box(to_name, errno);
+ break;
+
+ case FRS_OPEN_ERROR:
+ case FRS_READ_ERROR:
+ destroy_print_stream(stream);
+ break;
+
+ case FRS_PRINT_ERROR:
+ write_failure_alert_box(to_name, errno);
+ destroy_print_stream(stream);
+ break;
+ }
+ }
+
+ /* Save the directory name for future file dialogs. */
+ dirname = get_dirname(to_name); /* Overwrites to_name */
+ set_last_open_dir(dirname);
+ g_free(to_name);
+ return TRUE;
+}
+
+static void
+follow_save_as_destroy_cb(GtkWidget * win _U_, gpointer data)
+{
+ follow_info_t *follow_info = data;
+
+ /* Note that we no longer have a dialog box. */
+ follow_info->follow_save_as_w = NULL;
+}
+
+static void
+follow_stream_direction_changed(GtkWidget *w, gpointer data)
+{
+ follow_info_t *follow_info = data;
+
+ switch(gtk_combo_box_get_active(GTK_COMBO_BOX(w))) {
+
+ case 0 :
+ follow_info->show_stream = BOTH_HOSTS;
+ follow_load_text(follow_info);
+ break;
+ case 1 :
+ follow_info->show_stream = FROM_CLIENT;
+ follow_load_text(follow_info);
+ break;
+ case 2 :
+ follow_info->show_stream = FROM_SERVER;
+ follow_load_text(follow_info);
+ break;
+ }
+}
+
+/* Add a "follow_info_t" structure to the list. */
+static void
+remember_follow_info(follow_info_t *follow_info)
+{
+ follow_infos = g_list_append(follow_infos, follow_info);
+}
+
+#define IS_SHOW_TYPE(x) (follow_info->show_type == x ? 1 : 0)
+/* Remove a "follow_info_t" structure from the list. */
+static void
+forget_follow_info(follow_info_t *follow_info)
+{
+ follow_infos = g_list_remove(follow_infos, follow_info);
+}
+
+void
+follow_stream(gchar *title, follow_info_t *follow_info,
+ gchar *both_directions_string,
+ gchar *server_to_client_string, gchar *client_to_server_string)
+{
+ GtkWidget *streamwindow, *vbox, *txt_scrollw, *text;
+ GtkWidget *hbox, *bbox, *button, *radio_bt;
+ GtkWidget *stream_fr, *stream_vb, *direction_hbox;
+ GtkWidget *stream_cmb;
+ follow_stats_t stats;
+
+ follow_info->show_type = SHOW_RAW;
+
+ streamwindow = dlg_window_new(title);
+
+ /* needed in follow_filter_out_stream(), is there a better way? */
+ follow_info->streamwindow = streamwindow;
+
+ gtk_widget_set_name(streamwindow, title);
+ gtk_window_set_default_size(GTK_WINDOW(streamwindow),
+ DEF_WIDTH, DEF_HEIGHT);
+ gtk_container_set_border_width(GTK_CONTAINER(streamwindow), 6);
+
+ /* setup the container */
+ vbox = gtk_vbox_new(FALSE, 6);
+ gtk_container_add(GTK_CONTAINER(streamwindow), vbox);
+
+ /* content frame */
+ if (incomplete_tcp_stream) {
+ stream_fr = gtk_frame_new("Stream Content (incomplete)");
+ } else {
+ stream_fr = gtk_frame_new("Stream Content");
+ }
+ gtk_container_add(GTK_CONTAINER(vbox), stream_fr);
+ gtk_widget_show(stream_fr);
+
+ stream_vb = gtk_vbox_new(FALSE, 6);
+ gtk_container_set_border_width( GTK_CONTAINER(stream_vb) , 6);
+ gtk_container_add(GTK_CONTAINER(stream_fr), stream_vb);
+
+ /* create a scrolled window for the text */
+ txt_scrollw = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
+ GTK_SHADOW_IN);
+ gtk_box_pack_start(GTK_BOX(stream_vb), txt_scrollw, TRUE, TRUE, 0);
+
+ /* create a text box */
+ text = gtk_text_view_new();
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
+ gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
+
+ gtk_container_add(GTK_CONTAINER(txt_scrollw), text);
+ follow_info->text = text;
+
+ /* direction hbox */
+ direction_hbox = gtk_hbox_new(FALSE, 1);
+ gtk_box_pack_start(GTK_BOX(stream_vb), direction_hbox, FALSE, FALSE, 0);
+
+ stream_cmb = gtk_combo_box_text_new();
+
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(stream_cmb),
+ both_directions_string);
+ follow_info->show_stream = BOTH_HOSTS;
+
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(stream_cmb),
+ server_to_client_string);
+
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(stream_cmb),
+ client_to_server_string);
+
+ gtk_combo_box_set_active(GTK_COMBO_BOX(stream_cmb), 0); /* Do this before signal_connect */
+ /* so callback not triggered */
+
+ g_signal_connect(stream_cmb, "changed",
+ G_CALLBACK(follow_stream_direction_changed),
+ follow_info);
+
+ gtk_widget_set_tooltip_text(stream_cmb, "Select the stream direction to display");
+ gtk_box_pack_start(GTK_BOX(direction_hbox), stream_cmb, TRUE, TRUE, 0);
+
+ /* stream hbox */
+ hbox = gtk_hbox_new(FALSE, 1);
+ gtk_box_pack_start(GTK_BOX(stream_vb), hbox, FALSE, FALSE, 0);
+
+ /* Create Find Button */
+ button = gtk_button_new_from_stock(GTK_STOCK_FIND);
+ g_signal_connect(button, "clicked", G_CALLBACK(follow_find_cb), follow_info);
+ gtk_widget_set_tooltip_text(button, "Find text in the displayed content");
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+
+ /* Create Save As Button */
+ button = gtk_button_new_from_stock(GTK_STOCK_SAVE_AS);
+ g_signal_connect(button, "clicked", G_CALLBACK(follow_save_as_cmd_cb), follow_info);
+ gtk_widget_set_tooltip_text(button, "Save the content as currently displayed");
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+
+ /* Create Print Button */
+ button = gtk_button_new_from_stock(GTK_STOCK_PRINT);
+ g_signal_connect(button, "clicked", G_CALLBACK(follow_print_stream), follow_info);
+ gtk_widget_set_tooltip_text(button, "Print the content as currently displayed");
+ gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+
+ /* Stream to show */
+ follow_stats(&stats);
+
+ follow_info->is_ipv6 = stats.is_ipv6;
+
+ /* ASCII radio button */
+ radio_bt = gtk_radio_button_new_with_label(NULL, "ASCII");
+ gtk_widget_set_tooltip_text(radio_bt, "Stream data output in \"ASCII\" format");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
+ IS_SHOW_TYPE(SHOW_ASCII));
+ gtk_box_pack_start(GTK_BOX(hbox), radio_bt, TRUE, TRUE, 0);
+ g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
+ follow_info);
+ follow_info->ascii_bt = radio_bt;
+
+ /* EBCDIC radio button */
+ radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
+ (GTK_RADIO_BUTTON(radio_bt)),
+ "EBCDIC");
+ gtk_widget_set_tooltip_text(radio_bt, "Stream data output in \"EBCDIC\" format");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
+ IS_SHOW_TYPE(SHOW_EBCDIC));
+ gtk_box_pack_start(GTK_BOX(hbox), radio_bt, TRUE, TRUE, 0);
+ g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
+ follow_info);
+ follow_info->ebcdic_bt = radio_bt;
+
+ /* HEX DUMP radio button */
+ radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
+ (GTK_RADIO_BUTTON(radio_bt)),
+ "Hex Dump");
+ gtk_widget_set_tooltip_text(radio_bt, "Stream data output in \"Hexdump\" format");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
+ IS_SHOW_TYPE(SHOW_HEXDUMP));
+ gtk_box_pack_start(GTK_BOX(hbox), radio_bt, TRUE, TRUE, 0);
+ g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
+ follow_info);
+ follow_info->hexdump_bt = radio_bt;
+
+ /* C Array radio button */
+ radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
+ (GTK_RADIO_BUTTON(radio_bt)),
+ "C Arrays");
+ gtk_widget_set_tooltip_text(radio_bt, "Stream data output in \"C Array\" format");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
+ IS_SHOW_TYPE(SHOW_CARRAY));
+ gtk_box_pack_start(GTK_BOX(hbox), radio_bt, TRUE, TRUE, 0);
+ g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
+ follow_info);
+ follow_info->carray_bt = radio_bt;
+
+ /* Raw radio button */
+ radio_bt = gtk_radio_button_new_with_label(gtk_radio_button_get_group
+ (GTK_RADIO_BUTTON(radio_bt)),
+ "Raw");
+ gtk_widget_set_tooltip_text(radio_bt,
+ "Stream data output in \"Raw\" (binary) format. "
+ "As this contains non printable characters, "
+ "the screen output will be in ASCII format");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_bt),
+ IS_SHOW_TYPE(SHOW_RAW));
+ gtk_box_pack_start(GTK_BOX(hbox), radio_bt, TRUE, TRUE, 0);
+ g_signal_connect(radio_bt, "toggled", G_CALLBACK(follow_charset_toggle_cb),
+ follow_info);
+ follow_info->raw_bt = radio_bt;
+
+ /* Button row: help, filter out, close button */
+ bbox = dlg_button_row_new(WIRESHARK_STOCK_FILTER_OUT_STREAM,
+ GTK_STOCK_CLOSE, GTK_STOCK_HELP,
+ NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 5);
+
+
+ button = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_FILTER_OUT_STREAM);
+ gtk_widget_set_tooltip_text(button, "Build a display filter which cuts this stream from the capture");
+ g_signal_connect(button, "clicked", G_CALLBACK(follow_filter_out_stream),
+ follow_info);
+
+ button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(streamwindow, button, window_cancel_button_cb);
+ gtk_widget_set_tooltip_text(button, "Close the dialog and keep the current display filter");
+ gtk_widget_grab_default(button);
+
+ button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(button, "clicked", G_CALLBACK(topic_cb),
+ (gpointer)HELP_FOLLOW_STREAM_DIALOG);
+
+ /* Tuck away the follow_info object into the window */
+ g_object_set_data(G_OBJECT(streamwindow), E_FOLLOW_INFO_KEY, follow_info);
+
+ follow_load_text(follow_info);
+ remember_follow_info(follow_info);
+
+
+ g_signal_connect(streamwindow, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(streamwindow, "destroy", G_CALLBACK(follow_destroy_cb), NULL);
+
+ /* Make sure this widget gets destroyed if we quit the main loop,
+ so that if we exit, we clean up any temporary files we have
+ for "Follow TCP Stream" windows.
+ gtk_quit_add_destroy is deprecated and should not be used in newly-written code.
+ This function is going to be removed in GTK+ 3.0
+ gtk_quit_add_destroy(gtk_main_level(), GTK_OBJECT(streamwindow));
+ */
+
+ gtk_widget_show_all(streamwindow);
+ window_present(streamwindow);
+}
+
+/* The destroy call back has the responsibility of
+ * unlinking the temporary file
+ * and freeing the filter_out_filter */
+static void
+follow_destroy_cb(GtkWidget *w, gpointer data _U_)
+{
+ follow_info_t *follow_info;
+ follow_record_t *follow_record;
+ GList *cur;
+ int i;
+
+ follow_info = g_object_get_data(G_OBJECT(w), E_FOLLOW_INFO_KEY);
+
+ switch(follow_info->follow_type) {
+
+ case FOLLOW_TCP :
+ i = ws_unlink(follow_info->data_out_filename);
+ if(i != 0) {
+ g_warning("Follow: Couldn't remove temporary file: \"%s\", errno: %s (%u)",
+ follow_info->data_out_filename, g_strerror(errno), errno);
+ }
+ break;
+
+ case FOLLOW_UDP :
+ for(cur = follow_info->payload; cur; cur = g_list_next(cur))
+ if(cur->data) {
+ follow_record = cur->data;
+ if(follow_record->data)
+ g_byte_array_free(follow_record->data,
+ TRUE);
+
+ g_free(follow_record);
+ }
+
+ g_list_free(follow_info->payload);
+ break;
+
+ case FOLLOW_SSL :
+ /* free decrypted data list*/
+ for (cur = follow_info->payload; cur; cur = g_list_next(cur))
+ if (cur->data)
+ {
+ g_free(cur->data);
+ cur->data = NULL;
+ }
+ g_list_free (follow_info->payload);
+ break;
+ }
+
+ g_free(follow_info->data_out_filename);
+ g_free(follow_info->filter_out_filter);
+ g_free((gpointer)follow_info->client_ip.data);
+ forget_follow_info(follow_info);
+ g_free(follow_info);
+ gtk_widget_destroy(w);
+}
+
+frs_return_t
+follow_show(follow_info_t *follow_info,
+ gboolean (*print_line_fcn_p)(char *, size_t, gboolean, void *),
+ char *buffer, size_t nchars, gboolean is_server, void *arg,
+ guint32 *global_pos, guint32 *server_packet_count,
+ guint32 *client_packet_count)
+{
+ gchar initbuf[256];
+ guint32 current_pos;
+ static const gchar hexchars[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+
+ switch (follow_info->show_type) {
+
+ case SHOW_EBCDIC:
+ /* If our native arch is ASCII, call: */
+ EBCDIC_to_ASCII(buffer, (guint) nchars);
+ if (!(*print_line_fcn_p) (buffer, nchars, is_server, arg))
+ return FRS_PRINT_ERROR;
+ break;
+
+ case SHOW_ASCII:
+ /* If our native arch is EBCDIC, call:
+ * ASCII_TO_EBCDIC(buffer, nchars);
+ */
+ if (!(*print_line_fcn_p) (buffer, nchars, is_server, arg))
+ return FRS_PRINT_ERROR;
+ break;
+
+ case SHOW_RAW:
+ /* Don't translate, no matter what the native arch
+ * is.
+ */
+ if (!(*print_line_fcn_p) (buffer, nchars, is_server, arg))
+ return FRS_PRINT_ERROR;
+ break;
+
+ case SHOW_HEXDUMP:
+ current_pos = 0;
+ while (current_pos < nchars) {
+ gchar hexbuf[256];
+ int i;
+ gchar *cur = hexbuf, *ascii_start;
+
+ /* is_server indentation : put 4 spaces at the
+ * beginning of the string */
+ /* XXX - We might want to prepend each line with "C" or "S" instead. */
+ if (is_server && follow_info->show_stream == BOTH_HOSTS) {
+ memset(cur, ' ', 4);
+ cur += 4;
+ }
+ cur += g_snprintf(cur, 20, "%08X ", *global_pos);
+ /* 49 is space consumed by hex chars */
+ ascii_start = cur + 49;
+ for (i = 0; i < 16 && current_pos + i < nchars; i++) {
+ *cur++ =
+ hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
+ *cur++ =
+ hexchars[buffer[current_pos + i] & 0x0f];
+ *cur++ = ' ';
+ if (i == 7)
+ *cur++ = ' ';
+ }
+ /* Fill it up if column isn't complete */
+ while (cur < ascii_start)
+ *cur++ = ' ';
+
+ /* Now dump bytes as text */
+ for (i = 0; i < 16 && current_pos + i < nchars; i++) {
+ *cur++ =
+ (isprint((guchar)buffer[current_pos + i]) ?
+ buffer[current_pos + i] : '.' );
+ if (i == 7) {
+ *cur++ = ' ';
+ }
+ }
+ current_pos += i;
+ (*global_pos) += i;
+ *cur++ = '\n';
+ *cur = 0;
+ if (!(*print_line_fcn_p) (hexbuf, strlen(hexbuf), is_server, arg))
+ return FRS_PRINT_ERROR;
+ }
+ break;
+
+ case SHOW_CARRAY:
+ current_pos = 0;
+ g_snprintf(initbuf, sizeof(initbuf), "char peer%d_%d[] = {\n",
+ is_server ? 1 : 0,
+ is_server ? (*server_packet_count)++ : (*client_packet_count)++);
+ if (!(*print_line_fcn_p) (initbuf, strlen(initbuf), is_server, arg))
+ return FRS_PRINT_ERROR;
+
+ while (current_pos < nchars) {
+ gchar hexbuf[256];
+ int i, cur;
+
+ cur = 0;
+ for (i = 0; i < 8 && current_pos + i < nchars; i++) {
+ /* Prepend entries with "0x" */
+ hexbuf[cur++] = '0';
+ hexbuf[cur++] = 'x';
+ hexbuf[cur++] =
+ hexchars[(buffer[current_pos + i] & 0xf0) >> 4];
+ hexbuf[cur++] =
+ hexchars[buffer[current_pos + i] & 0x0f];
+
+ /* Delimit array entries with a comma */
+ if (current_pos + i + 1 < nchars)
+ hexbuf[cur++] = ',';
+
+ hexbuf[cur++] = ' ';
+ }
+
+ /* Terminate the array if we are at the end */
+ if (current_pos + i == nchars) {
+ hexbuf[cur++] = '}';
+ hexbuf[cur++] = ';';
+ }
+
+ current_pos += i;
+ (*global_pos) += i;
+ hexbuf[cur++] = '\n';
+ hexbuf[cur] = 0;
+ if (!(*print_line_fcn_p) (hexbuf, strlen(hexbuf), is_server, arg))
+ return FRS_PRINT_ERROR;
+ }
+ break;
+ }
+
+ return FRS_OK;
+}
diff --git a/ui/gtk/follow_stream.h b/ui/gtk/follow_stream.h
new file mode 100644
index 0000000000..1d4fef6362
--- /dev/null
+++ b/ui/gtk/follow_stream.h
@@ -0,0 +1,115 @@
+/* follow_stream.h
+ * Common routines for following data streams
+ *
+ * $Id$
+ *
+ * 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 __FOLLOW_STREAM_H__
+#define __FOLLOW_STREAM_H__
+
+#include <gtk/gtk.h>
+
+/* Type of follow we are doing */
+typedef enum {
+ FOLLOW_TCP,
+ FOLLOW_SSL,
+ FOLLOW_UDP
+} follow_type_t;
+
+/* Show Stream */
+typedef enum {
+ FROM_CLIENT,
+ FROM_SERVER,
+ BOTH_HOSTS
+} show_stream_t;
+
+/* Show Type */
+typedef enum {
+ SHOW_ASCII,
+ SHOW_EBCDIC,
+ SHOW_HEXDUMP,
+ SHOW_CARRAY,
+ SHOW_RAW
+} show_type_t;
+
+typedef enum {
+ FRS_OK,
+ FRS_OPEN_ERROR,
+ FRS_READ_ERROR,
+ FRS_PRINT_ERROR
+} frs_return_t;
+
+typedef struct {
+ gboolean is_server;
+ GByteArray *data;
+} follow_record_t;
+
+typedef struct {
+ follow_type_t follow_type;
+ show_stream_t show_stream;
+ show_type_t show_type;
+ char *data_out_filename;
+ GtkWidget *text;
+ GtkWidget *ascii_bt;
+ GtkWidget *ebcdic_bt;
+ GtkWidget *hexdump_bt;
+ GtkWidget *carray_bt;
+ GtkWidget *raw_bt;
+ GtkWidget *follow_save_as_w;
+ GtkWidget *find_dlg_w;
+ gboolean is_ipv6;
+ char *filter_out_filter;
+ GtkWidget *filter_te;
+ GtkWidget *streamwindow;
+ GList *payload;
+ guint bytes_written[2];
+ guint client_port;
+ address client_ip;
+} follow_info_t;
+
+#define E_FOLLOW_INFO_KEY "follow_info_key"
+
+/* List of "follow_info_t" structures for all "Follow TCP Stream" windows,
+ so we can redraw them all if the colors or font changes. */
+extern GList *follow_infos;
+
+void follow_load_text(follow_info_t *follow_info);
+void follow_filter_out_stream(GtkWidget * w, gpointer parent_w);
+void follow_stream(gchar *title, follow_info_t *follow_info,
+ gchar *both_directions_string,
+ gchar *server_to_client_string,
+ gchar *client_to_server_string);
+frs_return_t follow_show(follow_info_t *follow_info,
+ gboolean (*print_line)(char *, size_t, gboolean,
+ void *),
+ char *buffer, size_t nchars, gboolean is_server,
+ void *arg, guint32 *global_pos,
+ guint32 *server_packet_count,
+ guint32 *client_packet_count);
+gboolean follow_add_to_gtk_text(char *buffer, size_t nchars, gboolean is_server,
+ void *arg);
+
+frs_return_t follow_read_tcp_stream(follow_info_t *follow_info, gboolean (*print_line)(char *, size_t, gboolean, void *), void *arg);
+frs_return_t follow_read_udp_stream(follow_info_t *follow_info, gboolean (*print_line)(char *, size_t, gboolean, void *), void *arg);
+frs_return_t follow_read_ssl_stream(follow_info_t *follow_info, gboolean (*print_line)(char *, size_t, gboolean, void *), void *arg);
+
+#endif /* __FOLLOW_STREAM_H__ */
diff --git a/ui/gtk/follow_tcp.c b/ui/gtk/follow_tcp.c
new file mode 100644
index 0000000000..ada4f65b55
--- /dev/null
+++ b/ui/gtk/follow_tcp.c
@@ -0,0 +1,432 @@
+/* follow_tcp.c
+ * TCP specific routines for following traffic streams
+ *
+ * $Id$
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <ctype.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/follow.h>
+#include <epan/dissectors/packet-ipv6.h>
+#include <epan/prefs.h>
+#include <epan/addr_resolv.h>
+#include <epan/charsets.h>
+#include <epan/epan_dissect.h>
+#include <epan/filesystem.h>
+#include <epan/ipproto.h>
+#include <epan/charsets.h>
+
+#include "../file.h"
+#include "../alert_box.h"
+#include "../simple_dialog.h"
+#include "../tempfile.h"
+#include <wsutil/file_util.h>
+
+#include "gtkglobals.h"
+#include "ui/gtk/color_utils.h"
+#include "ui/gtk/follow_tcp.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/file_dlg.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/gui_utils.h"
+#include "win32/print_win32.h"
+#include "ui/gtk/font_utils.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/follow_stream.h"
+#include "ui/gtk/utf8_entities.h"
+
+/* With MSVC and a libwireshark.dll, we need a special declaration. */
+WS_VAR_IMPORT FILE *data_out_file;
+
+static void
+follow_redraw(gpointer data, gpointer user_data _U_)
+{
+ follow_load_text((follow_info_t *)data);
+}
+
+/* Redraw the text in all "Follow TCP Stream" windows. */
+void
+follow_tcp_redraw_all(void)
+{
+ g_list_foreach(follow_infos, follow_redraw, NULL);
+}
+
+/* Follow the TCP stream, if any, to which the last packet that we called
+ a dissection routine on belongs (this might be the most recently
+ selected packet, or it might be the last packet in the file). */
+void
+follow_tcp_stream_cb(GtkWidget * w _U_, gpointer data _U_)
+{
+ GtkWidget *filter_cm;
+ GtkWidget *filter_te;
+ int tmp_fd;
+ gchar *follow_filter;
+ const gchar *previous_filter;
+ int filter_out_filter_len;
+ const char *hostname0, *hostname1;
+ char *port0, *port1;
+ gchar *server_to_client_string = NULL;
+ gchar *client_to_server_string = NULL;
+ gchar *both_directions_string = NULL;
+ follow_stats_t stats;
+ follow_info_t *follow_info;
+ tcp_stream_chunk sc;
+ size_t nchars;
+ gchar *data_out_filename;
+
+ /* we got tcp so we can follow */
+ if (cfile.edt->pi.ipproto != IP_PROTO_TCP) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Error following stream. Please make\n"
+ "sure you have a TCP packet selected.");
+ return;
+ }
+
+ follow_info = g_new0(follow_info_t, 1);
+ follow_info->follow_type = FOLLOW_TCP;
+
+ /* Create a new filter that matches all packets in the TCP stream,
+ and set the display filter entry accordingly */
+ reset_tcp_reassembly();
+ follow_filter = build_follow_filter(&cfile.edt->pi);
+ if (!follow_filter) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Error creating filter for this stream.\n"
+ "A transport or network layer header is needed");
+ g_free(follow_info);
+ return;
+ }
+
+ /* Create a temporary file into which to dump the reassembled data
+ from the TCP stream, and set "data_out_file" to refer to it, so
+ that the TCP code will write to it.
+
+ XXX - it might be nicer to just have the TCP code directly
+ append stuff to the text widget for the TCP stream window,
+ if we can arrange that said window not pop up until we're
+ done. */
+ tmp_fd = create_tempfile(&data_out_filename, "follow");
+ follow_info->data_out_filename = g_strdup(data_out_filename);
+
+ if (tmp_fd == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Could not create temporary file %s: %s",
+ follow_info->data_out_filename, g_strerror(errno));
+ g_free(follow_info->data_out_filename);
+ g_free(follow_info);
+ g_free(follow_filter);
+ return;
+ }
+
+ data_out_file = fdopen(tmp_fd, "w+b");
+ if (data_out_file == NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Could not create temporary file %s: %s",
+ follow_info->data_out_filename, g_strerror(errno));
+ ws_close(tmp_fd);
+ ws_unlink(follow_info->data_out_filename);
+ g_free(follow_info->data_out_filename);
+ g_free(follow_info);
+ g_free(follow_filter);
+ return;
+ }
+
+ /* Set the display filter entry accordingly */
+ filter_cm = g_object_get_data(G_OBJECT(top_level), E_DFILTER_CM_KEY);
+ filter_te = gtk_bin_get_child(GTK_BIN(filter_cm));
+
+ /* needed in follow_filter_out_stream(), is there a better way? */
+ follow_info->filter_te = filter_te;
+
+ /* save previous filter, const since we're not supposed to alter */
+ previous_filter =
+ (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
+
+ /* allocate our new filter. API claims g_malloc terminates program on failure */
+ /* my calc for max alloc needed is really +10 but when did a few extra bytes hurt ? */
+ filter_out_filter_len = (int)(strlen(follow_filter) + strlen(previous_filter) + 16);
+ follow_info->filter_out_filter = (gchar *)g_malloc(filter_out_filter_len);
+
+ /* append the negation */
+ if(strlen(previous_filter)) {
+ g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
+ "%s and !(%s)", previous_filter, follow_filter);
+ } else {
+ g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
+ "!(%s)", follow_filter);
+ }
+
+ gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter);
+
+ /* Run the display filter so it goes in effect - even if it's the
+ same as the previous display filter. */
+ main_filter_packets(&cfile, follow_filter, TRUE);
+
+ /* Free the filter string, as we're done with it. */
+ g_free(follow_filter);
+
+ /* Check whether we got any data written to the file. */
+ if (empty_tcp_stream) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "The packets in the capture file for that stream have no data.");
+ ws_close(tmp_fd);
+ ws_unlink(follow_info->data_out_filename);
+ g_free(follow_info->data_out_filename);
+ g_free(follow_info->filter_out_filter);
+ g_free(follow_info);
+ return;
+ }
+
+ /* Go back to the top of the file and read the first tcp_stream_chunk
+ * to ensure that the IP addresses and port numbers in the drop-down
+ * list are tied to the correct lines displayed by follow_read_stream()
+ * later on (which also reads from this file). Close the file when
+ * we're done.
+ *
+ * We read the data now, before we pop up a window, in case the
+ * read fails. We use the data later.
+ */
+
+ rewind(data_out_file);
+ nchars=fread(&sc, 1, sizeof(sc), data_out_file);
+ if (nchars != sizeof(sc)) {
+ if (ferror(data_out_file)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Could not read from temporary file %s: %s",
+ follow_info->data_out_filename, g_strerror(errno));
+ } else {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Short read from temporary file %s: expected %lu, got %lu",
+ follow_info->data_out_filename,
+ (unsigned long)sizeof(sc),
+ (unsigned long)nchars);
+ }
+ ws_close(tmp_fd);
+ ws_unlink(follow_info->data_out_filename);
+ g_free(follow_info->data_out_filename);
+ g_free(follow_info->filter_out_filter);
+ g_free(follow_info);
+ return;
+ }
+ fclose(data_out_file);
+
+ /* The data_out_filename file now has all the text that was in the
+ session (this is dumped to file by the TCP dissector). */
+
+ /* Stream to show */
+ follow_stats(&stats);
+
+ if (stats.is_ipv6) {
+ struct e_in6_addr ipaddr;
+ memcpy(&ipaddr, stats.ip_address[0], 16);
+ hostname0 = get_hostname6(&ipaddr);
+ memcpy(&ipaddr, stats.ip_address[0], 16);
+ hostname1 = get_hostname6(&ipaddr);
+ } else {
+ guint32 ipaddr;
+ memcpy(&ipaddr, stats.ip_address[0], 4);
+ hostname0 = get_hostname(ipaddr);
+ memcpy(&ipaddr, stats.ip_address[1], 4);
+ hostname1 = get_hostname(ipaddr);
+ }
+
+ follow_info->is_ipv6 = stats.is_ipv6;
+
+ port0 = get_tcp_port(stats.port[0]);
+ port1 = get_tcp_port(stats.port[1]);
+
+ /* Host 0 --> Host 1 */
+ if(sc.src_port == stats.port[0]) {
+ server_to_client_string =
+ g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
+ hostname0, port0,
+ hostname1, port1,
+ stats.bytes_written[0]);
+ } else {
+ server_to_client_string =
+ g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
+ hostname1, port1,
+ hostname0,port0,
+ stats.bytes_written[0]);
+ }
+
+ /* Host 1 --> Host 0 */
+ if(sc.src_port == stats.port[1]) {
+ client_to_server_string =
+ g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
+ hostname0, port0,
+ hostname1, port1,
+ stats.bytes_written[1]);
+ } else {
+ client_to_server_string =
+ g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
+ hostname1, port1,
+ hostname0, port0,
+ stats.bytes_written[1]);
+ }
+
+ /* Both Stream Directions */
+ both_directions_string = g_strdup_printf("Entire conversation (%u bytes)", stats.bytes_written[0] + stats.bytes_written[1]);
+
+ follow_stream("Follow TCP Stream", follow_info, both_directions_string,
+ server_to_client_string, client_to_server_string);
+
+ g_free(both_directions_string);
+ g_free(server_to_client_string);
+ g_free(client_to_server_string);
+
+ data_out_file = NULL;
+}
+
+#define FLT_BUF_SIZE 1024
+
+/*
+ * XXX - the routine pointed to by "print_line_fcn_p" doesn't get handed lines,
+ * it gets handed bufferfuls. That's fine for "follow_write_raw()"
+ * and "follow_add_to_gtk_text()", but, as "follow_print_text()" calls
+ * the "print_line()" routine from "print.c", and as that routine might
+ * genuinely expect to be handed a line (if, for example, it's using
+ * some OS or desktop environment's printing API, and that API expects
+ * to be handed lines), "follow_print_text()" should probably accumulate
+ * lines in a buffer and hand them "print_line()". (If there's a
+ * complete line in a buffer - i.e., there's nothing of the line in
+ * the previous buffer or the next buffer - it can just hand that to
+ * "print_line()" after filtering out non-printables, as an
+ * optimization.)
+ *
+ * This might or might not be the reason why C arrays display
+ * correctly but get extra blank lines very other line when printed.
+ */
+frs_return_t
+follow_read_tcp_stream(follow_info_t *follow_info,
+ gboolean (*print_line_fcn_p)(char *, size_t, gboolean, void *),
+ void *arg)
+{
+ tcp_stream_chunk sc;
+ size_t bcount;
+ size_t bytes_read;
+ int iplen;
+ guint8 client_addr[MAX_IPADDR_LEN];
+ guint16 client_port = 0;
+ gboolean is_server;
+ guint32 global_client_pos = 0, global_server_pos = 0;
+ guint32 server_packet_count = 0;
+ guint32 client_packet_count = 0;
+ guint32 *global_pos;
+ gboolean skip;
+ char buffer[FLT_BUF_SIZE+1]; /* +1 to fix ws bug 1043 */
+ size_t nchars;
+ frs_return_t frs_return;
+
+ iplen = (follow_info->is_ipv6) ? 16 : 4;
+
+ data_out_file = ws_fopen(follow_info->data_out_filename, "rb");
+ if (data_out_file == NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Could not open temporary file %s: %s", follow_info->data_out_filename,
+ g_strerror(errno));
+ return FRS_OPEN_ERROR;
+ }
+
+ while ((nchars=fread(&sc, 1, sizeof(sc), data_out_file))) {
+ if (nchars != sizeof(sc)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Short read from temporary file %s: expected %lu, got %lu",
+ follow_info->data_out_filename,
+ (unsigned long)sizeof(sc),
+ (unsigned long)nchars);
+ fclose(data_out_file);
+ data_out_file = NULL;
+ return FRS_READ_ERROR;
+ }
+ if (client_port == 0) {
+ memcpy(client_addr, sc.src_addr, iplen);
+ client_port = sc.src_port;
+ }
+ skip = FALSE;
+ if (memcmp(client_addr, sc.src_addr, iplen) == 0 &&
+ client_port == sc.src_port) {
+ is_server = FALSE;
+ global_pos = &global_client_pos;
+ if (follow_info->show_stream == FROM_SERVER) {
+ skip = TRUE;
+ }
+ }
+ else {
+ is_server = TRUE;
+ global_pos = &global_server_pos;
+ if (follow_info->show_stream == FROM_CLIENT) {
+ skip = TRUE;
+ }
+ }
+
+ bytes_read = 0;
+ while (bytes_read < sc.dlen) {
+ bcount = ((sc.dlen-bytes_read) < FLT_BUF_SIZE) ? (sc.dlen-bytes_read) : FLT_BUF_SIZE;
+ nchars = fread(buffer, 1, bcount, data_out_file);
+ if (nchars == 0)
+ break;
+ /* XXX - if we don't get "bcount" bytes, is that an error? */
+ bytes_read += nchars;
+
+ if (!skip) {
+ frs_return = follow_show(follow_info, print_line_fcn_p, buffer,
+ nchars, is_server, arg, global_pos,
+ &server_packet_count,
+ &client_packet_count);
+ if(frs_return == FRS_PRINT_ERROR) {
+ fclose(data_out_file);
+ data_out_file = NULL;
+ return frs_return;
+
+ }
+ }
+ }
+ }
+
+ if (ferror(data_out_file)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Error reading temporary file %s: %s", follow_info->data_out_filename,
+ g_strerror(errno));
+ fclose(data_out_file);
+ data_out_file = NULL;
+ return FRS_READ_ERROR;
+ }
+
+ fclose(data_out_file);
+ data_out_file = NULL;
+ return FRS_OK;
+}
diff --git a/ui/gtk/follow_tcp.h b/ui/gtk/follow_tcp.h
new file mode 100644
index 0000000000..8bea0ba0b6
--- /dev/null
+++ b/ui/gtk/follow_tcp.h
@@ -0,0 +1,44 @@
+/* follow_tcp.h
+ * TCP specific routines for following traffic streams
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2000 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __FOLLOW_TCP_H__
+#define __FOLLOW_TCP_H__
+
+/** @file
+ * "Follow TCP Stream" dialog box.
+ * @ingroup dialog_group
+ */
+
+/** User requested the "Follow TCP Stream" dialog box by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void follow_tcp_stream_cb( GtkWidget *widget, gpointer data);
+
+/** Redraw the text in all "Follow TCP Stream" windows. */
+extern void follow_tcp_redraw_all(void);
+
+#endif /* __FOLLOW_TCP_H__ */
diff --git a/ui/gtk/follow_udp.c b/ui/gtk/follow_udp.c
new file mode 100644
index 0000000000..a25f569d1d
--- /dev/null
+++ b/ui/gtk/follow_udp.c
@@ -0,0 +1,304 @@
+/* follow_udp.c
+ * UDP specific routines for following traffic streams
+ *
+ * $Id$
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/addr_resolv.h>
+#include <epan/epan_dissect.h>
+#include <epan/follow.h>
+#include <epan/ipproto.h>
+#include <epan/strutil.h>
+#include <epan/tap.h>
+
+#include <../simple_dialog.h>
+
+#include "gtkglobals.h"
+#include "ui/gtk/follow_stream.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/follow_udp.h"
+#include "ui/gtk/utf8_entities.h"
+
+static int
+udp_queue_packet_data(void *tapdata, packet_info *pinfo,
+ epan_dissect_t *edt _U_, const void *data)
+{
+ follow_record_t *follow_record;
+ follow_info_t *follow_info = tapdata;
+ tvbuff_t *next_tvb = (tvbuff_t *)data;
+
+ follow_record = g_malloc(sizeof(follow_record_t));
+
+ follow_record->data = g_byte_array_sized_new(tvb_length(next_tvb));
+ follow_record->data = g_byte_array_append(follow_record->data,
+ tvb_get_ptr(next_tvb, 0, -1),
+ tvb_length(next_tvb));
+
+ if (follow_info->client_port == 0) {
+ follow_info->client_port = pinfo->srcport;
+ COPY_ADDRESS(&follow_info->client_ip, &pinfo->src);
+ }
+
+ if (ADDRESSES_EQUAL(&follow_info->client_ip, &pinfo->src) &&
+ follow_info->client_port == pinfo->srcport)
+ follow_record->is_server = FALSE;
+ else
+ follow_record->is_server = TRUE;
+
+ /* update stream counter */
+ follow_info->bytes_written[follow_record->is_server] +=
+ follow_record->data->len;
+
+ follow_info->payload = g_list_append(follow_info->payload,
+ follow_record);
+ return 0;
+}
+
+
+/* Follow the UDP stream, if any, to which the last packet that we called
+ a dissection routine on belongs (this might be the most recently
+ selected packet, or it might be the last packet in the file). */
+void
+follow_udp_stream_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ GtkWidget *filter_te, *filter_cm;
+ gchar *follow_filter;
+ const gchar *previous_filter;
+ int filter_out_filter_len, previous_filter_len;
+ const char *hostname0, *hostname1;
+ char *port0, *port1;
+ gchar *server_to_client_string = NULL;
+ gchar *client_to_server_string = NULL;
+ gchar *both_directions_string = NULL;
+ follow_stats_t stats;
+ follow_info_t *follow_info;
+ GString *msg;
+
+ /* we got udp so we can follow */
+ if(cfile.edt->pi.ipproto != IP_PROTO_UDP) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Error following stream. Please make\n"
+ "sure you have a UDP packet selected.");
+ return;
+ }
+
+ follow_info = g_new0(follow_info_t, 1);
+ follow_info->follow_type = FOLLOW_UDP;
+
+ /* Create a new filter that matches all packets in the UDP stream,
+ and set the display filter entry accordingly */
+ follow_filter = build_follow_filter(&cfile.edt->pi);
+ if (!follow_filter)
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Error creating filter for this stream.\n"
+ "A network layer header is needed");
+ g_free(follow_info);
+ return;
+ }
+
+ /* Set the display filter entry accordingly */
+ filter_cm = g_object_get_data(G_OBJECT(top_level), E_DFILTER_CM_KEY);
+ filter_te = gtk_bin_get_child(GTK_BIN(filter_cm));
+
+ /* needed in follow_filter_out_stream(), is there a better way? */
+ follow_info->filter_te = filter_te;
+
+ /* save previous filter, const since we're not supposed to alter */
+ previous_filter =
+ (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te));
+
+ /* allocate our new filter. API claims g_malloc terminates program on failure */
+ /* my calc for max alloc needed is really +10 but when did a few extra bytes hurt ? */
+ previous_filter_len = previous_filter?(int)strlen(previous_filter):0;
+ filter_out_filter_len = (int)strlen(follow_filter) + previous_filter_len + 16;
+ follow_info->filter_out_filter = (gchar *)g_malloc(filter_out_filter_len);
+
+ /* append the negation */
+ if(previous_filter_len) {
+ g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
+ "%s and !(%s)", previous_filter, follow_filter);
+ } else {
+ g_snprintf(follow_info->filter_out_filter, filter_out_filter_len,
+ "!(%s)", follow_filter);
+ }
+
+ /* data will be passed via tap callback*/
+ msg = register_tap_listener("udp_follow", follow_info, follow_filter,
+ 0, NULL, udp_queue_packet_data, NULL);
+ if (msg) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't register udp_follow tap: %s\n",
+ msg->str);
+ g_free(follow_info->filter_out_filter);
+ g_free(follow_info);
+ g_free(follow_filter);
+ return;
+ }
+
+ gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter);
+
+ /* Run the display filter so it goes in effect - even if it's the
+ same as the previous display filter. */
+ main_filter_packets(&cfile, follow_filter, TRUE);
+
+ /* Free the filter string, as we're done with it. */
+ g_free(follow_filter);
+
+ remove_tap_listener(follow_info);
+
+ /* Stream to show */
+ follow_stats(&stats);
+
+ if (stats.is_ipv6) {
+ struct e_in6_addr ipaddr;
+ memcpy(&ipaddr, stats.ip_address[0], 16);
+ hostname0 = get_hostname6(&ipaddr);
+ memcpy(&ipaddr, stats.ip_address[0], 16);
+ hostname1 = get_hostname6(&ipaddr);
+ } else {
+ guint32 ipaddr;
+ memcpy(&ipaddr, stats.ip_address[0], 4);
+ hostname0 = get_hostname(ipaddr);
+ memcpy(&ipaddr, stats.ip_address[1], 4);
+ hostname1 = get_hostname(ipaddr);
+ }
+
+ port0 = get_udp_port(stats.port[0]);
+ port1 = get_udp_port(stats.port[1]);
+
+ follow_info->is_ipv6 = stats.is_ipv6;
+
+ /* Both Stream Directions */
+ both_directions_string = g_strdup_printf("Entire conversation (%u bytes)", follow_info->bytes_written[0] + follow_info->bytes_written[1]);
+
+ if(follow_info->client_port == stats.port[0]) {
+ server_to_client_string =
+ g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
+ hostname0, port0,
+ hostname1, port1,
+ follow_info->bytes_written[0]);
+
+ client_to_server_string =
+ g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
+ hostname1, port1,
+ hostname0, port0,
+ follow_info->bytes_written[1]);
+ } else {
+ server_to_client_string =
+ g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
+ hostname1, port1,
+ hostname0, port0,
+ follow_info->bytes_written[0]);
+
+ client_to_server_string =
+ g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)",
+ hostname0, port0,
+ hostname1, port1,
+ follow_info->bytes_written[1]);
+ }
+
+ follow_stream("Follow UDP Stream", follow_info, both_directions_string,
+ server_to_client_string, client_to_server_string);
+
+ g_free(both_directions_string);
+ g_free(server_to_client_string);
+ g_free(client_to_server_string);
+}
+
+#define FLT_BUF_SIZE 1024
+
+/*
+ * XXX - the routine pointed to by "print_line_fcn_p" doesn't get handed lines,
+ * it gets handed bufferfuls. That's fine for "follow_write_raw()"
+ * and "follow_add_to_gtk_text()", but, as "follow_print_text()" calls
+ * the "print_line()" routine from "print.c", and as that routine might
+ * genuinely expect to be handed a line (if, for example, it's using
+ * some OS or desktop environment's printing API, and that API expects
+ * to be handed lines), "follow_print_text()" should probably accumulate
+ * lines in a buffer and hand them "print_line()". (If there's a
+ * complete line in a buffer - i.e., there's nothing of the line in
+ * the previous buffer or the next buffer - it can just hand that to
+ * "print_line()" after filtering out non-printables, as an
+ * optimization.)
+ *
+ * This might or might not be the reason why C arrays display
+ * correctly but get extra blank lines very other line when printed.
+ */
+frs_return_t
+follow_read_udp_stream(follow_info_t *follow_info,
+ gboolean (*print_line_fcn_p)(char *, size_t, gboolean, void *),
+ void *arg)
+{
+ guint32 global_client_pos = 0, global_server_pos = 0;
+ guint32 server_packet_count = 0;
+ guint32 client_packet_count = 0;
+ guint32 *global_pos;
+ gboolean skip;
+ GList* cur;
+ frs_return_t frs_return;
+ follow_record_t *follow_record;
+ char *buffer;
+
+
+ for (cur = follow_info->payload; cur; cur = g_list_next(cur)) {
+ follow_record = cur->data;
+ skip = FALSE;
+ if (!follow_record->is_server) {
+ global_pos = &global_client_pos;
+ if(follow_info->show_stream == FROM_SERVER) {
+ skip = TRUE;
+ }
+ } else {
+ global_pos = &global_server_pos;
+ if (follow_info->show_stream == FROM_CLIENT) {
+ skip = TRUE;
+ }
+ }
+
+ if (!skip) {
+ buffer = g_memdup(follow_record->data->data,
+ follow_record->data->len);
+
+ frs_return = follow_show(follow_info, print_line_fcn_p,
+ buffer,
+ follow_record->data->len,
+ follow_record->is_server, arg,
+ global_pos,
+ &server_packet_count,
+ &client_packet_count);
+ g_free(buffer);
+ if(frs_return == FRS_PRINT_ERROR)
+ return frs_return;
+ }
+ }
+
+ return FRS_OK;
+}
diff --git a/ui/gtk/follow_udp.h b/ui/gtk/follow_udp.h
new file mode 100644
index 0000000000..852e1c8392
--- /dev/null
+++ b/ui/gtk/follow_udp.h
@@ -0,0 +1,34 @@
+/* follow_udp.h
+ * UDP specific routines for following traffic streams
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __FOLLOW_UDP_H__
+#define __FOLLOW_UDP_H__
+
+/* Follow the UDP stream, if any, to which the last packet that we called
+ a dissection routine on belongs (this might be the most recently
+ selected packet, or it might be the last packet in the file). */
+void follow_udp_stream_cb(GtkWidget * w, gpointer data _U_);
+
+#endif /* __FOLLOW_UDP_H__ */
+
diff --git a/ui/gtk/font_utils.c b/ui/gtk/font_utils.c
new file mode 100644
index 0000000000..693087ed44
--- /dev/null
+++ b/ui/gtk/font_utils.c
@@ -0,0 +1,431 @@
+/* font_utils.c
+ * Utilities to use for font manipulation
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/prefs.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include <tchar.h>
+#include <wsutil/unicode-utils.h>
+#endif
+
+#include "../simple_dialog.h"
+
+#include "ui/gtk/main.h"
+#include "ui/gtk/recent.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/font_utils.h"
+#include "ui/gtk/main_proto_draw.h"
+#include "ui/gtk/follow_tcp.h"
+#include "ui/gtk/new_packet_list.h"
+
+
+static PangoFontDescription *m_r_font, *m_b_font;
+
+
+/* Get the regular user font.
+ *
+ * @return the regular user font
+ */
+PangoFontDescription *user_font_get_regular(void)
+{
+ return m_r_font;
+}
+
+/* Get the bold user font.
+ *
+ * @return the bold user font
+ */
+PangoFontDescription *user_font_get_bold(void)
+{
+ return m_b_font;
+}
+
+static void
+set_fonts(PangoFontDescription *regular, PangoFontDescription *bold)
+{
+ /* Yes, assert. The code that loads the font should check
+ * for NULL and provide its own error message. */
+ g_assert(m_r_font && m_b_font);
+ m_r_font = regular;
+ m_b_font = bold;
+}
+
+void
+view_zoom_in_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ gint save_gui_zoom_level;
+
+ save_gui_zoom_level = recent.gui_zoom_level;
+ recent.gui_zoom_level++;
+ switch (user_font_apply()) {
+
+ case FA_SUCCESS:
+ break;
+
+ case FA_FONT_NOT_RESIZEABLE:
+ /* "font_apply()" popped up an alert box. */
+ recent.gui_zoom_level = save_gui_zoom_level; /* undo zoom */
+ break;
+
+ case FA_FONT_NOT_AVAILABLE:
+ /* We assume this means that the specified size isn't available. */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Your current font isn't available in the next larger size.\n");
+ recent.gui_zoom_level = save_gui_zoom_level; /* undo zoom */
+ break;
+ }
+}
+
+void
+view_zoom_out_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ gint save_gui_zoom_level;
+
+ save_gui_zoom_level = recent.gui_zoom_level;
+ recent.gui_zoom_level--;
+ switch (user_font_apply()) {
+
+ case FA_SUCCESS:
+ break;
+
+ case FA_FONT_NOT_RESIZEABLE:
+ /* "font_apply()" popped up an alert box. */
+ recent.gui_zoom_level = save_gui_zoom_level; /* undo zoom */
+ break;
+
+ case FA_FONT_NOT_AVAILABLE:
+ /* We assume this means that the specified size isn't available. */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Your current font isn't available in the next smaller size.\n");
+ recent.gui_zoom_level = save_gui_zoom_level; /* undo zoom */
+ break;
+ }
+}
+
+void
+view_zoom_100_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ gint save_gui_zoom_level;
+
+ save_gui_zoom_level = recent.gui_zoom_level;
+ recent.gui_zoom_level = 0;
+ switch (user_font_apply()) {
+
+ case FA_SUCCESS:
+ break;
+
+ case FA_FONT_NOT_RESIZEABLE:
+ /* "font_apply()" popped up an alert box. */
+ recent.gui_zoom_level = save_gui_zoom_level; /* undo zoom */
+ break;
+
+ case FA_FONT_NOT_AVAILABLE:
+ /* We assume this means that the specified size isn't available.
+ XXX - this "shouldn't happen". */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Your current font couldn't be reloaded at the size you selected.\n");
+ recent.gui_zoom_level = save_gui_zoom_level; /* undo zoom */
+ break;
+ }
+}
+
+
+
+gboolean
+user_font_test(gchar *font_name)
+{
+ PangoFontDescription *new_r_font, *new_b_font;
+
+ new_r_font = pango_font_description_from_string(font_name);
+ if (new_r_font == NULL) {
+ /* Oops, that font didn't work.
+ Tell the user, but don't tear down the font selection
+ dialog, so that they can try again. */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "The font you selected can't be loaded.");
+
+ return FALSE;
+ }
+
+ new_b_font = pango_font_description_copy(new_r_font);
+ pango_font_description_set_weight(new_b_font, PANGO_WEIGHT_BOLD);
+ if (new_b_font == NULL) {
+ /* Oops, that font didn't work.
+ Tell the user, but don't tear down the font selection
+ dialog, so that they can try again. */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "The font you selected doesn't have a boldface version.");
+
+ pango_font_description_free(new_r_font);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+
+/* Given a font name, construct the name of a version of that font with
+ the current zoom factor applied. */
+static char *
+font_zoom(char *gui_font_name)
+{
+ char *new_font_name;
+ char *font_name_dup;
+ char *font_name_p;
+ long font_point_size_l;
+
+ if (recent.gui_zoom_level == 0) {
+ /* There is no zoom factor - just return the name, so that if
+ this is GTK+ 1.2[.x] and the font name isn't an XLFD font
+ name, we don't fail. */
+ return g_strdup(gui_font_name);
+ }
+
+ font_name_dup = g_strdup(gui_font_name);
+
+ /* find the start of the font_size string */
+ font_name_p = strrchr(font_name_dup, ' ');
+ *font_name_p = '\0';
+ font_name_p++;
+
+ /* calculate the new font size */
+ font_point_size_l = strtol(font_name_p, NULL, 10);
+ font_point_size_l += recent.gui_zoom_level;
+
+ /* build a new font name */
+ new_font_name = g_strdup_printf("%s %ld", font_name_dup, font_point_size_l);
+
+ g_free(font_name_dup);
+
+ return new_font_name;
+}
+
+fa_ret_t
+user_font_apply(void) {
+ char *gui_font_name;
+ PangoFontDescription *new_r_font, *new_b_font;
+ PangoFontDescription *old_r_font = NULL, *old_b_font = NULL;
+
+ /* convert font name to reflect the zoom level */
+ gui_font_name = font_zoom(prefs.gui_font_name);
+ if (gui_font_name == NULL) {
+ /*
+ * This means the font name isn't an XLFD font name.
+ * We just report that for now as a font not available in
+ * multiple sizes.
+ */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Your current font isn't available in any other sizes.\n");
+ return FA_FONT_NOT_RESIZEABLE;
+ }
+
+ /* load normal and bold font */
+ new_r_font = pango_font_description_from_string(gui_font_name);
+ new_b_font = pango_font_description_copy(new_r_font);
+ pango_font_description_set_weight(new_b_font, PANGO_WEIGHT_BOLD);
+
+ if (new_r_font == NULL || new_b_font == NULL) {
+ /* We're no longer using the new fonts; unreference them. */
+ if (new_r_font != NULL)
+ pango_font_description_free(new_r_font);
+ if (new_b_font != NULL)
+ pango_font_description_free(new_b_font);
+ g_free(gui_font_name);
+
+ /* We let our caller pop up a dialog box, as the error message
+ depends on the context (did they zoom in or out, or did they
+ do something else? */
+ return FA_FONT_NOT_AVAILABLE;
+ }
+
+ /* the font(s) seem to be ok */
+ new_packet_list_set_font(new_r_font);
+ set_ptree_font_all(new_r_font);
+ old_r_font = m_r_font;
+ old_b_font = m_b_font;
+ set_fonts(new_r_font, new_b_font);
+
+ /* Redraw the packet bytes windows. */
+ redraw_packet_bytes_all();
+
+ /* Redraw the "Follow TCP Stream" windows. */
+ follow_tcp_redraw_all();
+
+ /* We're no longer using the old fonts; unreference them. */
+ if (old_r_font != NULL)
+ pango_font_description_free(old_r_font);
+ if (old_b_font != NULL)
+ pango_font_description_free(old_b_font);
+ g_free(gui_font_name);
+
+ return FA_SUCCESS;
+}
+
+
+#ifdef _WIN32
+
+#define NAME_BUFFER_LEN 32
+
+static char appfontname[128] = "tahoma 8";
+
+static void
+set_app_font_gtk2(const char *fontname)
+{
+ GtkSettings *settings;
+
+ if (fontname != NULL && *fontname == 0) return;
+
+ settings = gtk_settings_get_default();
+
+ if (fontname == NULL) {
+ g_object_set(G_OBJECT(settings), "gtk-font-name", appfontname, NULL);
+ } else {
+ GtkWidget *w;
+ PangoFontDescription *pfd;
+ PangoContext *pc;
+ PangoFont *pfont;
+
+ w = gtk_label_new(NULL);
+ pfd = pango_font_description_from_string(fontname);
+ pc = gtk_widget_get_pango_context(w);
+ pfont = pango_context_load_font(pc, pfd);
+
+ if (pfont != NULL) {
+ g_strlcpy(appfontname, fontname, 128);
+ appfontname[127] = '\0';
+ g_object_set(G_OBJECT(settings), "gtk-font-name", appfontname, NULL);
+ }
+
+ gtk_widget_destroy(w);
+ pango_font_description_free(pfd);
+ }
+}
+
+static char *default_windows_menu_fontspec_gtk2(void)
+{
+ gchar *fontspec = NULL;
+ NONCLIENTMETRICS ncm;
+
+ memset(&ncm, 0, sizeof ncm);
+ ncm.cbSize = sizeof ncm;
+
+ if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0)) {
+ HDC screen = GetDC(0);
+ double y_scale = 72.0 / GetDeviceCaps(screen, LOGPIXELSY);
+ int point_size = (int) (ncm.lfMenuFont.lfHeight * y_scale);
+
+ if (point_size < 0) point_size = -point_size;
+ fontspec = g_strdup_printf("%s %d", ncm.lfMenuFont.lfFaceName,
+ point_size);
+ ReleaseDC(0, screen);
+ }
+
+ return fontspec;
+}
+
+static void try_to_get_windows_font_gtk2(void)
+{
+ gchar *fontspec;
+
+ fontspec = default_windows_menu_fontspec_gtk2();
+
+ if (fontspec != NULL) {
+ int match = 0;
+ PangoFontDescription *pfd;
+ PangoFont *pfont;
+ PangoContext *pc;
+ GtkWidget *w;
+
+ pfd = pango_font_description_from_string(fontspec);
+
+ w = gtk_label_new(NULL);
+ pc = gtk_widget_get_pango_context(w);
+ pfont = pango_context_load_font(pc, pfd);
+ match = (pfont != NULL);
+
+ pango_font_description_free(pfd);
+ g_object_unref(G_OBJECT(pc));
+ gtk_widget_destroy(w);
+
+ if (match) set_app_font_gtk2(fontspec);
+ g_free(fontspec);
+ }
+}
+#endif /* _WIN32 */
+
+
+void font_init(void)
+{
+#ifdef _WIN32
+ /* try to load the application font for GTK2 */
+ try_to_get_windows_font_gtk2();
+#endif
+
+ /* Try to load the regular and boldface fixed-width fonts */
+ m_r_font = pango_font_description_from_string(prefs.gui_font_name);
+ m_b_font = pango_font_description_copy(m_r_font);
+ pango_font_description_set_weight(m_b_font, PANGO_WEIGHT_BOLD);
+ if (m_r_font == NULL || m_b_font == NULL) {
+ /* XXX - pop this up as a dialog box? no */
+ if (m_r_font == NULL) {
+ fprintf(stderr, "wireshark: Warning: font %s not found - defaulting to Monospace 9\n",
+ prefs.gui_font_name);
+ } else {
+ pango_font_description_free(m_r_font);
+ }
+ if (m_b_font == NULL) {
+ fprintf(stderr, "wireshark: Warning: bold font %s not found - defaulting"
+ " to Monospace 9\n", prefs.gui_font_name);
+ } else {
+ pango_font_description_free(m_b_font);
+ }
+ if ((m_r_font = pango_font_description_from_string("Monospace 9")) == NULL)
+ {
+ fprintf(stderr, "wireshark: Error: font Monospace 9 not found\n");
+ exit(1);
+ }
+ if ((m_b_font = pango_font_description_copy(m_r_font)) == NULL) {
+ fprintf(stderr, "wireshark: Error: font Monospace 9 bold not found\n");
+ exit(1);
+ }
+ g_free(prefs.gui_font_name);
+ pango_font_description_set_weight(m_b_font, PANGO_WEIGHT_BOLD);
+ prefs.gui_font_name = g_strdup("Monospace 9");
+ }
+
+ /* Call this for the side-effects that set_fonts() produces */
+ set_fonts(m_r_font, m_b_font);
+}
diff --git a/ui/gtk/font_utils.h b/ui/gtk/font_utils.h
new file mode 100644
index 0000000000..d31338eb5e
--- /dev/null
+++ b/ui/gtk/font_utils.h
@@ -0,0 +1,75 @@
+/* font_utils.h
+ * Declarations of utilities to use for font manipulation
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+/** @file
+ * Utilities for font manipulation.
+ *
+ * There are two different fonts used:
+ * - the application font for menu's, dialog's and such
+ * - the user font for the packet panes
+ *
+ * The user font is also available in regular (m_r_font) and bold (m_b_font) versions.
+ */
+
+#ifndef __FONT_UTILS_H__
+#define __FONT_UTILS_H__
+
+/** Init the application and user fonts at program start. */
+extern void font_init(void);
+
+/** Return value from font_apply() */
+typedef enum {
+ FA_SUCCESS, /**< function succeeded */
+ FA_FONT_NOT_RESIZEABLE, /**< the chosen font isn't resizable */
+ FA_FONT_NOT_AVAILABLE /**< the chosen font isn't available */
+} fa_ret_t;
+
+/** Applies a new user font, corresponding to the preferences font name and recent zoom level.
+ * Will also redraw the screen.
+ *
+ * @return if the new font could be set or not
+ */
+extern fa_ret_t user_font_apply(void);
+
+/** Test, if the given font name is available.
+ *
+ * @param font_name the font to test
+ * @return TRUE, if this font is available
+ */
+extern gboolean user_font_test(gchar *font_name);
+
+/** Get the regular user font.
+ *
+ * @return the regular user font
+ */
+extern PangoFontDescription *user_font_get_regular(void);
+
+/** Get the bold user font.
+ *
+ * @return the bold user font
+ */
+extern PangoFontDescription *user_font_get_bold(void);
+
+#endif
diff --git a/ui/gtk/funnel_stat.c b/ui/gtk/funnel_stat.c
new file mode 100644
index 0000000000..afc1bb9871
--- /dev/null
+++ b/ui/gtk/funnel_stat.c
@@ -0,0 +1,668 @@
+/*
+ * funnel_stat.c
+ *
+ * EPAN's funneled GUI mini-API
+ *
+ * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Most of the code here has been harvested from other Wireshark gtk modules.
+ * most from prefs_dlg.c and about_dlg.c
+ *
+ * (From original checkin message:
+ * The funneled GUI mini API.
+ * A very reduced set of gui ops (by now just a text window)
+ * that can be funneled to dissectors (even plugins) via epan.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/prefs.h>
+#include <epan/funnel.h>
+
+#include "../timestats.h"
+#include "../simple_dialog.h"
+#include "../file.h"
+#include "../stat_menu.h"
+#include "../progress_dlg.h"
+#include "../color_filters.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/font_utils.h"
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/prefs_dlg.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/webbrowser.h"
+#include "ui/gtk/gtkglobals.h"
+
+
+struct _funnel_text_window_t {
+ GtkWidget* win;
+ GtkWidget* txt;
+ GtkWidget* button_hbox;
+ GtkWidget* bt_close;
+ text_win_close_cb_t close_cb;
+ void* close_data;
+ GPtrArray* buttons;
+};
+
+struct _funnel_tree_window_t {
+ GtkWidget *win;
+
+};
+
+struct _funnel_node_t {
+ void* dummy;
+};
+
+static void text_window_cancel_button_cb(GtkWidget *bt _U_, gpointer data) {
+ funnel_text_window_t* tw = data;
+
+ window_destroy(GTK_WIDGET(tw->win));
+ tw->win = NULL;
+
+ if (tw->close_cb)
+ tw->close_cb(tw->close_data);
+}
+
+static void unref_text_win_cancel_bt_cb(GtkWidget *bt _U_, gpointer data) {
+ funnel_text_window_t* tw = data;
+ unsigned i;
+
+ window_destroy(GTK_WIDGET(tw->win));
+ tw->win = NULL;
+
+ if (tw->close_cb)
+ tw->close_cb(tw->close_data);
+
+ for (i = 0; i < tw->buttons->len; i++) {
+ funnel_bt_t* cbd = g_ptr_array_index(tw->buttons,i);
+ /* XXX a free cb should be passed somehow */
+ if (cbd->data && cbd->free_data_fcn) cbd->free_data_fcn(cbd->data);
+ if (cbd->free_fcn) cbd->free_fcn(cbd);
+ }
+ g_ptr_array_free(tw->buttons,TRUE);
+ g_free(tw);
+}
+
+
+static gboolean text_window_unref_del_event_cb(GtkWidget *win _U_, GdkEvent *event _U_, gpointer user_data) {
+ funnel_text_window_t* tw = user_data;
+ unsigned i;
+
+ window_destroy(GTK_WIDGET(tw->win));
+ tw->win = NULL;
+
+ if (tw->close_cb)
+ tw->close_cb(tw->close_data);
+
+ for (i = 0; i < tw->buttons->len; i++) {
+ funnel_bt_t* cbd = g_ptr_array_index(tw->buttons,i);
+ /* XXX a free cb should be passed somehow */
+ if (cbd->data && cbd->free_data_fcn) cbd->free_data_fcn(cbd->data);
+ if (cbd->free_fcn) cbd->free_fcn(cbd);
+ }
+ g_ptr_array_free(tw->buttons,TRUE);
+ g_free(tw);
+
+ return TRUE;
+}
+
+static gboolean text_window_delete_event_cb(GtkWidget *win _U_, GdkEvent *event _U_, gpointer user_data)
+{
+ funnel_text_window_t* tw = user_data;
+
+ window_destroy(GTK_WIDGET(tw->win));
+ tw->win = NULL;
+
+ if (tw->close_cb)
+ tw->close_cb(tw->close_data);
+
+ return TRUE;
+}
+
+static funnel_text_window_t* new_text_window(const gchar* title) {
+ funnel_text_window_t* tw = g_malloc(sizeof(funnel_text_window_t));
+ GtkWidget *txt_scrollw, *main_vb, *hbox;
+
+ tw->close_cb = NULL;
+ tw->close_data = NULL;
+ tw->buttons = g_ptr_array_new();
+
+ tw->win = dlg_window_new(title); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(tw->win), TRUE);
+
+ g_signal_connect(tw->win, "delete-event", G_CALLBACK(text_window_delete_event_cb), tw);
+
+ txt_scrollw = scrolled_window_new(NULL, NULL);
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 6);
+ gtk_container_add(GTK_CONTAINER(tw->win), main_vb);
+
+ gtk_container_add(GTK_CONTAINER(main_vb), txt_scrollw);
+
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
+ GTK_SHADOW_IN);
+
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(txt_scrollw),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ tw->txt = gtk_text_view_new();
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(tw->txt), FALSE);
+ gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(tw->txt), GTK_WRAP_WORD);
+ gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(tw->txt), FALSE);
+
+ gtk_text_view_set_left_margin(GTK_TEXT_VIEW(tw->txt), 4);
+ gtk_text_view_set_right_margin(GTK_TEXT_VIEW(tw->txt), 4);
+
+ hbox = gtk_hbox_new(FALSE, 0);
+ gtk_widget_show(hbox);
+
+ tw->button_hbox = gtk_hbutton_box_new();
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(tw->button_hbox), GTK_BUTTONBOX_START);
+
+ gtk_box_pack_start(GTK_BOX(hbox), tw->button_hbox, TRUE, TRUE, 0);
+ gtk_widget_show(tw->button_hbox);
+
+ gtk_box_pack_start(GTK_BOX(main_vb), hbox, FALSE, FALSE, 0);
+
+ tw->bt_close = gtk_button_new_with_label("Close");
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(tw->bt_close, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(tw->bt_close, GTK_CAN_DEFAULT);
+#endif
+ g_object_set_data(G_OBJECT(hbox), "Close", tw->bt_close);
+
+ gtk_box_pack_end(GTK_BOX(hbox), tw->bt_close, FALSE, FALSE, 0);
+ gtk_widget_show(tw->bt_close);
+
+ g_signal_connect(tw->bt_close, "clicked", G_CALLBACK(text_window_cancel_button_cb), tw);
+ gtk_widget_grab_default(tw->bt_close);
+
+ gtk_container_add(GTK_CONTAINER(txt_scrollw), tw->txt);
+ gtk_window_resize(GTK_WINDOW(tw->win),400,300);
+ gtk_widget_show_all(tw->win);
+
+ return tw;
+}
+
+
+static void text_window_clear(funnel_text_window_t* tw)
+{
+ GtkTextBuffer *buf;
+
+ if (! tw->win) return;
+
+ buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(tw->txt));
+
+ gtk_text_buffer_set_text(buf, "", 0);
+}
+
+
+static void text_window_append(funnel_text_window_t* tw, const char *str)
+{
+ GtkWidget *txt;
+ int nchars;
+ GtkTextBuffer *buf;
+ GtkTextIter iter;
+
+ if (! tw->win) return;
+
+ txt = tw->txt;
+ nchars = (int) strlen(str);
+
+
+ buf= gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt));
+
+ gtk_text_buffer_get_end_iter(buf, &iter);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_font(GTK_WIDGET(txt), user_font_get_regular());
+#else
+ gtk_widget_modify_font(GTK_WIDGET(txt), user_font_get_regular());
+#endif
+ if (!g_utf8_validate(str, -1, NULL))
+ printf("Invalid utf8 encoding: %s\n", str);
+
+ gtk_text_buffer_insert(buf, &iter, str, nchars);
+}
+
+
+static void text_window_set_text(funnel_text_window_t* tw, const gchar* text)
+{
+ if (! tw->win) return;
+
+ text_window_clear(tw);
+ text_window_append(tw, text);
+}
+
+
+static void text_window_prepend(funnel_text_window_t* tw, const char *str _U_) {
+ GtkWidget *txt;
+ int nchars;
+ GtkTextBuffer *buf;
+ GtkTextIter iter;
+
+ if (! tw->win) return;
+
+ txt = tw->txt;
+ nchars = (int) strlen(str);
+
+
+ buf= gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt));
+
+ gtk_text_buffer_get_start_iter(buf, &iter);
+ #if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_font(GTK_WIDGET(txt), user_font_get_regular());
+#else
+ gtk_widget_modify_font(GTK_WIDGET(txt), user_font_get_regular());
+#endif
+ if (!g_utf8_validate(str, -1, NULL))
+ printf("Invalid utf8 encoding: %s\n", str);
+
+ gtk_text_buffer_insert(buf, &iter, str, nchars);
+}
+
+static const gchar* text_window_get_text(funnel_text_window_t* tw) {
+ GtkWidget *txt;
+ GtkTextBuffer *buf;
+ GtkTextIter start;
+ GtkTextIter end;
+
+ if (! tw->win) return "";
+
+ txt = tw->txt;
+
+ buf= gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt));
+ gtk_text_buffer_get_start_iter(buf, &start);
+ gtk_text_buffer_get_end_iter(buf, &end);
+
+ return gtk_text_buffer_get_text(buf, &start, &end, FALSE);
+}
+
+
+
+static void text_window_set_close_cb(funnel_text_window_t* tw, text_win_close_cb_t cb, void* data) {
+ tw->close_cb = cb;
+ tw->close_data = data;
+}
+
+static void text_window_destroy(funnel_text_window_t* tw) {
+ if (tw->win) {
+ /*
+ * the window is still there and its callbacks refer to this data structure
+ * we need to change the callback so that they free tw.
+ */
+ g_signal_connect(tw->bt_close, "clicked", G_CALLBACK(unref_text_win_cancel_bt_cb), tw);
+ g_signal_connect(tw->win, "delete-event", G_CALLBACK(text_window_unref_del_event_cb), tw);
+ } else {
+ unsigned i;
+ /*
+ * we have no window anymore a human user closed
+ * the window already just free the container
+ */
+ for (i = 0; i < tw->buttons->len; i++) {
+ funnel_bt_t* cbd = g_ptr_array_index(tw->buttons,i);
+ /* XXX a free cb should be passed somehow */
+ if (cbd->data && cbd->free_data_fcn) cbd->free_data_fcn(cbd->data);
+ if (cbd->free_fcn) cbd->free_fcn(cbd);
+ }
+ g_ptr_array_free(tw->buttons,TRUE);
+ g_free(tw);
+ }
+}
+
+static void text_window_set_editable(funnel_text_window_t* tw, gboolean editable){
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(tw->txt), editable);
+ gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(tw->txt), editable);
+}
+
+static gboolean text_window_button_cb(GtkWidget *bt _U_, gpointer user_data)
+{
+ funnel_bt_t* cbd = user_data;
+
+ if (cbd->func) {
+ return cbd->func(cbd->tw,cbd->data);
+ } else {
+ return TRUE;
+ }
+}
+
+static void text_window_add_button(funnel_text_window_t* tw, funnel_bt_t* cbd, const gchar* label) {
+ GtkWidget *button;
+
+ cbd->tw = tw;
+ g_ptr_array_add(tw->buttons,cbd);
+
+ button = gtk_button_new_with_label(label);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(button, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+#endif
+
+ gtk_box_pack_start(GTK_BOX(tw->button_hbox), button, FALSE, FALSE, 0);
+
+ gtk_widget_show(button);
+ g_signal_connect(button, "clicked", G_CALLBACK(text_window_button_cb), cbd);
+
+}
+
+
+struct _funnel_dlg_data {
+ GtkWidget* win;
+ GPtrArray* entries;
+ funnel_dlg_cb_t dlg_cb;
+ void* data;
+};
+
+static gboolean funnel_dlg_cb(GtkWidget *win _U_, gpointer user_data)
+{
+ struct _funnel_dlg_data* dd = user_data;
+ guint i;
+ guint len = dd->entries->len;
+ GPtrArray* returns = g_ptr_array_new();
+
+ for(i=0; i<len; i++) {
+ GtkEntry* entry = g_ptr_array_index(dd->entries,i);
+ g_ptr_array_add(returns,g_strdup(gtk_entry_get_text(entry)));
+ }
+
+ g_ptr_array_add(returns,NULL);
+
+ if (dd->dlg_cb)
+ dd->dlg_cb((gchar**)returns->pdata,dd->data);
+
+ window_destroy(GTK_WIDGET(dd->win));
+
+ g_ptr_array_free(returns,FALSE);
+
+ return TRUE;
+}
+
+static void funnel_cancel_btn_cb(GtkWidget *bt _U_, gpointer data) {
+ GtkWidget* win = data;
+
+ window_destroy(GTK_WIDGET(win));
+}
+
+static void funnel_new_dialog(const gchar* title,
+ const gchar** fieldnames,
+ funnel_dlg_cb_t dlg_cb,
+ void* data) {
+ GtkWidget *win, *main_tb, *main_vb, *bbox, *bt_cancel, *bt_ok;
+ guint i;
+ const gchar* fieldname;
+ struct _funnel_dlg_data* dd = g_malloc(sizeof(struct _funnel_dlg_data));
+
+ dd->entries = g_ptr_array_new();
+ dd->dlg_cb = dlg_cb;
+ dd->data = data;
+
+ for (i=0;fieldnames[i];i++);
+
+ win = dlg_window_new(title);
+
+ dd->win = win;
+
+ gtk_window_resize(GTK_WINDOW(win),400,10*(i+2));
+
+ main_vb = gtk_vbox_new(TRUE,5);
+ gtk_container_add(GTK_CONTAINER(win), main_vb);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 6);
+
+ main_tb = gtk_table_new(i+1, 2, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
+
+ for (i = 0; (fieldname = fieldnames[i]) ; i++) {
+ GtkWidget *entry, *label;
+
+ label = gtk_label_new(fieldname);
+ gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, i+1, i + 2);
+ gtk_widget_show(label);
+
+ entry = gtk_entry_new();
+ g_ptr_array_add(dd->entries,entry);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), entry, 1, 2, i+1, i + 2);
+ gtk_widget_show(entry);
+ }
+
+ bbox = dlg_button_row_new(GTK_STOCK_CANCEL,GTK_STOCK_OK, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+
+ bt_ok = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ g_signal_connect(bt_ok, "clicked", G_CALLBACK(funnel_dlg_cb), dd);
+ gtk_widget_grab_default(bt_ok);
+
+ bt_cancel = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ g_signal_connect(bt_cancel, "clicked", G_CALLBACK(funnel_cancel_btn_cb), win);
+ gtk_widget_grab_default(bt_cancel);
+
+ gtk_widget_show(main_tb);
+ gtk_widget_show(main_vb);
+ gtk_widget_show(win);
+}
+
+static void funnel_set_filter(const char* filter_string) {
+ gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), filter_string);
+}
+
+static void funnel_set_color_filter_slot(guint8 filt_nr, const gchar* filter_string) {
+ color_filters_set_tmp(filt_nr, (gchar *)filter_string, FALSE);
+}
+
+static void funnel_apply_filter(void) {
+ const char* filter_string = gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
+ main_filter_packets(&cfile, filter_string, FALSE);
+}
+
+/* XXX: finish this */
+static void funnel_logger(const gchar *log_domain _U_,
+ GLogLevelFlags log_level _U_,
+ const gchar *message,
+ gpointer user_data _U_) {
+ fputs(message,stderr);
+}
+
+static void funnel_retap_packets(void) {
+ cf_retap_packets(&cfile);
+}
+
+static gboolean funnel_open_file(const char* fname, const char* filter, const char** err_str) {
+ int err = 0;
+ dfilter_t *rfcode = NULL;
+
+ *err_str = "no error";
+
+ switch (cfile.state) {
+ case FILE_CLOSED:
+ case FILE_READ_DONE:
+ case FILE_READ_ABORTED:
+ break;
+ case FILE_READ_IN_PROGRESS:
+ *err_str = "file read in progress";
+ return FALSE;
+ }
+
+ if (filter) {
+ if (!dfilter_compile(filter, &rfcode)) {
+ *err_str = dfilter_error_msg ? dfilter_error_msg : "cannot compile filter";
+ return FALSE;
+ }
+ }
+
+
+ if (cf_open(&cfile, fname, FALSE, &err) != CF_OK) {
+ *err_str = g_strerror(err);
+ if (rfcode != NULL) dfilter_free(rfcode);
+ return FALSE;
+ }
+
+ cfile.rfcode = rfcode;
+
+ switch (cf_read(&cfile, FALSE)) {
+ case CF_READ_OK:
+ case CF_READ_ERROR:
+ break;
+ default:
+ *err_str = "problem while reading file";
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+typedef struct progdlg _funnel_progress_window_t;
+
+static funnel_progress_window_t* funnel_new_progress_window(const gchar* label, const gchar* task, gboolean terminate_is_stop, gboolean *stop_flag) {
+ return (funnel_progress_window_t*)create_progress_dlg(label, task, terminate_is_stop, stop_flag);
+}
+
+static void funnel_update_progress(funnel_progress_window_t* win, float pr, const gchar* task) {
+ update_progress_dlg((progdlg_t*)win, pr, task);
+}
+
+static void funnel_destroy_progress_window(funnel_progress_window_t* win) {
+ destroy_progress_dlg((progdlg_t*)win);
+}
+
+static void funnel_reload(void) {
+ if (cfile.state == FILE_READ_DONE) cf_reload(&cfile);
+}
+
+static const funnel_ops_t funnel_ops = {
+ new_text_window,
+ text_window_set_text,
+ text_window_append,
+ text_window_prepend,
+ text_window_clear,
+ text_window_get_text,
+ text_window_set_close_cb,
+ text_window_set_editable,
+ text_window_destroy,
+ text_window_add_button,
+ /*...,*/
+ funnel_new_dialog,
+ funnel_logger,
+ funnel_retap_packets,
+ copy_to_clipboard,
+ funnel_set_filter,
+ funnel_set_color_filter_slot,
+ funnel_open_file,
+ funnel_reload,
+ funnel_apply_filter,
+ browser_open_url,
+ browser_open_data_file,
+ funnel_new_progress_window,
+ funnel_update_progress,
+ funnel_destroy_progress_window
+};
+
+
+typedef struct _menu_cb_t {
+ void (*callback)(gpointer);
+ void* callback_data;
+ gboolean retap;
+} menu_cb_t;
+
+static void our_menu_callback(void* unused _U_, gpointer data) {
+ menu_cb_t* mcb = data;
+ mcb->callback(mcb->callback_data);
+ if (mcb->retap) cf_retap_packets(&cfile);
+}
+
+static const char* stat_group_name(register_stat_group_t group _U_) {
+ static const value_string VALS_GROUP_NAMES[] = {
+ {REGISTER_ANALYZE_GROUP_UNSORTED, "/Menubar/AnalyzeMenu|_Analyze"}, /* unsorted analyze stuff */
+ {REGISTER_ANALYZE_GROUP_CONVERSATION_FILTER, "/Menubar/AnalyzeMenu/ConversationFilterMenu|Conversation Filter"}, /* conversation filters */
+ {REGISTER_STAT_GROUP_UNSORTED, "/Menubar/StatisticsMenu|Statistics"}, /* unsorted statistic function */
+ {REGISTER_STAT_GROUP_GENERIC, "/Menubar/StatisticsMenu|Statistics"}, /* generic statistic function, not specific to a protocol */
+ {REGISTER_STAT_GROUP_CONVERSATION_LIST, "/Menubar/StatisticsMenu|Statistics/ConversationListMenu|_Conversation List"}, /* member of the conversation list */
+ {REGISTER_STAT_GROUP_ENDPOINT_LIST, "/Menubar/StatisticsMenu|Statistics/EndpointListMenu|_Endpoint List"}, /* member of the endpoint list */
+ {REGISTER_STAT_GROUP_RESPONSE_TIME, "/Menubar/StatisticsMenu|Statistics/ServiceResponseTimeMenu|Service _Response Time"}, /* member of the service response time list */
+ {REGISTER_STAT_GROUP_TELEPHONY, "/Menubar/TelephonyMenu|Telephon_y"}, /* telephony specific */
+ {REGISTER_TOOLS_GROUP_UNSORTED, "/Menubar/ToolsMenu|_Tools"}, /* unsorted tools */
+ { 0, NULL}
+ };
+ return val_to_str_const(group, VALS_GROUP_NAMES, "/Menubar/ToolsMenu|_Tools");
+}
+
+static void register_menu_cb(const char *name,
+ register_stat_group_t group _U_,
+ void (*callback)(gpointer),
+ gpointer callback_data,
+ gboolean retap) {
+
+ menu_cb_t* mcb = g_malloc(sizeof(menu_cb_t));
+ const char *label = NULL, *str = NULL;
+
+ mcb->callback = callback;
+ mcb->callback_data = callback_data;
+ mcb->retap = retap;
+
+ str = strrchr(name,'/');
+ if(str){
+ label = str+1;
+ }else{
+ label = name;
+ }
+
+ register_lua_menu_bar_menu_items(
+ stat_group_name(group), /* GUI path to the place holder in the menu */
+ name, /* Action name */
+ NULL, /* Stock id */
+ label, /* label */
+ NULL, /* Accelerator */
+ NULL, /* Tooltip */
+ our_menu_callback, /* Callback */
+ mcb, /* callback data */
+ TRUE, /* enabled */
+ NULL,
+ NULL);
+}
+
+void initialize_funnel_ops(void) {
+ funnel_set_funnel_ops(&funnel_ops);
+}
+
+void
+register_tap_listener_gtkfunnel(void)
+{
+ funnel_register_all_menus(register_menu_cb);
+}
diff --git a/ui/gtk/goto_dlg.c b/ui/gtk/goto_dlg.c
new file mode 100644
index 0000000000..a014c02895
--- /dev/null
+++ b/ui/gtk/goto_dlg.c
@@ -0,0 +1,171 @@
+/* goto_dlg.c
+ * Routines for "go to packet" window
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <epan/proto.h>
+
+#include "../globals.h"
+#include "../simple_dialog.h"
+#include "../ui_util.h"
+
+#include "ui/gtk/goto_dlg.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/help_dlg.h"
+
+
+/* Capture callback data keys */
+#define E_GOTO_FNUMBER_KEY "goto_fnumber_te"
+
+static void
+goto_frame_ok_cb(GtkWidget *ok_bt, gpointer parent_w);
+
+void
+goto_frame_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ GtkWidget *goto_frame_w, *main_vb, *fnumber_hb, *fnumber_lb, *fnumber_te,
+ *bbox, *ok_bt, *cancel_bt, *help_bt;
+
+ goto_frame_w = dlg_window_new("Wireshark: Go To Packet");
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(goto_frame_w), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* Frame number row */
+ fnumber_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(main_vb), fnumber_hb);
+ gtk_widget_show(fnumber_hb);
+
+ fnumber_lb = gtk_label_new("Packet number:");
+ gtk_box_pack_start(GTK_BOX(fnumber_hb), fnumber_lb, FALSE, FALSE, 0);
+ gtk_widget_show(fnumber_lb);
+
+ fnumber_te = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(fnumber_hb), fnumber_te, FALSE, FALSE, 0);
+ gtk_widget_show(fnumber_te);
+
+ /* Button row: OK and cancel buttons */
+ bbox = dlg_button_row_new(GTK_STOCK_JUMP_TO, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
+ gtk_container_add(GTK_CONTAINER(main_vb), bbox);
+ gtk_widget_show(bbox);
+
+ ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_JUMP_TO);
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(goto_frame_ok_cb), goto_frame_w);
+
+ cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ window_set_cancel_button(goto_frame_w, cancel_bt, window_cancel_button_cb);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_GOTO_DIALOG);
+
+ gtk_widget_grab_default(ok_bt);
+
+ /* Catch the "activate" signal on the frame number text entry, so that
+ if the user types Return there, we act as if the "OK" button
+ had been selected, as happens if Return is typed if some widget
+ that *doesn't* handle the Return key has the input focus. */
+ dlg_set_activate(fnumber_te, ok_bt);
+
+ /* Give the initial focus to the "Packet number" entry box. */
+ gtk_widget_grab_focus(fnumber_te);
+
+ /* Attach pointers to needed widgets to the capture prefs window/object */
+ g_object_set_data(G_OBJECT(goto_frame_w), E_GOTO_FNUMBER_KEY, fnumber_te);
+
+ g_signal_connect(goto_frame_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+
+ gtk_widget_show(goto_frame_w);
+ window_present(goto_frame_w);
+}
+
+static void
+goto_frame_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w)
+{
+ GtkWidget *fnumber_te;
+ const gchar *fnumber_text;
+ guint fnumber;
+ char *p;
+
+ fnumber_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_GOTO_FNUMBER_KEY);
+
+ fnumber_text = gtk_entry_get_text(GTK_ENTRY(fnumber_te));
+ fnumber = strtoul(fnumber_text, &p, 10);
+ if (p == fnumber_text || *p != '\0') {
+ /* Illegal number.
+ XXX - what about negative numbers (which "strtoul()" allows)?
+ Can we hack up signal handlers for the widget to make it
+ reject attempts to type in characters other than digits? */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "The packet number you entered isn't a valid number.");
+ return;
+ }
+
+ if (cf_goto_frame(&cfile, fnumber)) {
+ /* We succeeded in going to that frame; we're done. */
+ window_destroy(GTK_WIDGET(parent_w));
+ }
+}
+
+/*
+ * Go to frame specified by currently selected protocol tree item.
+ */
+void
+goto_framenum_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ cf_goto_framenum(&cfile);
+}
+
+void
+goto_top_frame_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ cf_goto_top_frame();
+}
+
+void
+goto_bottom_frame_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ cf_goto_bottom_frame();
+}
+
+void
+goto_next_frame_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ new_packet_list_next();
+}
+
+void
+goto_previous_frame_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ new_packet_list_prev();
+}
+
diff --git a/ui/gtk/goto_dlg.h b/ui/gtk/goto_dlg.h
new file mode 100644
index 0000000000..3a50a99cf5
--- /dev/null
+++ b/ui/gtk/goto_dlg.h
@@ -0,0 +1,76 @@
+/* goto_dlg.h
+ * Definitions for "go to frame" window
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GOTO_DLG_H__
+#define __GOTO_DLG_H__
+
+/** @file
+ * "Go To" dialog box and similar functions.
+ * @ingroup dialog_group
+ */
+
+/** User requested the "Go To" dialog box by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void goto_frame_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Go To Corresponding Packet" by menu.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void goto_framenum_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Go To First Packet" by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void goto_top_frame_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Go To Last Packet" by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void goto_bottom_frame_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Go To Next Packet" by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void goto_next_frame_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Go To Previous Packet" by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void goto_previous_frame_cb(GtkWidget *widget, gpointer data);
+
+#endif /* goto_dlg.h */
diff --git a/ui/gtk/graph_analysis.c b/ui/gtk/graph_analysis.c
new file mode 100644
index 0000000000..430927403e
--- /dev/null
+++ b/ui/gtk/graph_analysis.c
@@ -0,0 +1,2109 @@
+ /* graph_analysis.c
+ * Graphic Analysis addition for Wireshark
+ *
+ * $Id$
+ *
+ * Copyright 2004, Verso Technologies Inc.
+ * By Alejandro Vaquero <alejandrovaquero@yahoo.com>
+ *
+ * based on rtp_analysis.c and io_stat
+ *
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#if GTK_CHECK_VERSION(3,0,0)
+# include <gdk/gdkkeysyms-compat.h>
+#endif
+
+#include <epan/epan_dissect.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-rtp.h>
+#include <epan/addr_resolv.h>
+#include <epan/filesystem.h>
+
+#include "../util.h"
+#include "../simple_dialog.h"
+#include "../alert_box.h"
+#include <wsutil/file_util.h>
+
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/file_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/graph_analysis.h"
+#include "ui/gtk/recent.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+#include "../image/voip_bg.xpm"
+
+/****************************************************************************/
+
+static GtkWidget *save_to_file_w = NULL;
+
+#define MAX_LABEL 50
+#define MAX_COMMENT 100
+#define ITEM_HEIGHT 20
+#define NODE_WIDTH 100
+#define TOP_Y_BORDER 40
+#define BOTTOM_Y_BORDER 2
+#define COMMENT_WIDTH 400
+#define TIME_WIDTH 50
+
+#define NODE_CHARS_WIDTH 20
+#define CONV_TIME_HEADER "Conv.| Time "
+#define TIME_HEADER "|Time "
+#define CONV_TIME_EMPTY_HEADER " | "
+#define TIME_EMPTY_HEADER "| "
+#define CONV_TIME_HEADER_LENGTH 16
+#define TIME_HEADER_LENGTH 10
+
+/****************************************************************************/
+/* Reset the user_data structure */
+static void graph_analysis_reset(graph_analysis_data_t *user_data)
+{
+ int i;
+
+ user_data->num_nodes = 0;
+ user_data->num_items = 0;
+ for (i=0; i<MAX_NUM_NODES; i++){
+ user_data->nodes[i].type = AT_NONE;
+ user_data->nodes[i].len = 0;
+ g_free((void *)user_data->nodes[i].data);
+ user_data->nodes[i].data = NULL;
+ }
+
+ user_data->dlg.first_node=0;
+ user_data->dlg.first_item=0;
+ user_data->dlg.left_x_border=0;
+ user_data->dlg.selected_item=0xFFFFFFFF; /*not item selected */
+}
+
+/****************************************************************************/
+/* Init the user_data structure */
+static void graph_analysis_init_dlg(graph_analysis_data_t *user_data)
+{
+ int i;
+ user_data->num_nodes = 0;
+ user_data->num_items = 0;
+ user_data->on_destroy_user_data = NULL;
+ user_data->data = NULL;
+ for (i=0; i<MAX_NUM_NODES; i++){
+ user_data->nodes[i].type = AT_NONE;
+ user_data->nodes[i].len = 0;
+ user_data->nodes[i].data = NULL;
+ }
+
+ user_data->dlg.first_node=0;
+ user_data->dlg.first_item=0;
+ user_data->dlg.left_x_border=0;
+ user_data->dlg.selected_item=0xFFFFFFFF; /*not item selected */
+ /* init dialog_graph */
+ user_data->dlg.needs_redraw=TRUE;
+ user_data->dlg.draw_area_time=NULL;
+ user_data->dlg.draw_area=NULL;
+ user_data->dlg.draw_area_comments=NULL;
+#if GTK_CHECK_VERSION(2,22,0)
+ user_data->dlg.surface_main=NULL;
+ user_data->dlg.surface_time=NULL;
+ user_data->dlg.surface_comments=NULL;
+#else
+ user_data->dlg.pixmap_main=NULL;
+ user_data->dlg.pixmap_time=NULL;
+ user_data->dlg.pixmap_comments=NULL;
+#endif
+ user_data->dlg.v_scrollbar=NULL;
+ user_data->dlg.v_scrollbar_adjustment=NULL;
+ user_data->dlg.hpane=NULL;
+ user_data->dlg.surface_width=350;
+ user_data->dlg.surface_height=400;
+ user_data->dlg.first_node=0;
+ user_data->dlg.first_item=0;
+ user_data->dlg.left_x_border=0;
+ user_data->dlg.selected_item=0xFFFFFFFF; /*not item selected */
+ user_data->dlg.window=NULL;
+ user_data->dlg.parent_w=NULL;
+ user_data->dlg.inverse = FALSE;
+ user_data->dlg.title=NULL;
+}
+
+/****************************************************************************/
+/* CALLBACKS */
+
+/****************************************************************************/
+/* close the dialog window */
+static void on_destroy(GtkWidget *win _U_, graph_analysis_data_t *user_data)
+{
+ int i;
+
+ for (i=0; i<MAX_NUM_NODES; i++){
+ user_data->nodes[i].type = AT_NONE;
+ user_data->nodes[i].len = 0;
+ g_free((void *)user_data->nodes[i].data);
+ user_data->nodes[i].data = NULL;
+ }
+ user_data->dlg.window = NULL;
+ g_free(user_data->dlg.title);
+ user_data->dlg.title = NULL;
+
+ if(user_data->on_destroy_user_data){
+ user_data->on_destroy_user_data(user_data->data);
+ }
+}
+
+#define RIGHT_ARROW 1
+#define LEFT_ARROW 0
+#define WIDTH_ARROW 8
+#define HEIGHT_ARROW 6
+
+/****************************************************************************/
+#if GTK_CHECK_VERSION(2,22,0)
+static void draw_arrow(cairo_surface_t *surface, GdkColor *color, gint x, gint y, gboolean arrow_type)
+{
+ cairo_t *cr;
+
+ cr = cairo_create (surface);
+ gdk_cairo_set_source_color (cr, color);
+ if (arrow_type == LEFT_ARROW)
+ {
+ cairo_move_to (cr, x + WIDTH_ARROW, y);
+ cairo_line_to (cr, x + WIDTH_ARROW, y + HEIGHT_ARROW);
+ cairo_line_to (cr, x, y + HEIGHT_ARROW / 2.);
+ }
+ else if (arrow_type == RIGHT_ARROW)
+ {
+ cairo_move_to (cr, x, y);
+ cairo_line_to (cr, x + WIDTH_ARROW, y + HEIGHT_ARROW / 2.);
+ cairo_line_to (cr, x, y + HEIGHT_ARROW);
+ }
+ cairo_close_path (cr);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+}
+#else
+static void draw_arrow(GdkDrawable *pixmap, GdkColor *color, gint x, gint y, gboolean arrow_type)
+{
+ cairo_t *cr;
+
+ if (GDK_IS_DRAWABLE(pixmap)) {
+ cr = gdk_cairo_create (pixmap);
+ gdk_cairo_set_source_color (cr, color);
+ if (arrow_type == LEFT_ARROW)
+ {
+ cairo_move_to (cr, x + WIDTH_ARROW, y);
+ cairo_line_to (cr, x + WIDTH_ARROW, y + HEIGHT_ARROW);
+ cairo_line_to (cr, x, y + HEIGHT_ARROW / 2.);
+ }
+ else if (arrow_type == RIGHT_ARROW)
+ {
+ cairo_move_to (cr, x, y);
+ cairo_line_to (cr, x + WIDTH_ARROW, y + HEIGHT_ARROW / 2.);
+ cairo_line_to (cr, x, y + HEIGHT_ARROW);
+ }
+ cairo_close_path (cr);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+ }
+}
+#endif
+
+/****************************************************************************/
+/* Adds trailing characters to complete the requested length. */
+/****************************************************************************/
+
+static void enlarge_string(GString *gstr, guint32 length, char pad){
+
+ gsize i;
+
+ for (i = gstr->len; i < length; i++){
+ g_string_append_c(gstr, pad);
+ }
+}
+
+/****************************************************************************/
+/* overwrites the characters in a string, between positions p1 and p2, with */
+/* the characters of text_to_insert */
+/* NB: it does not check that p1 and p2 fit into string */
+/****************************************************************************/
+
+static void overwrite (GString *gstr, char *text_to_insert, guint32 p1, guint32 p2){
+
+ gsize len;
+ gsize pos;
+
+ if (p1 == p2)
+ return;
+
+ if (p1 > p2){
+ pos = p2;
+ len = p1 - p2;
+ }
+ else{
+ pos = p1;
+ len = p2 - p1;
+ }
+
+ if (len > strlen(text_to_insert)){
+ len = strlen(text_to_insert);
+ }
+
+ if (pos > gstr->len)
+ pos = gstr->len;
+
+ /* ouch this is ugly but gtk1 needs it */
+ if ((pos + len) > gstr->len)
+ g_string_truncate(gstr, pos);
+ else
+ g_string_erase(gstr, pos, len);
+
+ g_string_insert(gstr, pos, text_to_insert);
+}
+
+/****************************************************************************/
+static gboolean dialog_graph_dump_to_file(graph_analysis_data_t *user_data)
+{
+ guint32 i, first_node, display_items, display_nodes;
+ guint32 start_position, end_position, item_width, header_length;
+ graph_analysis_item_t *gai;
+ guint16 first_conv_num = 0;
+ gboolean several_convs = FALSE;
+ gboolean first_packet = TRUE;
+
+ GString *label_string, *empty_line,*separator_line, *tmp_str, *tmp_str2;
+ char *empty_header;
+ char src_port[8],dst_port[8];
+#if 0
+ gchar *time_str = g_malloc(COL_MAX_LEN);
+#endif
+ GList *list;
+
+ FILE *of;
+
+ of = ws_fopen(user_data->dlg.save_file,"w");
+ if (of==NULL){
+ open_failure_alert_box(user_data->dlg.save_file, errno, TRUE);
+ return FALSE;
+ }
+
+ label_string = g_string_new("");
+ empty_line = g_string_new("");
+ separator_line = g_string_new("");
+ tmp_str = g_string_new("");
+ tmp_str2 = g_string_new("");
+
+ display_items = 0;
+ list = g_list_first(user_data->graph_info->list);
+ while (list)
+ {
+ gai = list->data;
+ list = g_list_next(list);
+
+ if (!gai->display)
+ continue;
+
+ display_items += 1;
+ if (first_packet){
+ first_conv_num = gai->conv_num;
+ first_packet=FALSE;
+ }
+ else if (gai->conv_num != first_conv_num){
+ several_convs = TRUE;
+ }
+ }
+
+ /* if not items to display */
+ if (display_items == 0)
+ goto exit;
+
+ display_nodes = user_data->num_nodes;
+
+ first_node = user_data->dlg.first_node;
+
+ /* Write the conv. and time headers */
+ if (several_convs){
+ fprintf(of, CONV_TIME_HEADER);
+ empty_header = CONV_TIME_EMPTY_HEADER;
+ header_length = CONV_TIME_HEADER_LENGTH;
+ }
+ else{
+ fprintf(of, TIME_HEADER);
+ empty_header = TIME_EMPTY_HEADER;
+ header_length = TIME_HEADER_LENGTH;
+ }
+
+ /* Write the node names on top */
+ for (i=0; i<display_nodes; i+=2){
+ /* print the node identifiers */
+ g_string_printf(label_string, "| %s",
+ get_addr_name(&(user_data->nodes[i+first_node])));
+ enlarge_string(label_string, NODE_CHARS_WIDTH*2, ' ');
+ fprintf(of, "%s", label_string->str);
+ g_string_printf(label_string, "| ");
+ enlarge_string(label_string, NODE_CHARS_WIDTH, ' ');
+ g_string_append(empty_line, label_string->str);
+ }
+
+ fprintf(of, "|\n%s", empty_header);
+ g_string_printf(label_string, "| ");
+ enlarge_string(label_string, NODE_CHARS_WIDTH, ' ');
+ fprintf(of, "%s", label_string->str);
+
+ /* Write the node names on top */
+ for (i=1; i<display_nodes; i+=2){
+ /* print the node identifiers */
+ g_string_printf(label_string, "| %s",
+ get_addr_name(&(user_data->nodes[i+first_node])));
+ if (label_string->len < NODE_CHARS_WIDTH)
+ {
+ enlarge_string(label_string, NODE_CHARS_WIDTH, ' ');
+ g_string_append(label_string, "| ");
+ }
+ enlarge_string(label_string, NODE_CHARS_WIDTH*2, ' ');
+ fprintf(of, "%s", label_string->str);
+ g_string_printf(label_string, "| ");
+ enlarge_string(label_string, NODE_CHARS_WIDTH, ' ');
+ g_string_append(empty_line, label_string->str);
+ }
+
+ fprintf(of, "\n");
+
+ g_string_append_c(empty_line, '|');
+
+ enlarge_string(separator_line, (guint32) empty_line->len + header_length, '-');
+
+ /*
+ * Draw the items
+ */
+
+ list = g_list_first(user_data->graph_info->list);
+ while (list)
+ {
+ gai = list->data;
+ list = g_list_next(list);
+
+ if (!gai->display)
+ continue;
+
+ start_position = (gai->src_node-first_node)*NODE_CHARS_WIDTH+NODE_CHARS_WIDTH/2;
+
+ end_position = (gai->dst_node-first_node)*NODE_CHARS_WIDTH+NODE_CHARS_WIDTH/2;
+
+ if (start_position > end_position){
+ item_width=start_position-end_position;
+ }
+ else if (start_position < end_position){
+ item_width=end_position-start_position;
+ }
+ else{ /* same origin and destination address */
+ end_position = start_position+NODE_CHARS_WIDTH;
+ item_width = NODE_CHARS_WIDTH;
+ }
+
+ /* separator between conversations */
+ if (gai->conv_num != first_conv_num){
+ fprintf(of, "%s\n", separator_line->str);
+ first_conv_num=gai->conv_num;
+ }
+
+ /* write the conversation number */
+ if (several_convs){
+ g_string_printf(label_string, "%i", gai->conv_num);
+ enlarge_string(label_string, 5, ' ');
+ fprintf(of, "%s", label_string->str);
+ }
+
+ /* write the time */
+ g_string_printf(label_string, "|%.3f", nstime_to_sec(&gai->fd->rel_ts));
+#if 0
+ /* Write the time, using the same format as in th etime col */
+ set_fd_time(gai->fd, time_str);
+ g_string_printf(label_string, "|%s", time_str);
+#endif
+ enlarge_string(label_string, 10, ' ');
+ fprintf(of, "%s", label_string->str);
+
+ /* write the frame label */
+
+ g_string_printf(tmp_str, "%s", empty_line->str);
+ overwrite(tmp_str,gai->frame_label,
+ start_position,
+ end_position
+ );
+ fprintf(of, "%s", tmp_str->str);
+
+ /* write the comments */
+ fprintf(of, "%s\n", gai->comment);
+
+ /* write the arrow and frame label*/
+ fprintf(of, "%s", empty_header);
+
+ g_string_printf(tmp_str, "%s", empty_line->str);
+
+ g_string_truncate(tmp_str2, 0);
+
+ if (start_position<end_position){
+ enlarge_string(tmp_str2, item_width-2, '-');
+ g_string_append_c(tmp_str2, '>');
+ }
+ else{
+ g_string_printf(tmp_str2, "<");
+ enlarge_string(tmp_str2, item_width-1, '-');
+ }
+
+ overwrite(tmp_str,tmp_str2->str,
+ start_position,
+ end_position
+ );
+
+ g_snprintf(src_port,sizeof(src_port),"(%i)", gai->port_src);
+ g_snprintf(dst_port,sizeof(dst_port),"(%i)", gai->port_dst);
+
+ if (start_position<end_position){
+ overwrite(tmp_str,src_port,start_position-9,start_position-1);
+ overwrite(tmp_str,dst_port,end_position+1,end_position+9);
+ }
+ else{
+ overwrite(tmp_str,src_port,start_position+1,start_position+9);
+ overwrite(tmp_str,dst_port,end_position-9,end_position+1);
+ }
+
+ fprintf(of,"%s\n",tmp_str->str);
+ }
+
+exit:
+ g_string_free(label_string, TRUE);
+ g_string_free(empty_line, TRUE);
+ g_string_free(separator_line, TRUE);
+ g_string_free(tmp_str, TRUE);
+ g_string_free(tmp_str2, TRUE);
+#if 0
+ g_free(time_str);
+#endif
+ fclose (of);
+ return TRUE;
+
+}
+
+/****************************************************************************/
+static void save_to_file_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+ /* Note that we no longer have a Save to file dialog box. */
+ save_to_file_w = NULL;
+}
+
+/****************************************************************************/
+/* save in a file */
+
+/* first an auxiliary function in case we need an overwrite confirmation dialog */
+
+static void overwrite_existing_file_cb(gpointer dialog _U_, gint btn, gpointer user_data)
+{
+ switch(btn) {
+ case(ESD_BTN_YES):
+ /* overwrite the file*/
+ dialog_graph_dump_to_file(user_data);
+ break;
+ case(ESD_BTN_NO):
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+/* and then the save in a file dialog itself */
+
+static gboolean save_to_file_ok_cb(GtkWidget *ok_bt _U_, gpointer user_data)
+{
+ FILE *file_test;
+ graph_analysis_data_t *user_data_p = user_data;
+
+ user_data_p->dlg.save_file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(save_to_file_w));
+
+ /* Perhaps the user specified a directory instead of a file.
+ Check whether they did. */
+ if (test_for_directory(user_data_p->dlg.save_file) == EISDIR) {
+ /* It's a directory - set the file selection box to display it. */
+ set_last_open_dir(user_data_p->dlg.save_file);
+ file_selection_set_current_folder(save_to_file_w, get_last_open_dir());
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_to_file_w), "");
+ g_free(user_data_p->dlg.save_file);
+ return FALSE; /* run the dialog again */
+ }
+
+ /* GtkFileChooserDialog/gtk_dialog_run is currently being used. */
+ /* So: Trying to leave the graph_analysis window up if graph_dump */
+ /* fails doesn't work well. */
+ /* (See comment under on_save_bt_clicked) */
+ /* */
+ /* As a work-around: */
+ /* We'll always destroy the window. */
+
+ /* check whether the file exists */
+ file_test = ws_fopen(user_data_p->dlg.save_file,"r");
+ if (file_test!=NULL){
+ gpointer dialog;
+ dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_YES_NO,
+ "%sFile: \"%s\" already exists!%s\n\n"
+ "Do you want to overwrite it?",
+ simple_dialog_primary_start(),user_data_p->dlg.save_file, simple_dialog_primary_end());
+ simple_dialog_set_cb(dialog, overwrite_existing_file_cb, user_data);
+ fclose(file_test);
+ return TRUE;
+ }
+
+ else{
+ if (!dialog_graph_dump_to_file(user_data)) {
+ /* Couldn't open the file ? */
+ g_free(user_data_p->dlg.save_file);
+ return TRUE;
+ }
+ }
+ g_free(user_data_p->dlg.save_file);
+ return TRUE;
+}
+
+/****************************************************************************/
+static void
+on_save_bt_clicked (GtkWidget *button _U_,
+ graph_analysis_data_t *user_data)
+{
+#if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
+ if (save_to_file_w != NULL) {
+ /* There's already a Save to file dialog box; reactivate it. */
+ reactivate_window(save_to_file_w);
+ return;
+ }
+#endif
+ save_to_file_w =
+ gtk_file_chooser_dialog_new("Wireshark: Save graph to plain text file",
+ GTK_WINDOW(user_data->dlg.window),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NULL);
+
+ g_signal_connect(save_to_file_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(save_to_file_w, "destroy", G_CALLBACK(save_to_file_destroy_cb), NULL);
+
+ gtk_widget_show(save_to_file_w);
+ window_present(save_to_file_w);
+
+ /* "Run" the GtkFileChooserDialog. */
+ /* Upon exit: If "Accept" run the OK callback. */
+ /* If the OK callback returns with a FALSE status, re-run the dialog.*/
+ /* Destroy the window. */
+ /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
+ /* return with a TRUE status so that the dialog window will be destroyed. */
+ /* Trying to re-run the dialog after popping up an alert box will not work */
+ /* since the user will not be able to dismiss the alert box. */
+ /* The (somewhat unfriendly) effect: the user must re-invoke the */
+ /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
+ /* */
+ /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
+ /* GtkFileChooserDialog. */
+ while (gtk_dialog_run(GTK_DIALOG(save_to_file_w)) == GTK_RESPONSE_ACCEPT) {
+ if (save_to_file_ok_cb(NULL, user_data)) {
+ break; /* we're done */
+ }
+ }
+ window_destroy(save_to_file_w);
+}
+
+/****************************************************************************/
+static void dialog_graph_draw(graph_analysis_data_t *user_data)
+{
+ guint32 i, last_item, first_item, display_items;
+ guint32 start_arrow, end_arrow, label_x, src_port_x, dst_port_x, arrow_width;
+ guint32 current_item;
+ guint32 left_x_border;
+ guint32 right_x_border;
+ guint32 top_y_border;
+ guint32 bottom_y_border;
+ graph_analysis_item_t *gai;
+
+ PangoLayout *layout;
+ PangoLayout *middle_layout;
+ PangoLayout *small_layout;
+ PangoFontDescription *middle_font_desc;
+ gint middle_font_size;
+ PangoFontDescription *small_font_desc;
+ gint small_font_size;
+
+ gint label_width, label_height;
+ guint32 draw_width, draw_height;
+ char label_string[MAX_COMMENT];
+ GList *list;
+ cairo_t *cr;
+#if 0
+ gchar *time_str = g_malloc(COL_MAX_LEN);
+#endif
+
+ GdkColor *color_p, *bg_color_p;
+ GdkColor black_color = {0, 0, 0, 0};
+ GdkColor white_color = {0, 0xffff, 0xffff, 0xffff};
+ /* gray and soft gray colors */
+ GdkColor grey_color0 = {0, 0x64ff, 0x64ff, 0x64ff};
+ GdkColor grey_color1 = {0, 0x25ff, 0x25ff, 0x25ff};
+
+ /* the first color is blue to highlight the selected item */
+ static GdkColor background_color[MAX_NUM_COL_CONV+1] = {
+ {0, 0x00FF, 0x00FF, 0xFFFF},
+ {0, 0x90FF, 0xEEFF, 0x90FF},
+ {0, 0xFFFF, 0xA0FF, 0x7AFF},
+ {0, 0xFFFF, 0xB6FF, 0xC1FF},
+ {0, 0xFAFF, 0xFAFF, 0xD2FF},
+ {0, 0xFFFF, 0xFFFF, 0x33FF},
+ {0, 0x66FF, 0xCDFF, 0xAAFF},
+ {0, 0xE0FF, 0xFFFF, 0xFFFF},
+ {0, 0xB0FF, 0xC4FF, 0xDEFF},
+ {0, 0x87FF, 0xCEFF, 0xFAFF},
+ {0, 0xD3FF, 0xD3FF, 0xD3FF}
+ };
+
+ /* XXX can't we just set the background color ? */
+ GdkPixbuf *bg_pixbuf = gdk_pixbuf_new_from_xpm_data(voip_bg_xpm);
+
+ /* Dashed line pattern */
+ static const double dashed1[] = {5.0, 4.0};
+ static int len1 = sizeof(dashed1) / sizeof(dashed1[0]);
+
+ GtkAllocation draw_area_time_alloc, draw_area_alloc, draw_area_comments_alloc;
+
+ if(!user_data->dlg.needs_redraw){
+ return;
+ }
+ user_data->dlg.needs_redraw=FALSE;
+
+ gtk_widget_get_allocation(user_data->dlg.draw_area_time, &draw_area_time_alloc);
+ gtk_widget_get_allocation(user_data->dlg.draw_area, &draw_area_alloc);
+ gtk_widget_get_allocation(user_data->dlg.draw_area_comments, &draw_area_comments_alloc);
+
+ /* Clear out old plot */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.surface_time);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_rectangle (cr, 0, 0, draw_area_time_alloc.width,draw_area_time_alloc.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+ cr = cairo_create (user_data->dlg.surface_main);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_rectangle (cr, 0, 0, draw_area_alloc.width,draw_area_alloc.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+ cr = cairo_create (user_data->dlg.surface_comments);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_rectangle (cr, 0, 0, draw_area_comments_alloc.width,draw_area_comments_alloc.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+#else
+ if ( GDK_IS_DRAWABLE(user_data->dlg.pixmap_time) ){
+ cr = gdk_cairo_create (user_data->dlg.pixmap_time);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_rectangle (cr, 0, 0, draw_area_time_alloc.width,draw_area_time_alloc.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ }
+
+ if ( GDK_IS_DRAWABLE(user_data->dlg.pixmap_main) ){
+ cr = gdk_cairo_create (user_data->dlg.pixmap_main);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_rectangle (cr, 0, 0, draw_area_alloc.width,draw_area_alloc.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ }
+
+ if ( GDK_IS_DRAWABLE(user_data->dlg.pixmap_comments) ){
+ cr = gdk_cairo_create (user_data->dlg.pixmap_comments);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_rectangle (cr, 0, 0, draw_area_comments_alloc.width,draw_area_comments_alloc.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ }
+#endif
+ /* Calculate the y border */
+ top_y_border=TOP_Y_BORDER; /* to display the node address */
+ bottom_y_border=BOTTOM_Y_BORDER;
+
+ draw_height=draw_area_alloc.height-top_y_border-bottom_y_border;
+
+ first_item = user_data->dlg.first_item;
+ display_items = draw_height/ITEM_HEIGHT;
+
+ /* get the items to display and fill the matrix array */
+ list = g_list_first(user_data->graph_info->list);
+ current_item = 0;
+ i = 0;
+ while (list)
+ {
+ gai = list->data;
+ if (gai->display){
+ if (current_item>=display_items) break; /* the item is outside the display */
+ if (i>=first_item){
+ user_data->dlg.items[current_item].fd = gai->fd;
+ user_data->dlg.items[current_item].port_src = gai->port_src;
+ user_data->dlg.items[current_item].port_dst = gai->port_dst;
+ /* Add "..." if the length is 50 characters */
+ if (strlen(gai->frame_label) > 48) {
+ gai->frame_label[48] = '.';
+ gai->frame_label[47] = '.';
+ gai->frame_label[46] = '.';
+ }
+ user_data->dlg.items[current_item].frame_label = gai->frame_label;
+ user_data->dlg.items[current_item].comment = gai->comment;
+ user_data->dlg.items[current_item].conv_num = gai->conv_num;
+
+ user_data->dlg.items[current_item].src_node = gai->src_node;
+ user_data->dlg.items[current_item].dst_node = gai->dst_node;
+ user_data->dlg.items[current_item].line_style = gai->line_style;
+ current_item++;
+ }
+ i++;
+ }
+
+ list = g_list_next(list);
+ }
+ /* in case the window is resized we might have to move the top item */
+ if ((first_item + display_items) > user_data->num_items){
+ if (display_items>user_data->num_items)
+ first_item=0;
+ else
+ first_item = user_data->num_items - display_items;
+ }
+
+ /* in case there are less items than possible displayed */
+ display_items = current_item;
+ last_item = first_item+display_items-1;
+
+ /* if no items to display */
+ if (display_items == 0) return;
+
+
+ /* Calculate the x borders */
+ /* We use time from the last display item to calcultate the x left border */
+ /* XXX TODO: Use recent.gui_time_format to chose time format */
+ g_snprintf(label_string, MAX_LABEL, "%.3f", nstime_to_sec(&user_data->dlg.items[display_items-1].fd->rel_ts));
+#if 0
+ /* Write the time, using the same format as in th etime col */
+ set_fd_time(user_data->dlg.items[display_items-1].fd, time_str);
+ g_snprintf(label_string, MAX_LABEL, "%s", time_str);
+#endif
+ layout = gtk_widget_create_pango_layout(user_data->dlg.draw_area_time, label_string);
+ middle_layout = gtk_widget_create_pango_layout(user_data->dlg.draw_area_time, label_string);
+ small_layout = gtk_widget_create_pango_layout(user_data->dlg.draw_area_time, label_string);
+
+ middle_font_desc = pango_font_description_copy(pango_context_get_font_description(pango_layout_get_context(middle_layout)));
+ middle_font_size = pango_font_description_get_size(middle_font_desc);
+ pango_font_description_set_size(middle_font_desc,(gint)(middle_font_size*0.8));
+ pango_layout_set_font_description(middle_layout,middle_font_desc);
+
+ small_font_desc = pango_font_description_copy(pango_context_get_font_description(pango_layout_get_context(small_layout)));
+ small_font_size = pango_font_description_get_size(small_font_desc);
+ pango_font_description_set_size(small_font_desc,(gint)(small_font_size*0.7));
+ pango_layout_set_font_description(small_layout,small_font_desc);
+
+ pango_layout_get_pixel_size(layout, &label_width, &label_height);
+
+ /* resize the "time" draw area */
+ left_x_border=0;
+ user_data->dlg.left_x_border = left_x_border;
+
+ right_x_border=0;
+ draw_width=user_data->dlg.surface_width-right_x_border-left_x_border;
+
+#if GTK_CHECK_VERSION(2,22,0)
+ /* Paint time title background */
+ cr = cairo_create (user_data->dlg.surface_time);
+ gdk_cairo_set_source_pixbuf (cr, bg_pixbuf, 0, 0);
+ cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+ cairo_rectangle (cr, 0, 0, draw_area_time_alloc.width, top_y_border);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+ /* Paint main title background */
+ cr = cairo_create (user_data->dlg.surface_main);
+ gdk_cairo_set_source_pixbuf (cr, bg_pixbuf, 0, 0);
+ cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+ cairo_rectangle (cr, 0, 0, draw_area_alloc.width, top_y_border);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+ /* Paint main comment background */
+ cr = cairo_create (user_data->dlg.surface_comments);
+ gdk_cairo_set_source_pixbuf (cr, bg_pixbuf, 0, 0);
+ cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+ cairo_rectangle (cr, 0, 0, draw_area_comments_alloc.width, top_y_border);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+#else
+ /* Paint time title background */
+ if ( GDK_IS_DRAWABLE(user_data->dlg.pixmap_time) ){
+ cr = gdk_cairo_create (user_data->dlg.pixmap_time);
+ gdk_cairo_set_source_pixbuf (cr, bg_pixbuf, 0, 0);
+ cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+ cairo_rectangle (cr, 0, 0, draw_area_time_alloc.width, top_y_border);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+ }
+ /* Paint main title background */
+ if ( GDK_IS_DRAWABLE(user_data->dlg.pixmap_main) ){
+ cr = gdk_cairo_create (user_data->dlg.pixmap_main);
+ gdk_cairo_set_source_pixbuf (cr, bg_pixbuf, 0, 0);
+ cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+ cairo_rectangle (cr, 0, 0, draw_area_alloc.width, top_y_border);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ }
+
+ /* Paint main comment background */
+ if ( GDK_IS_DRAWABLE(user_data->dlg.pixmap_comments) ){
+ cr = gdk_cairo_create (user_data->dlg.pixmap_comments);
+ gdk_cairo_set_source_pixbuf (cr, bg_pixbuf, 0, 0);
+ cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+ cairo_rectangle (cr, 0, 0, draw_area_comments_alloc.width, top_y_border);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ }
+
+#endif
+ /* Draw the word "Time" on top of time column */
+ g_snprintf(label_string, label_width, "%s", " Time");
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &label_width, &label_height);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.surface_time);
+ cairo_move_to (cr, left_x_border, top_y_border/2-label_height/2);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+#else
+ if (GDK_IS_DRAWABLE(user_data->dlg.pixmap_time)) {
+ cr = gdk_cairo_create (user_data->dlg.pixmap_time);
+ cairo_move_to (cr, left_x_border, top_y_border/2-label_height/2);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+#endif
+ /* Draw the word "Comment" on top of comment column */
+ g_snprintf(label_string, label_width, "%s", "Comment");
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &label_width, &label_height);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.surface_comments);
+ cairo_move_to (cr, MAX_COMMENT/2-label_width/2, top_y_border/2-((i&1)?0:label_height));
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+#endif
+ /* Paint the background items */
+ for (current_item=0; current_item<display_items; current_item++){
+ /*select the color. if it is the selected item select blue color */
+ if ( current_item+first_item == user_data->dlg.selected_item ) {
+ bg_color_p = &background_color[0]; /* blue */
+ } else {
+ bg_color_p = &background_color[1+user_data->dlg.items[current_item].conv_num%MAX_NUM_COL_CONV];
+ }
+#if GTK_CHECK_VERSION(2,22,0)
+ /* Paint background */
+ cr = cairo_create (user_data->dlg.surface_main);
+ gdk_cairo_set_source_color (cr, bg_color_p);
+ cairo_rectangle (cr, left_x_border, top_y_border+current_item*ITEM_HEIGHT, draw_width, ITEM_HEIGHT);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+#else
+ if (GDK_IS_DRAWABLE(user_data->dlg.pixmap_main)) {
+ /* Paint background */
+ cr = gdk_cairo_create (user_data->dlg.pixmap_main);
+ gdk_cairo_set_source_color (cr, bg_color_p);
+ cairo_rectangle (cr, left_x_border, top_y_border+current_item*ITEM_HEIGHT, draw_width, ITEM_HEIGHT);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ }
+#endif
+ }
+ /* Draw the node names on top and the division lines */
+ for (i=0; i<user_data->num_nodes; i++){
+ /* print the node identifiers */
+ /* XXX we assign 5 pixels per character in the node identity */
+ g_strlcpy(label_string, get_addr_name(&(user_data->nodes[i])), NODE_WIDTH/5);
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &label_width, &label_height);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.surface_main);
+ cairo_move_to (cr, left_x_border+NODE_WIDTH/2-label_width/2+NODE_WIDTH*i, top_y_border/2-((i&1)?0:label_height));
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+#else
+ if (GDK_IS_DRAWABLE(user_data->dlg.pixmap_main)) {
+ cr = gdk_cairo_create (user_data->dlg.pixmap_main);
+ cairo_move_to (cr, left_x_border+NODE_WIDTH/2-label_width/2+NODE_WIDTH*i, top_y_border/2-((i&1)?0:label_height));
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+#endif
+#if GTK_CHECK_VERSION(2,22,0)
+ /* draw the node division lines */
+ cr = cairo_create (user_data->dlg.surface_main);
+ gdk_cairo_set_source_color (cr, &grey_color0);
+ cairo_set_line_width (cr, 1.0);
+ cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
+ cairo_set_dash(cr, dashed1, len1, 0);
+ cairo_move_to(cr, left_x_border+NODE_WIDTH/2+NODE_WIDTH*i, top_y_border);
+ cairo_line_to(cr, (left_x_border+NODE_WIDTH/2+NODE_WIDTH*i),draw_area_alloc.height-bottom_y_border);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+#else
+ /* draw the node division lines */
+ if (GDK_IS_DRAWABLE(user_data->dlg.pixmap_main) ) {
+ cr = gdk_cairo_create (user_data->dlg.pixmap_main);
+ gdk_cairo_set_source_color (cr, &grey_color0);
+ cairo_set_line_width (cr, 1.0);
+ cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
+ cairo_set_dash(cr, dashed1, len1, 0);
+ cairo_move_to(cr, left_x_border+NODE_WIDTH/2+NODE_WIDTH*i, top_y_border);
+ cairo_line_to(cr, (left_x_border+NODE_WIDTH/2+NODE_WIDTH*i),draw_area_alloc.height-bottom_y_border);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ }
+#endif
+ }
+
+ /* Draw the items */
+ for (current_item=0; current_item<display_items; current_item++){
+ /* Draw the time */
+ g_snprintf(label_string, MAX_LABEL, "%.3f", nstime_to_sec(&user_data->dlg.items[current_item].fd->rel_ts));
+#if 0
+ set_fd_time(user_data->dlg.items[current_item].fd, time_str);
+ g_snprintf(label_string, MAX_LABEL, "%s", time_str);
+#endif
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &label_width, &label_height);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.surface_time);
+ cairo_move_to (cr, 3, top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT/2-label_height/2);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+#else
+ if (GDK_IS_DRAWABLE(user_data->dlg.pixmap_time)) {
+ cr = gdk_cairo_create (user_data->dlg.pixmap_time);
+ cairo_move_to (cr, 3, top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT/2-label_height/2);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+#endif
+ /*draw the comments */
+ g_snprintf(label_string, MAX_COMMENT, "%s", user_data->dlg.items[current_item].comment);
+ pango_layout_set_text(middle_layout, label_string, -1);
+ pango_layout_get_pixel_size(middle_layout, &label_width, &label_height);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.surface_comments);
+ cairo_move_to (cr, 2, top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT/2-label_height/2);
+ pango_cairo_show_layout (cr, middle_layout);
+ cairo_destroy (cr);
+ cr = NULL;
+#else
+ if (GDK_IS_DRAWABLE(user_data->dlg.pixmap_comments)) {
+ cr = gdk_cairo_create (user_data->dlg.pixmap_comments);
+ cairo_move_to (cr, 2, top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT/2-label_height/2);
+ pango_cairo_show_layout (cr, middle_layout);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+#endif
+ /* draw the arrow line */
+ start_arrow = left_x_border+(user_data->dlg.items[current_item].src_node)*NODE_WIDTH+NODE_WIDTH/2;
+ end_arrow = left_x_border+(user_data->dlg.items[current_item].dst_node)*NODE_WIDTH+NODE_WIDTH/2;
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.surface_main);
+ if (user_data->dlg.items[current_item].line_style == 2) {
+ /* draw a line thick */
+ cairo_set_line_width (cr, 2.0);
+ }else{
+ cairo_set_line_width (cr, 1.0);
+ }
+ if ( current_item+first_item == user_data->dlg.selected_item ){
+ /* draw white line */
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ }else{
+ /* draw black line */
+ cairo_set_source_rgb (cr, 0, 0, 0);
+ }
+ cairo_move_to(cr, start_arrow, (top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT-7)+0.5);
+ cairo_line_to(cr, end_arrow, (top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT-7)+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+#else
+ if (GDK_IS_DRAWABLE(user_data->dlg.pixmap_main) ) {
+ cr = gdk_cairo_create (user_data->dlg.pixmap_main);
+ if (user_data->dlg.items[current_item].line_style == 2) {
+ /* draw a line thick */
+ cairo_set_line_width (cr, 2.0);
+ }else{
+ cairo_set_line_width (cr, 1.0);
+ }
+ if ( current_item+first_item == user_data->dlg.selected_item ){
+ /* draw white line */
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ }else{
+ /* draw black line */
+ cairo_set_source_rgb (cr, 0, 0, 0);
+ }
+ cairo_move_to(cr, start_arrow, (top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT-7)+0.5);
+ cairo_line_to(cr, end_arrow, (top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT-7)+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ }
+#endif
+ /* select colors */
+ if ( current_item+first_item == user_data->dlg.selected_item ){
+ color_p = &white_color;
+ } else {
+ color_p = &black_color;
+ }
+ /* draw the arrow */
+#if GTK_CHECK_VERSION(2,22,0)
+ if (start_arrow<end_arrow)
+ draw_arrow(user_data->dlg.surface_main, color_p, end_arrow-WIDTH_ARROW, (top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT-7)-(HEIGHT_ARROW/2), RIGHT_ARROW);
+ else
+ draw_arrow(user_data->dlg.surface_main, color_p, end_arrow, top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT-7-(HEIGHT_ARROW/2), LEFT_ARROW);
+#else
+ if (start_arrow<end_arrow)
+ draw_arrow(user_data->dlg.pixmap_main, color_p, end_arrow-WIDTH_ARROW, (top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT-7)-(HEIGHT_ARROW/2), RIGHT_ARROW);
+ else
+ draw_arrow(user_data->dlg.pixmap_main, color_p, end_arrow, top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT-7-(HEIGHT_ARROW/2), LEFT_ARROW);
+#endif
+ /* draw the frame comment */
+ g_snprintf(label_string, MAX_LABEL, "%s", user_data->dlg.items[current_item].frame_label);
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &label_width, &label_height);
+ if (start_arrow<end_arrow){
+ arrow_width = end_arrow-start_arrow;
+ label_x = arrow_width/2+start_arrow;
+ }
+ else {
+ arrow_width = start_arrow-end_arrow;
+ label_x = arrow_width/2+end_arrow;
+ }
+
+ if (label_width>(gint)arrow_width) arrow_width = label_width;
+
+ if ((int)left_x_border > ((int)label_x-(int)label_width/2))
+ label_x = left_x_border + label_width/2;
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.surface_main);
+ gdk_cairo_set_source_color (cr, color_p);
+ cairo_move_to (cr, label_x - label_width/2, top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT/2-label_height/2-3);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+#else
+ if (GDK_IS_DRAWABLE(user_data->dlg.pixmap_main)) {
+ cr = gdk_cairo_create (user_data->dlg.pixmap_main);
+ gdk_cairo_set_source_color (cr, color_p);
+ cairo_move_to (cr, label_x - label_width/2, top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT/2-label_height/2-3);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+#endif
+ /* draw the source port number */
+ g_snprintf(label_string, MAX_LABEL, "(%i)", user_data->dlg.items[current_item].port_src);
+ pango_layout_set_text(small_layout, label_string, -1);
+ pango_layout_get_pixel_size(small_layout, &label_width, &label_height);
+ if (start_arrow<end_arrow){
+ src_port_x = start_arrow - label_width - 2;
+ }
+ else {
+ src_port_x = start_arrow + 2;
+ }
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.surface_main);
+ /* select color */
+ if ( current_item+first_item == user_data->dlg.selected_item ){
+ gdk_cairo_set_source_color (cr, &grey_color1);
+ } else {
+ gdk_cairo_set_source_color (cr, &grey_color0);
+ }
+ gdk_cairo_set_source_color (cr, &grey_color0);
+ cairo_move_to (cr, src_port_x, top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT-2-label_height/2-2);
+ pango_cairo_show_layout (cr, small_layout);
+ cairo_destroy (cr);
+#else
+ if (GDK_IS_DRAWABLE(user_data->dlg.pixmap_main)) {
+ cr = gdk_cairo_create (user_data->dlg.pixmap_main);
+ /* select color */
+ if ( current_item+first_item == user_data->dlg.selected_item ){
+ gdk_cairo_set_source_color (cr, &grey_color1);
+ } else {
+ gdk_cairo_set_source_color (cr, &grey_color0);
+ }
+ gdk_cairo_set_source_color (cr, &grey_color0);
+ cairo_move_to (cr, src_port_x, top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT-2-label_height/2-2);
+ pango_cairo_show_layout (cr, small_layout);
+ cairo_destroy (cr);
+ }
+#endif
+ /* draw the destination port number */
+ g_snprintf(label_string, MAX_LABEL, "(%i)", user_data->dlg.items[current_item].port_dst);
+ pango_layout_set_text(small_layout, label_string, -1);
+ pango_layout_get_pixel_size(small_layout, &label_width, &label_height);
+ if (start_arrow<end_arrow){
+ dst_port_x = end_arrow + 2;
+ }
+ else {
+ dst_port_x = end_arrow - label_width - 2;
+ }
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.surface_main);
+ /* select color */
+ if ( current_item+first_item == user_data->dlg.selected_item ){
+ gdk_cairo_set_source_color (cr, &grey_color1);
+ } else {
+ gdk_cairo_set_source_color (cr, &grey_color0);
+ }
+ cairo_move_to (cr, dst_port_x, top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT-2-label_height/2-2);
+ pango_cairo_show_layout (cr, small_layout);
+ cairo_destroy (cr);
+#else
+ if (GDK_IS_DRAWABLE(user_data->dlg.pixmap_main)) {
+ cr = gdk_cairo_create (user_data->dlg.pixmap_main);
+ /* select color */
+ if ( current_item+first_item == user_data->dlg.selected_item ){
+ gdk_cairo_set_source_color (cr, &grey_color1);
+ } else {
+ gdk_cairo_set_source_color (cr, &grey_color0);
+ }
+ cairo_move_to (cr, dst_port_x, top_y_border+current_item*ITEM_HEIGHT+ITEM_HEIGHT-2-label_height/2-2);
+ pango_cairo_show_layout (cr, small_layout);
+ cairo_destroy (cr);
+ }
+#endif
+ /* draw the div line of the selected item with soft gray*/
+ if ( current_item+first_item == user_data->dlg.selected_item )
+ for (i=0; i<user_data->num_nodes; i++){
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.surface_main);
+ gdk_cairo_set_source_color (cr, &grey_color1);
+ cairo_set_line_width (cr, 1.0);
+ cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
+ cairo_set_dash(cr, dashed1, len1, 0);
+ cairo_move_to(cr, left_x_border+NODE_WIDTH/2+NODE_WIDTH*i, (user_data->dlg.selected_item-first_item)*ITEM_HEIGHT+TOP_Y_BORDER);
+ cairo_line_to(cr, (left_x_border+NODE_WIDTH/2+NODE_WIDTH*i),(user_data->dlg.selected_item-first_item)*ITEM_HEIGHT+TOP_Y_BORDER+ITEM_HEIGHT);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+#else
+ if (GDK_IS_DRAWABLE(user_data->dlg.pixmap_main) ) {
+ cr = gdk_cairo_create (user_data->dlg.pixmap_main);
+ gdk_cairo_set_source_color (cr, &grey_color1);
+ cairo_set_line_width (cr, 1.0);
+ cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
+ cairo_set_dash(cr, dashed1, len1, 0);
+ cairo_move_to(cr, left_x_border+NODE_WIDTH/2+NODE_WIDTH*i, (user_data->dlg.selected_item-first_item)*ITEM_HEIGHT+TOP_Y_BORDER);
+ cairo_line_to(cr, (left_x_border+NODE_WIDTH/2+NODE_WIDTH*i),(user_data->dlg.selected_item-first_item)*ITEM_HEIGHT+TOP_Y_BORDER+ITEM_HEIGHT);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ }
+#endif
+ }
+ }
+
+ g_object_unref(G_OBJECT(layout));
+#if 0
+ g_free(time_str);
+#endif
+ /* refresh the draw areas */
+ if (gtk_widget_is_drawable(user_data->dlg.draw_area_time) ){
+ cr = gdk_cairo_create (gtk_widget_get_window(user_data->dlg.draw_area_time));
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, user_data->dlg.surface_time, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, user_data->dlg.pixmap_time, 0, 0);
+#endif
+ cairo_rectangle (cr, 0, 0, draw_area_time_alloc.width, draw_area_time_alloc.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+
+ if (gtk_widget_is_drawable(user_data->dlg.draw_area) ){
+ cr = gdk_cairo_create (gtk_widget_get_window(user_data->dlg.draw_area));
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, user_data->dlg.surface_main, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, user_data->dlg.pixmap_main, 0, 0);
+#endif
+ cairo_rectangle (cr, 0, 0, draw_area_alloc.width, draw_area_alloc.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+
+ if (gtk_widget_is_drawable(user_data->dlg.draw_area_comments) ){
+ cr = gdk_cairo_create (gtk_widget_get_window(user_data->dlg.draw_area_comments));
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, user_data->dlg.surface_comments, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, user_data->dlg.pixmap_comments, 0, 0);
+#endif
+ cairo_rectangle (cr, 0, 0, draw_area_comments_alloc.width, draw_area_comments_alloc.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+
+ /* update the v_scrollbar */
+ gtk_adjustment_set_upper(user_data->dlg.v_scrollbar_adjustment, (gdouble) user_data->num_items-1);
+ gtk_adjustment_set_step_increment(user_data->dlg.v_scrollbar_adjustment, 1);
+ gtk_adjustment_set_page_increment(user_data->dlg.v_scrollbar_adjustment, (gdouble) (last_item-first_item));
+ gtk_adjustment_set_page_size(user_data->dlg.v_scrollbar_adjustment, (gdouble) (last_item-first_item));
+ gtk_adjustment_set_value(user_data->dlg.v_scrollbar_adjustment, (gdouble) first_item);
+
+ gtk_adjustment_changed(user_data->dlg.v_scrollbar_adjustment);
+ gtk_adjustment_value_changed(user_data->dlg.v_scrollbar_adjustment);
+}
+
+/****************************************************************************/
+static void dialog_graph_redraw(graph_analysis_data_t *user_data)
+{
+ user_data->dlg.needs_redraw=TRUE;
+ dialog_graph_draw(user_data);
+}
+
+/****************************************************************************/
+static gboolean button_press_event(GtkWidget *widget _U_, GdkEventButton *event, gpointer data)
+{
+ graph_analysis_data_t *user_data = data;
+ guint32 item;
+
+ if (event->type != GDK_BUTTON_PRESS) return TRUE;
+
+ if (event->y<TOP_Y_BORDER) return TRUE;
+
+ /* get the item clicked */
+ item = ((guint32)event->y - TOP_Y_BORDER) / ITEM_HEIGHT;
+ if (item >= user_data->num_items) return TRUE;
+ user_data->dlg.selected_item = item + user_data->dlg.first_item;
+
+ user_data->dlg.needs_redraw=TRUE;
+ dialog_graph_draw(user_data);
+
+ cf_goto_frame(&cfile, user_data->dlg.items[item].fd->num);
+
+ return TRUE;
+}
+
+
+
+/****************************************************************************/
+static gboolean key_press_event(GtkWidget *widget _U_, GdkEventKey *event, gpointer data)
+{
+ graph_analysis_data_t *user_data = data;
+
+ /* if there is nothing selected, just return */
+ if (user_data->dlg.selected_item == 0xFFFFFFFF) return TRUE;
+
+ /* Up arrow */
+ if (event->keyval == GDK_Up){
+ if (user_data->dlg.selected_item == 0) return TRUE;
+ user_data->dlg.selected_item--;
+ if ( (user_data->dlg.selected_item<user_data->dlg.first_item) || (user_data->dlg.selected_item>user_data->dlg.first_item+gtk_adjustment_get_page_size(user_data->dlg.v_scrollbar_adjustment)) )
+ user_data->dlg.first_item = user_data->dlg.selected_item;
+ /* Down arrow */
+ } else if (event->keyval == GDK_Down){
+ if (user_data->dlg.selected_item == user_data->num_items-1) return TRUE;
+ user_data->dlg.selected_item++;
+ if ( (user_data->dlg.selected_item<user_data->dlg.first_item) || (user_data->dlg.selected_item>user_data->dlg.first_item+gtk_adjustment_get_page_size(user_data->dlg.v_scrollbar_adjustment)) )
+ user_data->dlg.first_item = (guint32)user_data->dlg.selected_item-(guint32)gtk_adjustment_get_page_size(user_data->dlg.v_scrollbar_adjustment);
+ } else if (event->keyval == GDK_Left){
+ if (user_data->dlg.first_node == 0) return TRUE;
+ user_data->dlg.first_node--;
+ } else return TRUE;
+
+ user_data->dlg.needs_redraw=TRUE;
+ dialog_graph_draw(user_data);
+
+ cf_goto_frame(&cfile, user_data->dlg.items[user_data->dlg.selected_item-user_data->dlg.first_item].fd->num);
+
+ return TRUE;
+}
+
+
+
+/****************************************************************************/
+static gboolean draw_area_draw(GtkWidget *widget, cairo_t *cr, gpointer data)
+{
+ graph_analysis_data_t *user_data = data;
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation (widget, &allocation);
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, user_data->dlg.surface_main, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, user_data->dlg.pixmap_main,0, 0);
+#endif
+ cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
+ cairo_fill (cr);
+
+
+ return FALSE;
+}
+#if !GTK_CHECK_VERSION(3,0,0)
+/****************************************************************************/
+static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event _U_, gpointer data)
+{
+ GtkAllocation allocation;
+ graph_analysis_data_t *user_data = data;
+ cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(widget));
+
+
+ if (gtk_widget_is_drawable(widget)){
+ gtk_widget_get_allocation (widget, &allocation);
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, user_data->dlg.surface_main, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, user_data->dlg.pixmap_main, 0, 0);
+#endif
+ cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+ }
+
+ return FALSE;
+}
+#endif
+
+static gboolean
+draw_area_scrolled(GtkAdjustment *adjustment _U_, gpointer data)
+{
+ graph_analysis_data_t *user_data = data;
+ cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(user_data->dlg.draw_area));
+
+
+ draw_area_draw(user_data->dlg.draw_area, cr, data);
+
+ return TRUE;
+}
+/****************************************************************************/
+static gboolean draw_comments(GtkWidget *widget, cairo_t *cr, gpointer data)
+{
+ graph_analysis_data_t *user_data = data;
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, user_data->dlg.surface_comments, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, user_data->dlg.pixmap_comments,0, 0);
+#endif
+ cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
+ cairo_fill (cr);
+
+
+ return FALSE;
+}
+#if !GTK_CHECK_VERSION(3,0,0)
+/****************************************************************************/
+static gboolean expose_event_comments(GtkWidget *widget, GdkEventExpose *event _U_, gpointer data)
+{
+ GtkAllocation allocation;
+ graph_analysis_data_t *user_data = data;
+ cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(widget));
+
+ if (gtk_widget_is_drawable(widget)){
+ gtk_widget_get_allocation (widget, &allocation);
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, user_data->dlg.surface_comments, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, user_data->dlg.pixmap_comments,0, 0);
+#endif
+ cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+ }
+
+ return FALSE;
+}
+#endif
+
+static gboolean
+comments_area_scrolled(GtkAdjustment *adjustment _U_, gpointer data)
+{
+ graph_analysis_data_t *user_data = data;
+ cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(user_data->dlg.draw_area_comments));
+
+
+ draw_comments(user_data->dlg.draw_area_comments, cr, data);
+
+ return TRUE;
+}
+
+#if GTK_CHECK_VERSION(3,0,0)
+/****************************************************************************/
+static gboolean draw_time(GtkWidget *widget, cairo_t *cr, gpointer data)
+{
+ graph_analysis_data_t *user_data = data;
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ cairo_set_source_surface (cr, user_data->dlg.surface_time, 0, 0);
+ cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
+ cairo_fill (cr);
+
+ return FALSE;
+}
+#else
+/****************************************************************************/
+static gboolean expose_event_time(GtkWidget *widget, GdkEventExpose *event _U_, gpointer data)
+{
+ GtkAllocation allocation;
+ graph_analysis_data_t *user_data = data;
+ cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(widget));
+
+ if (gtk_widget_is_drawable(widget) ){
+ gtk_widget_get_allocation (widget, &allocation);
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, user_data->dlg.surface_time, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, user_data->dlg.pixmap_time, 0, 0);
+#endif
+ cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+ }
+
+ return FALSE;
+}
+#endif
+/****************************************************************************/
+static gboolean configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer data)
+{
+ graph_analysis_data_t *user_data = data;
+ GtkAllocation widget_alloc;
+ cairo_t *cr;
+
+#if GTK_CHECK_VERSION(2,22,0)
+ if(user_data->dlg.surface_main){
+ cairo_surface_destroy (user_data->dlg.surface_main);
+ user_data->dlg.surface_main=NULL;
+ }
+#else
+ if(user_data->dlg.pixmap_main){
+ g_object_unref(user_data->dlg.pixmap_main);
+ user_data->dlg.pixmap_main=NULL;
+ }
+#endif
+ gtk_widget_get_allocation(widget, &widget_alloc);
+
+#if GTK_CHECK_VERSION(2,22,0)
+ user_data->dlg.surface_main = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
+ CAIRO_CONTENT_COLOR,
+ widget_alloc.width,
+ widget_alloc.height);
+
+ cr = cairo_create (user_data->dlg.surface_main);
+ cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ cr = NULL;
+#else
+ user_data->dlg.pixmap_main=gdk_pixmap_new(gtk_widget_get_window(widget),
+ widget_alloc.width,
+ widget_alloc.height,
+ -1);
+ if ( GDK_IS_DRAWABLE(user_data->dlg.pixmap_main) ){
+ cr = gdk_cairo_create (user_data->dlg.pixmap_main);
+ cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+#endif
+
+ dialog_graph_redraw(user_data);
+
+ return TRUE;
+}
+
+/****************************************************************************/
+static gboolean configure_event_comments(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer data)
+{
+ graph_analysis_data_t *user_data = data;
+ GtkAllocation widget_alloc;
+ cairo_t *cr;
+
+ gtk_widget_get_allocation(widget, &widget_alloc);
+
+#if GTK_CHECK_VERSION(2,22,0)
+ if(user_data->dlg.surface_comments){
+ cairo_surface_destroy (user_data->dlg.surface_comments);
+ user_data->dlg.surface_comments=NULL;
+ }
+#else
+ if(user_data->dlg.pixmap_comments){
+ g_object_unref(user_data->dlg.pixmap_comments);
+ user_data->dlg.pixmap_comments=NULL;
+ }
+#endif
+
+#if GTK_CHECK_VERSION(2,22,0)
+ user_data->dlg.surface_comments=gdk_window_create_similar_surface (gtk_widget_get_window(widget),
+ CAIRO_CONTENT_COLOR,
+ widget_alloc.width,
+ widget_alloc.height);
+
+ cr = cairo_create (user_data->dlg.surface_comments);
+ cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ cr = NULL;
+#else
+ user_data->dlg.pixmap_comments=gdk_pixmap_new(gtk_widget_get_window(widget),
+ widget_alloc.width,
+ widget_alloc.height,
+ -1);
+
+ if ( GDK_IS_DRAWABLE(user_data->dlg.pixmap_main) ){
+ cr = gdk_cairo_create (user_data->dlg.pixmap_comments);
+ cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+#endif
+ dialog_graph_redraw(user_data);
+ return TRUE;
+}
+
+/****************************************************************************/
+static gboolean configure_event_time(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer data)
+{
+ graph_analysis_data_t *user_data = data;
+ GtkAllocation widget_alloc;
+ cairo_t *cr;
+
+ gtk_widget_get_allocation(widget, &widget_alloc);
+
+#if GTK_CHECK_VERSION(2,22,0)
+ if(user_data->dlg.surface_time){
+ cairo_surface_destroy (user_data->dlg.surface_time);
+ user_data->dlg.surface_time=NULL;
+ }
+#else
+ if(user_data->dlg.pixmap_time){
+ g_object_unref(user_data->dlg.pixmap_time);
+ user_data->dlg.pixmap_time=NULL;
+ }
+#endif
+#if GTK_CHECK_VERSION(2,22,0)
+ user_data->dlg.surface_time=gdk_window_create_similar_surface(gtk_widget_get_window(widget),
+ CAIRO_CONTENT_COLOR,
+ widget_alloc.width,
+ widget_alloc.height);
+
+ cr = cairo_create (user_data->dlg.surface_time);
+ cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ cr = NULL;
+#else
+ user_data->dlg.pixmap_time=gdk_pixmap_new(gtk_widget_get_window(widget),
+ widget_alloc.width,
+ widget_alloc.height,
+ -1);
+
+ if ( GDK_IS_DRAWABLE(user_data->dlg.pixmap_time) ){
+ cr = gdk_cairo_create (user_data->dlg.pixmap_time);
+ cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+#endif
+
+ dialog_graph_redraw(user_data);
+
+ return TRUE;
+}
+
+/****************************************************************************/
+static gboolean pane_callback(GtkWidget *widget _U_, GParamSpec *pspec _U_, gpointer data)
+{
+ graph_analysis_data_t *user_data = data;
+ GtkAllocation draw_area_comments_alloc, draw_area_alloc;
+ cairo_t *cr;
+
+ if (gtk_paned_get_position(GTK_PANED(user_data->dlg.hpane)) > user_data->dlg.surface_width)
+ gtk_paned_set_position(GTK_PANED(user_data->dlg.hpane), user_data->dlg.surface_width);
+ else if (gtk_paned_get_position(GTK_PANED(user_data->dlg.hpane)) < NODE_WIDTH*2)
+ gtk_paned_set_position(GTK_PANED(user_data->dlg.hpane), NODE_WIDTH*2);
+
+ /* repaint the comment area because when moving the pane position there are times that the expose_event_comments is not called */
+
+ gtk_widget_get_allocation(user_data->dlg.draw_area_comments, &draw_area_comments_alloc);
+
+ if (gtk_widget_is_drawable(user_data->dlg.draw_area_comments)){
+ cr = gdk_cairo_create (gtk_widget_get_window(user_data->dlg.draw_area_comments));
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, user_data->dlg.surface_comments, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, user_data->dlg.pixmap_comments, 0, 0);
+#endif
+ cairo_rectangle (cr, 0, 0, draw_area_comments_alloc.width, draw_area_comments_alloc.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ }
+ /* repaint the draw area because when moving the pane position there are times that the expose_event_comments is not called */
+ gtk_widget_get_allocation(user_data->dlg.draw_area, &draw_area_alloc);
+
+ if (gtk_widget_is_drawable(user_data->dlg.draw_area)){
+ cr = gdk_cairo_create(gtk_widget_get_window(user_data->dlg.draw_area));
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, user_data->dlg.surface_main, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, user_data->dlg.pixmap_main, 0, 0);
+#endif
+ cairo_rectangle (cr, 0, 0, draw_area_alloc.width, draw_area_alloc.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ }
+
+
+ return TRUE;
+}
+
+/****************************************************************************/
+static void v_scrollbar_changed(GtkWidget *widget _U_, gpointer data)
+{
+ graph_analysis_data_t *user_data = data;
+
+ if ((user_data->dlg.first_item+gtk_adjustment_get_page_size(user_data->dlg.v_scrollbar_adjustment)+1 == user_data->num_items)
+ && (gtk_adjustment_get_value(user_data->dlg.v_scrollbar_adjustment) >= user_data->dlg.first_item ))
+ return;
+
+ if (user_data->dlg.first_item == gtk_adjustment_get_value(user_data->dlg.v_scrollbar_adjustment))
+ return;
+
+ user_data->dlg.first_item = (guint32) gtk_adjustment_get_value(user_data->dlg.v_scrollbar_adjustment);
+
+ dialog_graph_redraw(user_data);
+
+ return;
+}
+
+/****************************************************************************/
+static void create_draw_area(graph_analysis_data_t *user_data, GtkWidget *box)
+{
+ GtkWidget *hbox;
+ GtkWidget *viewport;
+ GtkWidget *scroll_window_comments;
+ GtkWidget *viewport_comments;
+ GtkWidget *frame_time;
+ GtkWidget *scroll_vbox;
+ GtkWidget *frame_box;
+ GtkRequisition scroll_requisition;
+ GtkWidget *frame;
+
+ hbox=gtk_hbox_new(FALSE, 0);
+ gtk_widget_show(hbox);
+
+ /* create "time" draw area */
+ user_data->dlg.draw_area_time=gtk_drawing_area_new();
+ gtk_widget_set_size_request(user_data->dlg.draw_area_time, TIME_WIDTH, user_data->dlg.surface_height);
+ frame_time = gtk_frame_new(NULL);
+ gtk_widget_show(frame_time);
+ gtk_container_add(GTK_CONTAINER(frame_time),user_data->dlg.draw_area_time);
+
+ /* create "comments" draw area */
+ user_data->dlg.draw_area_comments=gtk_drawing_area_new();
+ gtk_widget_set_size_request(user_data->dlg.draw_area_comments, COMMENT_WIDTH, user_data->dlg.surface_height);
+ scroll_window_comments=gtk_scrolled_window_new(NULL, NULL);
+ gtk_widget_set_size_request(scroll_window_comments, (gint)(COMMENT_WIDTH/1.5), user_data->dlg.surface_height);
+ /*
+ * Set the scrollbar policy for the horizontal and vertical scrollbars
+ * The policy determines when the scrollbar should appear
+ */
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scroll_window_comments),
+ GTK_POLICY_ALWAYS, /* Policy for horizontal bar. */
+ GTK_POLICY_NEVER); /* Policy for vertical bar */
+
+ /* Changes the type of shadow drawn around the contents of scrolled_window. */
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll_window_comments),
+ GTK_SHADOW_ETCHED_IN);
+
+ g_signal_connect(gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(scroll_window_comments)),
+ "value-changed", G_CALLBACK(comments_area_scrolled), user_data);
+
+
+ viewport_comments = gtk_viewport_new(gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(scroll_window_comments)),
+ gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scroll_window_comments)));
+ gtk_container_add(GTK_CONTAINER(viewport_comments), user_data->dlg.draw_area_comments);
+ gtk_container_add(GTK_CONTAINER(scroll_window_comments), viewport_comments);
+ gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport_comments), GTK_SHADOW_NONE);
+ gtk_widget_add_events (user_data->dlg.draw_area_comments, GDK_BUTTON_PRESS_MASK);
+
+ /* create main Graph draw area */
+ user_data->dlg.draw_area=gtk_drawing_area_new();
+ if (user_data->num_nodes < 2)
+ user_data->dlg.surface_width = 2 * NODE_WIDTH;
+ else
+ user_data->dlg.surface_width = user_data->num_nodes * NODE_WIDTH;
+ gtk_widget_set_size_request(user_data->dlg.draw_area, user_data->dlg.surface_width, user_data->dlg.surface_height);
+ user_data->dlg.scroll_window=gtk_scrolled_window_new(NULL, NULL);
+ if ( user_data->num_nodes < 6)
+ gtk_widget_set_size_request(user_data->dlg.scroll_window, NODE_WIDTH*user_data->num_nodes, user_data->dlg.surface_height);
+ else
+ gtk_widget_set_size_request(user_data->dlg.scroll_window, NODE_WIDTH*5, user_data->dlg.surface_height);
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(user_data->dlg.scroll_window),
+ GTK_POLICY_ALWAYS,
+ GTK_POLICY_NEVER);
+
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(user_data->dlg.scroll_window),
+ GTK_SHADOW_ETCHED_IN);
+
+ g_signal_connect(gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(user_data->dlg.scroll_window)),
+ "value-changed", G_CALLBACK(draw_area_scrolled), user_data);
+
+
+ viewport = gtk_viewport_new(gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(user_data->dlg.scroll_window)),
+ gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(user_data->dlg.scroll_window)));
+ gtk_container_add(GTK_CONTAINER(viewport), user_data->dlg.draw_area);
+ gtk_container_add(GTK_CONTAINER(user_data->dlg.scroll_window), viewport);
+ gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_focus(user_data->dlg.draw_area, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(user_data->dlg.draw_area, GTK_CAN_FOCUS);
+#endif
+ gtk_widget_grab_focus(user_data->dlg.draw_area);
+
+ /* signals needed to handle backing pixmap */
+#if GTK_CHECK_VERSION(3,0,0)
+ g_signal_connect(user_data->dlg.draw_area, "draw", G_CALLBACK(draw_area_draw), user_data);
+#else
+ g_signal_connect(user_data->dlg.draw_area, "expose_event", G_CALLBACK(expose_event), user_data);
+#endif
+ g_signal_connect(user_data->dlg.draw_area, "configure_event", G_CALLBACK(configure_event), user_data);
+
+ /* signals needed to handle backing pixmap comments */
+#if GTK_CHECK_VERSION(3,0,0)
+ g_signal_connect(user_data->dlg.draw_area_comments, "draw", G_CALLBACK(draw_comments), user_data);
+#else
+ g_signal_connect(user_data->dlg.draw_area_comments, "expose_event", G_CALLBACK(expose_event_comments), user_data);
+#endif
+ g_signal_connect(user_data->dlg.draw_area_comments, "configure_event", G_CALLBACK(configure_event_comments), user_data);
+
+ /* signals needed to handle backing pixmap time */
+#if GTK_CHECK_VERSION(3,0,0)
+ g_signal_connect(user_data->dlg.draw_area_time, "draw", G_CALLBACK(draw_time), user_data);
+#else
+ g_signal_connect(user_data->dlg.draw_area_time, "expose_event", G_CALLBACK(expose_event_time), user_data);
+#endif
+ g_signal_connect(user_data->dlg.draw_area_time, "configure_event", G_CALLBACK(configure_event_time), user_data);
+
+ gtk_widget_add_events (user_data->dlg.draw_area, GDK_BUTTON_PRESS_MASK);
+ g_signal_connect(user_data->dlg.draw_area, "button_press_event", G_CALLBACK(button_press_event), user_data);
+ g_signal_connect(user_data->dlg.draw_area, "key_press_event", G_CALLBACK(key_press_event), user_data);
+
+ gtk_widget_show(user_data->dlg.draw_area_time);
+ gtk_widget_show(user_data->dlg.draw_area);
+ gtk_widget_show(viewport);
+ gtk_widget_show(user_data->dlg.draw_area_comments);
+ gtk_widget_show(viewport_comments);
+
+ gtk_widget_show(user_data->dlg.scroll_window);
+ gtk_widget_show(scroll_window_comments);
+
+ gtk_box_pack_start(GTK_BOX(hbox), frame_time, FALSE, FALSE, 3);
+
+ user_data->dlg.hpane = gtk_hpaned_new();
+ gtk_paned_pack1(GTK_PANED (user_data->dlg.hpane), user_data->dlg.scroll_window, FALSE, TRUE);
+ gtk_paned_pack2(GTK_PANED (user_data->dlg.hpane), scroll_window_comments, TRUE, TRUE);
+ g_signal_connect(user_data->dlg.hpane, "notify::position", G_CALLBACK(pane_callback), user_data);
+ gtk_widget_show(user_data->dlg.hpane);
+
+ gtk_box_pack_start(GTK_BOX(hbox), user_data->dlg.hpane, TRUE, TRUE, 0);
+
+ /* Create the scroll_vbox to include the vertical scroll and a box at the bottom */
+ scroll_vbox=gtk_vbox_new(FALSE, 0);
+ gtk_widget_show(scroll_vbox);
+
+ /* create the associated v_scrollbar */
+ user_data->dlg.v_scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
+ user_data->dlg.v_scrollbar=gtk_vscrollbar_new(user_data->dlg.v_scrollbar_adjustment);
+ gtk_widget_show(user_data->dlg.v_scrollbar);
+ gtk_box_pack_start(GTK_BOX(scroll_vbox), user_data->dlg.v_scrollbar, TRUE, TRUE, 0);
+ g_signal_connect(user_data->dlg.v_scrollbar_adjustment, "value_changed",
+ G_CALLBACK(v_scrollbar_changed), user_data);
+
+ frame_box = gtk_frame_new(NULL);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_get_preferred_size(user_data->dlg.v_scrollbar, &scroll_requisition, NULL);
+#else
+ gtk_widget_size_request(user_data->dlg.v_scrollbar, &scroll_requisition);
+#endif
+ gtk_widget_set_size_request(frame_box, 1, scroll_requisition.width+2);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame_box), GTK_SHADOW_NONE);
+ gtk_widget_show(frame_box);
+ gtk_box_pack_end(GTK_BOX(scroll_vbox), frame_box, FALSE, FALSE, 0);
+ gtk_box_pack_end(GTK_BOX(hbox), scroll_vbox, FALSE, FALSE, 3);
+
+ /* Frame around the main area */
+ frame = gtk_frame_new(NULL);
+ gtk_widget_show(frame);
+ gtk_container_add(GTK_CONTAINER(frame), hbox);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
+
+ /*gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 15);*/
+ /*gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 15);*/
+ gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(box), 10);
+}
+/****************************************************************************/
+/* PUBLIC */
+/****************************************************************************/
+
+
+/****************************************************************************/
+static void dialog_graph_create_window(graph_analysis_data_t *user_data)
+{
+ GtkWidget *vbox;
+ GtkWidget *hbuttonbox;
+ GtkWidget *bt_close;
+ GtkWidget *bt_save;
+ const gchar *title_name_ptr;
+ gchar *win_name;
+
+ title_name_ptr = cf_get_display_name(&cfile);
+ win_name = g_strdup_printf("%s - Graph Analysis", title_name_ptr);
+
+ /* create the main window */
+ user_data->dlg.window=dlg_window_new((user_data->dlg.title)?user_data->dlg.title:win_name);
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(user_data->dlg.window), TRUE);
+
+ vbox=gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(user_data->dlg.window), vbox);
+ gtk_widget_show(vbox);
+
+ create_draw_area(user_data, vbox);
+
+ /* button row */
+ hbuttonbox = gtk_hbutton_box_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), hbuttonbox, FALSE, FALSE, 10);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_SPREAD);
+ gtk_box_set_spacing (GTK_BOX (hbuttonbox), 30);
+ gtk_widget_show(hbuttonbox);
+
+ bt_save = gtk_button_new_from_stock(GTK_STOCK_SAVE_AS);
+ gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_save);
+ gtk_widget_show(bt_save);
+ g_signal_connect(bt_save, "clicked", G_CALLBACK(on_save_bt_clicked), user_data);
+ gtk_widget_set_tooltip_text(bt_save, "Save an ASCII representation of the graph to a file");
+
+ bt_close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_close);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(bt_close, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(bt_close, GTK_CAN_DEFAULT);
+#endif
+ gtk_widget_show(bt_close);
+ gtk_widget_set_tooltip_text(bt_close, "Close this dialog");
+ window_set_cancel_button(user_data->dlg.window, bt_close, window_cancel_button_cb);
+
+ g_signal_connect(user_data->dlg.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(user_data->dlg.window, "destroy", G_CALLBACK(on_destroy), user_data);
+
+ gtk_widget_show(user_data->dlg.window);
+ window_present(user_data->dlg.window);
+
+ /* Destroy our graph window with our parent if the caller specified the parent */
+ if(user_data->dlg.parent_w) {
+ gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.window),
+ GTK_WINDOW(user_data->dlg.parent_w));
+ /* Destruction of this child window */
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(user_data->dlg.window), TRUE);
+ }
+ g_free(win_name);
+}
+
+/* Return the index array if the node is in the array. Return -1 if there is room in the array
+ * and Return -2 if the array is full
+ */
+/****************************************************************************/
+static gint add_or_get_node(graph_analysis_data_t *user_data, address *node) {
+ guint i;
+
+ if (node->type == AT_NONE) return NODE_OVERFLOW;
+
+ for (i=0; i<MAX_NUM_NODES && i < user_data->num_nodes ; i++){
+ if ( CMP_ADDRESS(&(user_data->nodes[i]), node) == 0 ) return i; /* it is in the array */
+ }
+
+ if (i == MAX_NUM_NODES) {
+ return NODE_OVERFLOW;
+ } else {
+ user_data->num_nodes++;
+ COPY_ADDRESS(&(user_data->nodes[i]),node);
+ return i;
+ }
+}
+
+/* Get the nodes from the list */
+/****************************************************************************/
+static void get_nodes(graph_analysis_data_t *user_data)
+{
+ GList *list;
+ graph_analysis_item_t *gai;
+
+ /* fill the node array */
+ list = g_list_first(user_data->graph_info->list);
+ while (list)
+ {
+ gai = list->data;
+ if (gai->display) {
+ user_data->num_items++;
+ if (!user_data->dlg.inverse) {
+ gai->src_node = (guint16)add_or_get_node(user_data, &(gai->src_addr));
+ gai->dst_node = (guint16)add_or_get_node(user_data, &(gai->dst_addr));
+ } else {
+ gai->dst_node = (guint16)add_or_get_node(user_data, &(gai->src_addr));
+ gai->src_node = (guint16)add_or_get_node(user_data, &(gai->dst_addr));
+ }
+ }
+ list = g_list_next(list);
+ }
+}
+
+/****************************************************************************/
+graph_analysis_data_t *graph_analysis_init(void)
+{
+ graph_analysis_data_t *user_data;
+ /* init */
+ user_data = g_malloc(sizeof(graph_analysis_data_t));
+
+ /* init user_data */
+ graph_analysis_init_dlg(user_data);
+
+ return user_data;
+}
+/****************************************************************************/
+/* PUBLIC */
+/****************************************************************************/
+
+/****************************************************************************/
+void graph_analysis_create(graph_analysis_data_t *user_data)
+{
+ /* reset the data */
+ graph_analysis_reset(user_data);
+
+ /* get nodes (each node is an address) */
+ get_nodes(user_data);
+
+ /* create the graph windows */
+ dialog_graph_create_window(user_data);
+
+ /* redraw the graph */
+ dialog_graph_redraw(user_data);
+
+ return;
+}
+
+/****************************************************************************/
+void graph_analysis_update(graph_analysis_data_t *user_data)
+{
+ /* reset the data */
+ graph_analysis_reset(user_data);
+
+ /* get nodes (each node is an address) */
+ get_nodes(user_data);
+
+ user_data->dlg.surface_width = user_data->num_nodes * NODE_WIDTH;
+ gtk_widget_set_size_request(user_data->dlg.draw_area, user_data->dlg.surface_width, user_data->dlg.surface_width);
+ if (user_data->num_nodes < 6)
+ gtk_widget_set_size_request(user_data->dlg.scroll_window, NODE_WIDTH*user_data->num_nodes, user_data->dlg.surface_height);
+ else
+ gtk_widget_set_size_request(user_data->dlg.scroll_window, NODE_WIDTH*5, user_data->dlg.surface_height);
+
+ /* redraw the graph */
+ dialog_graph_redraw(user_data);
+
+ window_present(user_data->dlg.window);
+ return;
+}
+
+
+/****************************************************************************/
+void graph_analysis_redraw(graph_analysis_data_t *user_data)
+{
+ /* get nodes (each node is an address) */
+ get_nodes(user_data);
+
+ user_data->dlg.surface_width = user_data->num_nodes * NODE_WIDTH;
+ gtk_widget_set_size_request(user_data->dlg.draw_area, user_data->dlg.surface_width, user_data->dlg.surface_height);
+ if (user_data->num_nodes < 6)
+ gtk_widget_set_size_request(user_data->dlg.scroll_window, NODE_WIDTH*user_data->num_nodes, user_data->dlg.surface_height);
+ else
+ gtk_widget_set_size_request(user_data->dlg.scroll_window, NODE_WIDTH*5, user_data->dlg.surface_height);
+
+
+ /* redraw the graph */
+ dialog_graph_redraw(user_data);
+
+ window_present(user_data->dlg.window);
+ return;
+}
diff --git a/ui/gtk/graph_analysis.h b/ui/gtk/graph_analysis.h
new file mode 100644
index 0000000000..31d46d8786
--- /dev/null
+++ b/ui/gtk/graph_analysis.h
@@ -0,0 +1,136 @@
+/* graph_analysis.h
+ * Graphic Analysis addition for Wireshark
+ *
+ * $Id$
+ *
+ * Copyright 2004, Verso Technologies Inc.
+ * By Alejandro Vaquero <alejandrovaquero@yahoo.com>
+ *
+ * based on rtp_analysis.c and io_stat
+ *
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GRAPH_ANALYSIS_H__
+#define __GRAPH_ANALYSIS_H__
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <epan/address.h>
+
+#define MAX_NUM_NODES 40
+
+/* defines an entry in for the graph analysis */
+typedef struct _graph_analysis_item {
+ frame_data *fd; /* Holds the frame number and time information */
+ address src_addr;
+ guint16 port_src;
+ address dst_addr;
+ guint16 port_dst;
+ gchar *frame_label; /* the label on top of the arrow */
+ gchar *comment; /* a comment that appears at the left of the graph */
+ guint16 conv_num; /* the conversation number, each conversation will be colored */
+ gboolean display; /* indicate if the packet is displayed or not in the graph */
+ guint16 src_node; /* this is used by graph_analysis.c to identify the node */
+ guint16 dst_node; /* a node is an IP address that will be displayed in columns */
+ guint16 line_style; /* the arrow line width in pixels*/
+} graph_analysis_item_t;
+
+/* defines the graph analysis structure */
+typedef struct _graph_analysis_info {
+ int nconv; /* number of conversations in the list */
+ GList* list; /* list with the graph analysis items */
+} graph_analysis_info_t;
+
+/* max number of nodes to display, each node will be an IP address */
+#define MAX_NUM_COL_CONV 10
+#define NODE_OVERFLOW MAX_NUM_NODES+1
+#define NUM_DISPLAY_ITEMS 1000
+
+typedef struct _display_items {
+ frame_data *fd; /* Holds the frame number and time information */
+ guint16 port_src;
+ guint16 port_dst;
+ gchar *frame_label; /* the label on top of the arrow */
+ gchar *comment; /* a comment that appears at the left of the graph */
+ guint16 conv_num; /* the conversation number, each conversation will be colored */
+ guint16 src_node; /* this is used by graph_analysis.c to identify the node */
+ guint16 dst_node; /* a node is an IP address that will be displayed in columns */
+ guint16 line_style; /* the arrow line width in pixels*/
+} display_items_t;
+
+typedef struct _graph_analysis_dialog_data_t {
+ GtkWidget *window;
+ GtkWidget *parent_w;
+ gboolean needs_redraw;
+ gboolean inverse; /* set the nodes in reverse mode as "dst <---- src" instead of "src ----> dst"*/
+ gint selected_row;
+ GtkWidget *draw_area_time;
+ GtkWidget *draw_area;
+ GtkWidget *draw_area_comments;
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_surface_t *surface_time;
+ cairo_surface_t *surface_main;
+ cairo_surface_t *surface_comments;
+ cairo_surface_t *surface_tile_select;
+#else
+ GdkPixmap *pixmap_time;
+ GdkPixmap *pixmap_main;
+ GdkPixmap *pixmap_comments;
+ GdkPixmap *pixmap_tile_select;
+#endif
+ GtkWidget *scroll_window;
+ GtkWidget *v_scrollbar;
+ GtkAdjustment *v_scrollbar_adjustment;
+ GtkWidget *hpane;
+ int surface_width;
+ int surface_height;
+ guint16 first_node; /* the first node on the left to show in the screen */
+ guint32 first_item; /* the first item (row) to show from the top */
+ guint32 selected_item; /* the selected item */
+ display_items_t items[NUM_DISPLAY_ITEMS];
+ guint32 left_x_border;
+ char *save_file;
+ char *title; /* Graph analysis window's title */
+} graph_analysis_dialog_data_t;
+
+typedef void (*destroy_user_data_cb)(void *data);
+
+/* structure that holds general information and the dialog */
+typedef struct _graph_analysis_data_t {
+ /* graphic data */
+ graph_analysis_info_t *graph_info;
+
+ /* dialog associated data */
+ graph_analysis_dialog_data_t dlg;
+ address nodes[MAX_NUM_NODES];
+ guint32 num_nodes;
+ guint32 num_items;
+ destroy_user_data_cb on_destroy_user_data; /* callback info for destroy */
+ void *data; /* data to be passes when on destroy */
+} graph_analysis_data_t;
+
+graph_analysis_data_t* graph_analysis_init(void);
+void graph_analysis_create(graph_analysis_data_t* user_data);
+void graph_analysis_update(graph_analysis_data_t* user_data);
+void graph_analysis_redraw(graph_analysis_data_t* user_data);
+
+
+#endif /* __GRAPH_ANALYSIS_H__ */
diff --git a/ui/gtk/gsm_a_stat.c b/ui/gtk/gsm_a_stat.c
new file mode 100644
index 0000000000..5f39e516d7
--- /dev/null
+++ b/ui/gtk/gsm_a_stat.c
@@ -0,0 +1,671 @@
+/* gsm_a_stat.c
+ *
+ * Copyright 2003, Michael Lum <mlum [AT] telostech.com>
+ * In association with Telos Technology Inc.
+ *
+ * MUCH code modified from service_response_time_table.c.
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * This TAP provides statistics for the GSM A-Interface:
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/packet_info.h"
+#include "epan/epan.h"
+#include "epan/value_string.h"
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-bssap.h>
+#include <epan/dissectors/packet-gsm_a_common.h>
+
+#include "../stat_menu.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/gui_utils.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+enum
+{
+ IEI_COLUMN,
+ MSG_NAME_COLUMN,
+ COUNT_COLUMN,
+ N_COLUMN /* The number of columns */
+};
+
+typedef struct _gsm_a_stat_dlg_t {
+ GtkWidget *win;
+ GtkWidget *scrolled_win;
+ GtkWidget *table;
+} gsm_a_stat_dlg_t;
+
+typedef struct _gsm_a_stat_t {
+ int bssmap_message_type[0xff];
+ int dtap_mm_message_type[0xff];
+ int dtap_rr_message_type[0xff];
+ int dtap_cc_message_type[0xff];
+ int dtap_gmm_message_type[0xff];
+ int dtap_sms_message_type[0xff];
+ int dtap_sm_message_type[0xff];
+ int dtap_ss_message_type[0xff];
+ int dtap_tp_message_type[0xff];
+ int sacch_rr_message_type[0xff];
+} gsm_a_stat_t;
+
+
+static gsm_a_stat_dlg_t dlg_bssmap;
+static gsm_a_stat_dlg_t dlg_dtap_mm;
+static gsm_a_stat_dlg_t dlg_dtap_rr;
+static gsm_a_stat_dlg_t dlg_dtap_cc;
+static gsm_a_stat_dlg_t dlg_dtap_gmm;
+static gsm_a_stat_dlg_t dlg_dtap_sms;
+static gsm_a_stat_dlg_t dlg_dtap_sm;
+static gsm_a_stat_dlg_t dlg_dtap_ss;
+static gsm_a_stat_dlg_t dlg_dtap_tp;
+static gsm_a_stat_dlg_t dlg_sacch_rr;
+static gsm_a_stat_t gsm_a_stat;
+
+/* Create list */
+static
+GtkWidget* create_list(void)
+{
+
+ GtkListStore *list_store;
+ GtkWidget *list;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSortable *sortable;
+ GtkTreeView *list_view;
+ GtkTreeSelection *selection;
+
+ /* Create the store */
+ list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX*/
+ G_TYPE_UINT, /* IEI */
+ G_TYPE_STRING, /* Message Name */
+ G_TYPE_UINT); /* Count */
+
+ /* Create a view */
+ list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
+
+ list_view = GTK_TREE_VIEW(list);
+ sortable = GTK_TREE_SORTABLE(list_store);
+
+ /* Speed up the list display */
+ gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
+
+ /* Setup the sortable columns */
+ gtk_tree_sortable_set_sort_column_id(sortable, IEI_COLUMN, GTK_SORT_ASCENDING);
+ gtk_tree_view_set_headers_clickable(list_view, FALSE);
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref (G_OBJECT (list_store));
+
+ /*
+ * Create the first column packet, associating the "text" attribute of the
+ * cell_renderer to the first column of the model
+ */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("IEI", renderer,
+ "text", IEI_COLUMN,
+ NULL);
+
+ /* gtk_tree_view_column_set_cell_data_func(column, renderer, present_as_hex_func,
+ GINT_TO_POINTER(IEI_COLUMN), NULL);
+ */
+
+ gtk_tree_view_column_set_sort_column_id(column, IEI_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 50);
+
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Second column.. Message Name. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Message Name", renderer,
+ "text", MSG_NAME_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, MSG_NAME_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 280);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Third column.. Count. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Count", renderer,
+ "text", COUNT_COLUMN,
+ NULL);
+
+
+ gtk_tree_view_column_set_sort_column_id(column, COUNT_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 50);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Now enable the sorting of each column */
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
+
+ /* Setup the selection handler */
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+
+ return list;
+
+}
+static void
+gsm_a_stat_reset(
+ void *tapdata)
+{
+ gsm_a_stat_t *stat_p = tapdata;
+
+ memset(stat_p, 0, sizeof(gsm_a_stat_t));
+}
+
+
+static gboolean
+gsm_a_stat_packet(
+ void *tapdata,
+ packet_info *pinfo _U_,
+ epan_dissect_t *edt _U_,
+ const void *data)
+{
+ gsm_a_stat_t *stat_p = tapdata;
+ const gsm_a_tap_rec_t *data_p = data;
+
+ switch (data_p->pdu_type)
+ {
+ case BSSAP_PDU_TYPE_BSSMAP:
+ stat_p->bssmap_message_type[data_p->message_type]++;
+ break;
+
+ case BSSAP_PDU_TYPE_DTAP:
+ switch (data_p->protocol_disc)
+ {
+ case PD_CC:
+ stat_p->dtap_cc_message_type[data_p->message_type]++;
+ break;
+ case PD_MM:
+ stat_p->dtap_mm_message_type[data_p->message_type]++;
+ break;
+ case PD_RR:
+ stat_p->dtap_rr_message_type[data_p->message_type]++;
+ break;
+ case PD_GMM:
+ stat_p->dtap_gmm_message_type[data_p->message_type]++;
+ break;
+ case PD_SMS:
+ stat_p->dtap_sms_message_type[data_p->message_type]++;
+ break;
+ case PD_SM:
+ stat_p->dtap_sm_message_type[data_p->message_type]++;
+ break;
+ case PD_SS:
+ stat_p->dtap_ss_message_type[data_p->message_type]++;
+ break;
+ case PD_TP:
+ stat_p->dtap_tp_message_type[data_p->message_type]++;
+ break;
+ default:
+ /*
+ * unsupported PD
+ */
+ return(FALSE);
+ }
+ break;
+
+ case GSM_A_PDU_TYPE_SACCH:
+ switch (data_p->protocol_disc)
+ {
+ case 0:
+ stat_p->sacch_rr_message_type[data_p->message_type]++;
+ break;
+ default:
+ /* unknown Short PD */
+ break;
+ }
+ break;
+
+ default:
+ /*
+ * unknown PDU type !!!
+ */
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+
+static void
+gsm_a_stat_draw_aux(
+ gsm_a_stat_dlg_t *dlg_p,
+ int *message_count,
+ const value_string *msg_strings)
+{
+ GtkListStore *list_store;
+ GtkTreeIter iter;
+ int i;
+
+
+ if (dlg_p->win != NULL){
+ i = 0;
+ list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (dlg_p->table))); /* Get store */
+
+ while (msg_strings[i].strptr){
+ /* Creates a new row at position. iter will be changed to point to this new row.
+ * If position is larger than the number of rows on the list, then the new row will be appended to the list.
+ * The row will be filled with the values given to this function.
+ * :
+ * should generally be preferred when inserting rows in a sorted list store.
+ */
+ gtk_list_store_insert_with_values( list_store , &iter, G_MAXINT,
+ IEI_COLUMN, msg_strings[i].value,
+ MSG_NAME_COLUMN, (char *)msg_strings[i].strptr,
+ COUNT_COLUMN, message_count[msg_strings[i].value],
+ -1);
+ i++;
+ }
+ }
+}
+
+static void
+gsm_a_stat_draw(
+ void *tapdata)
+{
+ gsm_a_stat_t *stat_p = tapdata;
+
+ if (!tapdata) return;
+
+ if (dlg_bssmap.win != NULL)
+ {
+ gsm_a_stat_draw_aux(&dlg_bssmap,
+ stat_p->bssmap_message_type,
+ gsm_a_bssmap_msg_strings);
+ }
+
+ if (dlg_dtap_mm.win != NULL)
+ {
+ gsm_a_stat_draw_aux(&dlg_dtap_mm,
+ stat_p->dtap_mm_message_type,
+ gsm_a_dtap_msg_mm_strings);
+ }
+
+ if (dlg_dtap_rr.win != NULL)
+ {
+ gsm_a_stat_draw_aux(&dlg_dtap_rr,
+ stat_p->dtap_rr_message_type,
+ gsm_a_dtap_msg_rr_strings);
+ }
+
+ if (dlg_dtap_cc.win != NULL)
+ {
+ gsm_a_stat_draw_aux(&dlg_dtap_cc,
+ stat_p->dtap_cc_message_type,
+ gsm_a_dtap_msg_cc_strings);
+ }
+
+ if (dlg_dtap_gmm.win != NULL)
+ {
+ gsm_a_stat_draw_aux(&dlg_dtap_gmm,
+ stat_p->dtap_gmm_message_type,
+ gsm_a_dtap_msg_gmm_strings);
+ }
+
+ if (dlg_dtap_sms.win != NULL)
+ {
+ gsm_a_stat_draw_aux(&dlg_dtap_sms,
+ stat_p->dtap_sms_message_type,
+ gsm_a_dtap_msg_sms_strings);
+ }
+
+ if (dlg_dtap_sm.win != NULL)
+ {
+ gsm_a_stat_draw_aux(&dlg_dtap_sm,
+ stat_p->dtap_sm_message_type,
+ gsm_a_dtap_msg_sm_strings);
+ }
+
+ if (dlg_dtap_ss.win != NULL)
+ {
+ gsm_a_stat_draw_aux(&dlg_dtap_ss,
+ stat_p->dtap_ss_message_type,
+ gsm_a_dtap_msg_ss_strings);
+ }
+
+ if (dlg_dtap_tp.win != NULL)
+ {
+ gsm_a_stat_draw_aux(&dlg_dtap_tp,
+ stat_p->dtap_tp_message_type,
+ gsm_a_dtap_msg_tp_strings);
+ }
+
+ if (dlg_sacch_rr.win != NULL)
+ {
+ gsm_a_stat_draw_aux(&dlg_sacch_rr,
+ stat_p->sacch_rr_message_type,
+ gsm_a_rr_short_pd_msg_strings);
+ }
+}
+
+
+
+static void
+gsm_a_stat_gtk_win_destroy_cb(
+ GtkWindow *win _U_,
+ gpointer user_data)
+{
+ memset((void *) user_data, 0, sizeof(gsm_a_stat_dlg_t));
+}
+
+
+static void
+gsm_a_stat_gtk_win_create(
+ gsm_a_stat_dlg_t *dlg_p,
+ const char *title)
+{
+ GtkWidget *vbox;
+ GtkWidget *bt_close;
+ GtkWidget *bbox;
+
+
+ dlg_p->win = dlg_window_new(title); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(dlg_p->win), TRUE);
+ gtk_window_set_default_size(GTK_WINDOW(dlg_p->win), 490, 500);
+
+ vbox = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(dlg_p->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ dlg_p->scrolled_win = scrolled_window_new(NULL, NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), dlg_p->scrolled_win, TRUE, TRUE, 0);
+
+ dlg_p->table = create_list();
+ gtk_container_add(GTK_CONTAINER(dlg_p->scrolled_win), dlg_p->table);
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ bt_close = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(dlg_p->win, bt_close, window_cancel_button_cb);
+
+ g_signal_connect(dlg_p->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(dlg_p->win, "destroy", G_CALLBACK(gsm_a_stat_gtk_win_destroy_cb), dlg_p);
+
+ gtk_widget_show_all(dlg_p->win);
+ window_present(dlg_p->win);
+}
+
+void gsm_a_stat_gtk_bssmap_cb(GtkAction *action _U_, gpointer user_data _U_ )
+{
+ /* int i;*/
+
+
+ /*
+ * if the window is already open, bring it to front
+ */
+ if (dlg_bssmap.win)
+ {
+ gdk_window_raise(gtk_widget_get_window(dlg_bssmap.win));
+ return;
+ }
+
+ gsm_a_stat_gtk_win_create(&dlg_bssmap, "GSM A-I/F BSSMAP Statistics");
+ gsm_a_stat_draw(&gsm_a_stat);
+}
+
+
+static void
+gsm_a_stat_gtk_bssmap_init(
+ const char *optarg _U_,
+ void* userdata _U_)
+{
+ gsm_a_stat_gtk_bssmap_cb(NULL, NULL);
+}
+
+static void
+gsm_a_stat_gtk_dtap_cb(
+ GtkAction *action _U_,
+ gpointer user_data _U_,
+ gsm_a_stat_dlg_t *dlg_dtap_p,
+ const char *title,
+ const value_string *dtap_msg_strings _U_)
+{
+
+ /*
+ * if the window is already open, bring it to front
+ */
+ if (dlg_dtap_p->win)
+ {
+ gdk_window_raise(gtk_widget_get_window(dlg_dtap_p->win));
+ return;
+ }
+
+ gsm_a_stat_gtk_win_create(dlg_dtap_p, title);
+
+ gsm_a_stat_draw(&gsm_a_stat);
+}
+
+void
+gsm_a_stat_gtk_dtap_mm_cb(GtkAction *action, gpointer user_data )
+{
+ gsm_a_stat_gtk_dtap_cb(action, user_data, &dlg_dtap_mm,
+ "GSM A-I/F DTAP Mobility Management Statistics",
+ gsm_a_dtap_msg_mm_strings);
+}
+
+static void
+gsm_a_stat_gtk_dtap_mm_init(const char *optarg _U_,
+ void* userdata _U_)
+{
+ gsm_a_stat_gtk_dtap_mm_cb(NULL, NULL);
+}
+
+void
+gsm_a_stat_gtk_dtap_rr_cb(GtkAction *action, gpointer user_data )
+{
+ gsm_a_stat_gtk_dtap_cb(action, user_data, &dlg_dtap_rr,
+ "GSM A-I/F DTAP Radio Resource Management Statistics",
+ gsm_a_dtap_msg_rr_strings);
+}
+
+static void
+gsm_a_stat_gtk_dtap_rr_init(const char *optarg _U_,
+ void* userdata _U_)
+{
+ gsm_a_stat_gtk_dtap_rr_cb(NULL, NULL);
+}
+
+void
+gsm_a_stat_gtk_dtap_cc_cb(GtkAction *action, gpointer user_data )
+{
+ gsm_a_stat_gtk_dtap_cb(action, user_data, &dlg_dtap_cc,
+ "GSM A-I/F DTAP Call Control Statistics",
+ gsm_a_dtap_msg_cc_strings);
+}
+
+static void
+gsm_a_stat_gtk_dtap_cc_init(const char *optarg _U_,
+ void* userdata _U_)
+{
+ gsm_a_stat_gtk_dtap_cc_cb(NULL, NULL);
+}
+
+void
+gsm_a_stat_gtk_dtap_gmm_cb(GtkAction *action, gpointer user_data )
+{
+ gsm_a_stat_gtk_dtap_cb(action, user_data, &dlg_dtap_gmm,
+ "GSM A-I/F DTAP GPRS Mobility Management Statistics",
+ gsm_a_dtap_msg_gmm_strings);
+}
+
+static void
+gsm_a_stat_gtk_dtap_gmm_init(const char *optarg _U_,
+ void* userdata _U_)
+{
+ gsm_a_stat_gtk_dtap_gmm_cb(NULL, NULL);
+}
+
+void
+gsm_a_stat_gtk_dtap_sms_cb(GtkAction *action, gpointer user_data )
+{
+ gsm_a_stat_gtk_dtap_cb(action, user_data, &dlg_dtap_sms,
+ "GSM A-I/F DTAP Short Message Service Statistics",
+ gsm_a_dtap_msg_sms_strings);
+}
+
+static void
+gsm_a_stat_gtk_dtap_sms_init(const char *optarg _U_,
+ void* userdata _U_)
+{
+ gsm_a_stat_gtk_dtap_sms_cb(NULL, NULL);
+}
+
+void
+gsm_a_stat_gtk_dtap_sm_cb(GtkAction *action, gpointer user_data )
+{
+ gsm_a_stat_gtk_dtap_cb(action, user_data, &dlg_dtap_sm,
+ "GSM A-I/F DTAP GPRS Session Management Statistics",
+ gsm_a_dtap_msg_sm_strings);
+}
+
+static void
+gsm_a_stat_gtk_dtap_sm_init(const char *optarg _U_,
+ void* userdata _U_)
+{
+ gsm_a_stat_gtk_dtap_sm_cb(NULL, NULL);
+}
+
+void
+gsm_a_stat_gtk_dtap_ss_cb(GtkAction *action, gpointer user_data )
+{
+ gsm_a_stat_gtk_dtap_cb(action, user_data, &dlg_dtap_ss,
+ "GSM A-I/F DTAP Supplementary Services Statistics",
+ gsm_a_dtap_msg_ss_strings);
+}
+
+static void
+gsm_a_stat_gtk_dtap_ss_init(
+ const char *optarg _U_,
+ void *userdata _U_)
+{
+ gsm_a_stat_gtk_dtap_ss_cb(NULL, NULL);
+}
+
+void
+gsm_a_stat_gtk_dtap_tp_cb(GtkAction *action, gpointer user_data )
+{
+ gsm_a_stat_gtk_dtap_cb(action, user_data, &dlg_dtap_tp,
+ "GSM A-I/F DTAP Special Conformance Testing Functions Statistics",
+ gsm_a_dtap_msg_tp_strings);
+}
+
+static void
+gsm_a_stat_gtk_dtap_tp_init(
+ const char *optarg _U_,
+ void *userdata _U_)
+{
+ gsm_a_stat_gtk_dtap_tp_cb(NULL, NULL);
+}
+
+void
+gsm_a_stat_gtk_sacch_rr_cb(GtkAction *action _U_, gpointer user_data _U_ )
+{
+
+ /*
+ * if the window is already open, bring it to front
+ */
+ if (dlg_sacch_rr.win)
+ {
+ gdk_window_raise(gtk_widget_get_window(dlg_sacch_rr.win));
+ return;
+ }
+
+ gsm_a_stat_gtk_win_create(&dlg_sacch_rr, "GSM A-I/F SACCH Statistics");
+ gsm_a_stat_draw(&gsm_a_stat);
+}
+
+static void
+gsm_a_stat_gtk_sacch_rr_init(
+ const char *optarg _U_,
+ void* userdata _U_)
+{
+ gsm_a_stat_gtk_sacch_rr_cb(NULL, NULL);
+}
+
+void
+register_tap_listener_gtkgsm_a_stat(void)
+{
+ GString *err_p;
+
+
+ memset((void *) &gsm_a_stat, 0, sizeof(gsm_a_stat_t));
+
+ err_p =
+ register_tap_listener("gsm_a", &gsm_a_stat, NULL, 0,
+ gsm_a_stat_reset,
+ gsm_a_stat_packet,
+ gsm_a_stat_draw);
+
+ if (err_p != NULL)
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_p->str);
+ g_string_free(err_p, TRUE);
+
+ exit(1);
+ }
+
+ register_stat_cmd_arg("gsm_a,bssmap", gsm_a_stat_gtk_bssmap_init,NULL);
+
+ register_stat_cmd_arg("gsm_a,dtap_mm", gsm_a_stat_gtk_dtap_mm_init,NULL);
+
+ register_stat_cmd_arg("gsm_a,dtap_rr", gsm_a_stat_gtk_dtap_rr_init,NULL);
+
+ register_stat_cmd_arg("gsm_a,dtap_cc", gsm_a_stat_gtk_dtap_cc_init,NULL);
+
+ register_stat_cmd_arg("gsm_a,dtap_gmm", gsm_a_stat_gtk_dtap_gmm_init,NULL);
+
+ register_stat_cmd_arg("gsm_a,dtap_sms", gsm_a_stat_gtk_dtap_sms_init,NULL);
+
+ register_stat_cmd_arg("gsm_a,dtap_sm", gsm_a_stat_gtk_dtap_sm_init,NULL);
+
+ register_stat_cmd_arg("gsm_a,dtap_ss", gsm_a_stat_gtk_dtap_ss_init,NULL);
+
+ register_stat_cmd_arg("gsm_a,dtap_tp", gsm_a_stat_gtk_dtap_tp_init,NULL);
+
+ register_stat_cmd_arg("gsm_a,sacch", gsm_a_stat_gtk_sacch_rr_init,NULL);
+}
diff --git a/ui/gtk/gsm_map_stat.c b/ui/gtk/gsm_map_stat.c
new file mode 100644
index 0000000000..4da736065b
--- /dev/null
+++ b/ui/gtk/gsm_map_stat.c
@@ -0,0 +1,477 @@
+/* gsm_map_stat.c
+ *
+ * Copyright 2004, Michael Lum <mlum [AT] telostech.com>
+ * In association with Telos Technology Inc.
+ *
+ * MUCH code modified from service_response_time_table.c.
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * This TAP provides statistics for GSM MAP Operations:
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/epan.h>
+#include <epan/packet.h>
+#include <epan/packet_info.h>
+#include <epan/value_string.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/asn1.h>
+#include <epan/dissectors/packet-gsm_map.h>
+
+#include "../stat_menu.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/gsm_map_stat.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+enum
+{
+ ID_COLUMN,
+ OP_CODE_COLUMN,
+ INVOKES_COLUMN,
+ NUM_BYTES_FWD_COLUMN,
+ AVG_BYTES_FWD_COLUMN,
+ RET_RES_COLUMN,
+ NUM_BYTES_REV_COLUMN,
+ AVG_BYTES_REV_COLUMN,
+ TOT_BYTES_COLUMN,
+ AVG_BYTES_COLUMN,
+ N_COLUMN /* The number of columns */
+};
+
+/* Create list */
+static
+GtkWidget* create_list(void)
+{
+
+ GtkListStore *list_store;
+ GtkWidget *list;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSortable *sortable;
+ GtkTreeView *list_view;
+ GtkTreeSelection *selection;
+
+ /* Create the store */
+ list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX*/
+ G_TYPE_UINT, /* ID */
+ G_TYPE_STRING, /* Operation Code */
+ G_TYPE_INT, /* Invokes */
+ G_TYPE_INT, /* Num Bytes */
+ G_TYPE_FLOAT, /* Avg Bytes */
+ G_TYPE_INT, /* RetResult */
+ G_TYPE_INT, /* Num Bytes */
+ G_TYPE_FLOAT, /* Avg Bytes */
+ G_TYPE_INT, /* Total Bytes */
+ G_TYPE_FLOAT); /* Avg Bytes */
+
+ /* Create a view */
+ list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
+
+ list_view = GTK_TREE_VIEW(list);
+ sortable = GTK_TREE_SORTABLE(list_store);
+
+ /* Speed up the list display */
+ gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
+
+ /* Setup the sortable columns */
+ gtk_tree_sortable_set_sort_column_id(sortable, ID_COLUMN, GTK_SORT_ASCENDING);
+ gtk_tree_view_set_headers_clickable(list_view, FALSE);
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref (G_OBJECT (list_store));
+
+ /*
+ * Create the first column packet, associating the "text" attribute of the
+ * cell_renderer to the first column of the model
+ */
+ /* 1:st column */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("ID", renderer,
+ "text", ID_COLUMN,
+ NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, ID_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 40);
+
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 2:nd column..Operation Code. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Operation Code", renderer,
+ "text", OP_CODE_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, OP_CODE_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 210);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 3:d column..Invokes. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Invokes", renderer,
+ "text", INVOKES_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, INVOKES_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 4:th column.. Num Bytes. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Num Bytes", renderer,
+ "text", NUM_BYTES_FWD_COLUMN,
+ NULL);
+
+
+ gtk_tree_view_column_set_sort_column_id(column, NUM_BYTES_FWD_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 5:th column.. Avg Bytes. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Avg Bytes", renderer,
+ "text", AVG_BYTES_FWD_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
+ GINT_TO_POINTER(AVG_BYTES_FWD_COLUMN), NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, AVG_BYTES_FWD_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 6:d column..Invokes. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("ReturnResult", renderer,
+ "text", RET_RES_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, RET_RES_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 7:th column.. Num Bytes. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Num Bytes", renderer,
+ "text", NUM_BYTES_REV_COLUMN,
+ NULL);
+
+
+ gtk_tree_view_column_set_sort_column_id(column, NUM_BYTES_FWD_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 8:th column.. Avg Bytes. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Avg Bytes", renderer,
+ "text", AVG_BYTES_REV_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
+ GINT_TO_POINTER(AVG_BYTES_REV_COLUMN), NULL);
+
+
+ gtk_tree_view_column_set_sort_column_id(column, AVG_BYTES_REV_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 9:th column.. Total Bytes. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Total Bytes", renderer,
+ "text", TOT_BYTES_COLUMN,
+ NULL);
+
+
+ gtk_tree_view_column_set_sort_column_id(column, NUM_BYTES_FWD_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 10:th column.. Avg Bytes. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Avg Bytes", renderer,
+ "text", AVG_BYTES_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
+ GINT_TO_POINTER(AVG_BYTES_COLUMN), NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, AVG_BYTES_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Now enable the sorting of each column */
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
+
+ /* Setup the selection handler */
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+
+ return list;
+
+}
+
+typedef struct _gsm_map_stat_dlg_t {
+ GtkWidget *win;
+ GtkWidget *scrolled_win;
+ GtkWidget *table;
+} gsm_map_stat_dlg_t;
+
+static gsm_map_stat_dlg_t dlg;
+
+/*
+ * used by gsm_map_summary.c
+ */
+gsm_map_stat_t gsm_map_stat;
+
+
+static void
+gsm_map_stat_reset(
+ void *tapdata)
+{
+ gsm_map_stat_t *stat_p = tapdata;
+
+ memset(stat_p, 0, sizeof(gsm_map_stat_t));
+}
+
+
+static gboolean
+gsm_map_stat_packet(
+ void *tapdata,
+ packet_info *pinfo _U_,
+ epan_dissect_t *edt _U_,
+ const void *data)
+{
+ gsm_map_stat_t *stat_p = tapdata;
+ const gsm_map_tap_rec_t *data_p = data;
+
+#if 0 /* always false because message_type is 8 bit value */
+ if (data_p->opr_code_idx > sizeof(stat_p->opr_code))
+ {
+ /*
+ * unknown message type !!!
+ */
+ return(FALSE);
+ }
+#endif
+
+ if (data_p->invoke)
+ {
+ stat_p->opr_code[data_p->opr_code_idx]++;
+ stat_p->size[data_p->opr_code_idx] += data_p->size;
+ }
+ else
+ {
+ stat_p->opr_code_rr[data_p->opr_code_idx]++;
+ stat_p->size_rr[data_p->opr_code_idx] += data_p->size;
+ }
+
+ return(TRUE);
+}
+
+
+static void
+gsm_map_stat_draw(
+ void *tapdata)
+{
+ gsm_map_stat_t *stat_p = tapdata;
+ int i;
+ GtkListStore *list_store;
+ GtkTreeIter iter;
+
+ if (dlg.win && tapdata)
+ {
+ list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (dlg.table))); /* Get store */
+
+ i = 0;
+ while (gsm_map_opr_code_strings[i].strptr){
+ float avrage_bytes_fwd;
+ float avrage_bytes_rev;
+ float avrage_bytes_tot;
+
+ if (stat_p->opr_code[i] >0){
+ avrage_bytes_fwd =(float)stat_p->size[i]/(float)stat_p->opr_code[i];
+ }else{
+ avrage_bytes_fwd = 0;
+ }
+ if (stat_p->opr_code_rr[i] >0){
+ avrage_bytes_rev = (float)stat_p->size_rr[i]/(float)stat_p->opr_code_rr[i];
+ }else{
+ avrage_bytes_rev = 0;
+ }
+ if ((stat_p->opr_code[i] + stat_p->opr_code_rr[i])>0){
+ avrage_bytes_tot = (float)(stat_p->size[i] +stat_p->size_rr[i])/(float)(stat_p->opr_code[i] + stat_p->opr_code_rr[i]);
+ }else{
+ avrage_bytes_tot = 0;
+ }
+ /* Creates a new row at position. iter will be changed to point to this new row.
+ * If position is larger than the number of rows on the list, then the new row will be appended to the list.
+ * The row will be filled with the values given to this function.
+ * :
+ * should generally be preferred when inserting rows in a sorted list store.
+ */
+ gtk_list_store_insert_with_values( list_store , &iter, G_MAXINT,
+ ID_COLUMN, gsm_map_opr_code_strings[i].value,
+ OP_CODE_COLUMN, (char*)gsm_map_opr_code_strings[i].strptr,
+ INVOKES_COLUMN, stat_p->opr_code[i],
+ NUM_BYTES_FWD_COLUMN, (gint)stat_p->size[i],
+ AVG_BYTES_FWD_COLUMN, avrage_bytes_fwd,
+ RET_RES_COLUMN, stat_p->opr_code_rr[i],
+ NUM_BYTES_REV_COLUMN, stat_p->size_rr[i],
+ AVG_BYTES_REV_COLUMN, avrage_bytes_rev,
+ TOT_BYTES_COLUMN, stat_p->size[i] + stat_p->size_rr[i],
+ AVG_BYTES_COLUMN, avrage_bytes_tot,
+ -1);
+ i++;
+ }
+ }
+}
+
+static void
+gsm_map_stat_gtk_win_destroy_cb(
+ GtkWindow *win _U_,
+ gpointer user_data)
+{
+ memset((void *) user_data, 0, sizeof(gsm_map_stat_dlg_t));
+}
+
+
+static void
+gsm_map_stat_gtk_win_create(
+ gsm_map_stat_dlg_t *dlg_p,
+ const char *title)
+{
+ GtkWidget *vbox;
+ GtkWidget *bt_close;
+ GtkWidget *bbox;
+
+
+ dlg_p->win = dlg_window_new(title); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(dlg_p->win), TRUE);
+ gtk_window_set_default_size(GTK_WINDOW(dlg_p->win), 560, 390);
+
+ vbox = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(dlg_p->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ dlg_p->scrolled_win = scrolled_window_new(NULL, NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), dlg_p->scrolled_win, TRUE, TRUE, 0);
+
+ dlg_p->table = create_list();
+ gtk_widget_show(dlg_p->table);
+
+ gtk_container_add(GTK_CONTAINER(dlg_p->scrolled_win), dlg_p->table);
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ bt_close = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(dlg_p->win, bt_close, window_cancel_button_cb);
+
+ g_signal_connect(dlg_p->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(dlg_p->win, "destroy", G_CALLBACK(gsm_map_stat_gtk_win_destroy_cb), dlg_p);
+
+ gtk_widget_show_all(dlg_p->win);
+ window_present(dlg_p->win);
+}
+
+void
+gsm_map_stat_gtk_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+
+
+ /*
+ * if the window is already open, bring it to front
+ */
+ if (dlg.win){
+ gdk_window_raise(gtk_widget_get_window(dlg.win));
+ return;
+ }
+
+ gsm_map_stat_gtk_win_create(&dlg, "GSM MAP Operation Statistics");
+
+ gsm_map_stat_draw(&gsm_map_stat);
+}
+
+
+static void
+gsm_map_stat_gtk_init(const char *optarg _U_,
+ void* userdata _U_)
+{
+ gsm_map_stat_gtk_cb(NULL, NULL);
+}
+
+
+void
+register_tap_listener_gtkgsm_map_stat(void)
+{
+ GString *err_p;
+
+
+ memset((void *) &gsm_map_stat, 0, sizeof(gsm_map_stat_t));
+
+ err_p =
+ register_tap_listener("gsm_map", &gsm_map_stat, NULL, 0,
+ gsm_map_stat_reset,
+ gsm_map_stat_packet,
+ gsm_map_stat_draw);
+
+ if (err_p != NULL)
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_p->str);
+ g_string_free(err_p, TRUE);
+
+ exit(1);
+ }
+
+ register_stat_cmd_arg("gsm_map", gsm_map_stat_gtk_init,NULL);
+}
diff --git a/ui/gtk/gsm_map_stat.h b/ui/gtk/gsm_map_stat.h
new file mode 100644
index 0000000000..f5290c77ee
--- /dev/null
+++ b/ui/gtk/gsm_map_stat.h
@@ -0,0 +1,46 @@
+/* gsm_map_stat.h
+ *
+ * $Id$
+ *
+ * Copyright 2004, Michael Lum <mlum [AT] telostech.com>,
+ * In association with Telos Technology Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GSM_MAP_STAT_H__
+#define __GSM_MAP_STAT_H__
+
+/** @file
+ * Statistics for GSM MAP Operations.
+ */
+
+/** Gsm map statistic data */
+typedef struct _gsm_map_stat_t {
+ int opr_code[GSM_MAP_MAX_NUM_OPR_CODES];
+ int size[GSM_MAP_MAX_NUM_OPR_CODES];
+
+ int opr_code_rr[GSM_MAP_MAX_NUM_OPR_CODES];
+ int size_rr[GSM_MAP_MAX_NUM_OPR_CODES];
+} gsm_map_stat_t;
+
+/** Global gsm map statistic data */
+extern gsm_map_stat_t gsm_map_stat;
+
+#endif /* __GSM_MAP_STAT_H__ */
diff --git a/ui/gtk/gsm_map_summary.c b/ui/gtk/gsm_map_summary.c
new file mode 100644
index 0000000000..6651a47661
--- /dev/null
+++ b/ui/gtk/gsm_map_summary.c
@@ -0,0 +1,300 @@
+/* gsm_map_summary.c
+ * Routines for GSM MAP Statictics summary window
+ *
+ * Copyright 2004, Michael Lum <mlum [AT] telostech.com>
+ * In association with Telos Technology Inc.
+ *
+ * Modified from summary_dlg.c
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <gtk/gtk.h>
+
+#include <wiretap/wtap.h>
+
+#include <epan/epan.h>
+#include <epan/packet.h>
+#include <epan/packet_info.h>
+#include <epan/value_string.h>
+#include <epan/tap.h>
+#include <epan/asn1.h>
+#include <epan/dissectors/packet-gsm_map.h>
+
+#include "../stat_menu.h"
+#include "../globals.h"
+#include "../file.h"
+#include "../summary.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/gsm_map_stat.h"
+
+
+#define SUM_STR_MAX 1024
+
+
+static void
+add_string_to_box(gchar *str, GtkWidget *box)
+{
+ GtkWidget *lb;
+ lb = gtk_label_new(str);
+ gtk_misc_set_alignment(GTK_MISC(lb), 0.0f, 0.5f);
+ gtk_box_pack_start(GTK_BOX(box), lb,FALSE,FALSE, 0);
+ gtk_widget_show(lb);
+}
+
+void gsm_map_stat_gtk_sum_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ summary_tally summary;
+ GtkWidget *sum_open_w,
+ *main_vb, *file_fr, *data_fr, *file_box,
+ *data_box, *bbox, *close_bt,
+ *invoke_fr, *invoke_box,
+ *rr_fr, *rr_box,
+ *tot_fr, *tot_box;
+
+ gchar string_buff[SUM_STR_MAX];
+ double seconds;
+ int i;
+ int tot_invokes, tot_rr;
+ double tot_invokes_size, tot_rr_size;
+
+ /* initialize the tally */
+ summary_fill_in(&cfile, &summary);
+
+ /* initial compututations */
+ seconds = summary.stop_time - summary.start_time;
+
+ sum_open_w = dlg_window_new("GSM MAP Statistics: Summary"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(sum_open_w), TRUE);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(sum_open_w), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* File frame */
+ file_fr = gtk_frame_new("File");
+ gtk_container_add(GTK_CONTAINER(main_vb), file_fr);
+ gtk_widget_show(file_fr);
+
+ file_box = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(file_fr), file_box);
+ gtk_widget_show(file_box);
+
+ /* filename */
+ g_snprintf(string_buff, SUM_STR_MAX, "Name: %s", ((summary.filename) ? summary.filename : "None"));
+ add_string_to_box(string_buff, file_box);
+
+ /* length */
+ g_snprintf(string_buff, SUM_STR_MAX, "Length: %" G_GINT64_MODIFIER "d", summary.file_length);
+ add_string_to_box(string_buff, file_box);
+
+ /* format */
+ g_snprintf(string_buff, SUM_STR_MAX, "Format: %s", wtap_file_type_string(summary.file_type));
+ add_string_to_box(string_buff, file_box);
+
+ if (summary.has_snap) {
+ /* snapshot length */
+ g_snprintf(string_buff, SUM_STR_MAX, "Snapshot length: %u", summary.snap);
+ add_string_to_box(string_buff, file_box);
+ }
+
+ /* Data frame */
+ data_fr = gtk_frame_new("Data");
+ gtk_container_add(GTK_CONTAINER(main_vb), data_fr);
+ gtk_widget_show(data_fr);
+
+ data_box = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(data_fr), data_box);
+ gtk_widget_show(data_box);
+
+ /* seconds */
+ g_snprintf(string_buff, SUM_STR_MAX, "Elapsed time: %.3f seconds", summary.elapsed_time);
+ add_string_to_box(string_buff, data_box);
+
+ g_snprintf(string_buff, SUM_STR_MAX, "Between first and last packet: %.3f seconds", seconds);
+ add_string_to_box(string_buff, data_box);
+
+ /* Packet count */
+ g_snprintf(string_buff, SUM_STR_MAX, "Packet count: %i", summary.packet_count);
+ add_string_to_box(string_buff, data_box);
+
+ tot_invokes = 0;
+ tot_invokes_size = 0;
+ for (i=0; i < GSM_MAP_MAX_NUM_OPR_CODES; i++)
+ {
+ tot_invokes += gsm_map_stat.opr_code[i];
+ tot_invokes_size += gsm_map_stat.size[i];
+ }
+
+ tot_rr = 0;
+ tot_rr_size = 0;
+ for (i=0; i < GSM_MAP_MAX_NUM_OPR_CODES; i++)
+ {
+ tot_rr += gsm_map_stat.opr_code_rr[i];
+ tot_rr_size += gsm_map_stat.size_rr[i];
+ }
+
+ /* Invoke frame */
+ invoke_fr = gtk_frame_new("Invokes");
+ gtk_container_add(GTK_CONTAINER(main_vb), invoke_fr);
+ gtk_widget_show(invoke_fr);
+
+ invoke_box = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(invoke_fr), invoke_box);
+ gtk_widget_show(invoke_box);
+
+ /* Total number of invokes */
+ g_snprintf(string_buff, SUM_STR_MAX, "Total number of Invokes: %u", tot_invokes);
+ add_string_to_box(string_buff, invoke_box);
+
+ /* Total number of invokes per second */
+ if (seconds)
+ g_snprintf(string_buff, SUM_STR_MAX, "Total number of Invokes per second: %.2f", tot_invokes/seconds);
+ else
+ g_snprintf(string_buff, SUM_STR_MAX, "Total number of Invokes per second: N/A");
+ add_string_to_box(string_buff, invoke_box);
+
+ /* Total size of invokes */
+ g_snprintf(string_buff, SUM_STR_MAX, "Total number of bytes for Invokes: %.0f", tot_invokes_size);
+ add_string_to_box(string_buff, invoke_box);
+
+ /* Average size of invokes */
+ if (tot_invokes)
+ g_snprintf(string_buff, SUM_STR_MAX, "Average number of bytes per Invoke: %.2f", tot_invokes_size/tot_invokes);
+ else
+ g_snprintf(string_buff, SUM_STR_MAX, "Average number of bytes per Invoke: N/A");
+ add_string_to_box(string_buff, invoke_box);
+
+ /* Average size of invokes per second */
+ if (seconds)
+ g_snprintf(string_buff, SUM_STR_MAX, "Average number of bytes per second: %.2f", tot_invokes_size/seconds);
+ else
+ g_snprintf(string_buff, SUM_STR_MAX, "Average number of bytes per second: N/A");
+ add_string_to_box(string_buff, invoke_box);
+
+ /* Return Results frame */
+ rr_fr = gtk_frame_new("Return Results");
+ gtk_container_add(GTK_CONTAINER(main_vb), rr_fr);
+ gtk_widget_show(rr_fr);
+
+ rr_box = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(rr_fr), rr_box);
+ gtk_widget_show(rr_box);
+
+ /* Total number of return results */
+ g_snprintf(string_buff, SUM_STR_MAX, "Total number of Return Results: %u", tot_rr);
+ add_string_to_box(string_buff, rr_box);
+
+ /* Total number of return results per second */
+ if (seconds)
+ g_snprintf(string_buff, SUM_STR_MAX, "Total number of Return Results per second: %.2f", tot_rr/seconds);
+ else
+ g_snprintf(string_buff, SUM_STR_MAX, "Total number of Return Results per second: N/A");
+ add_string_to_box(string_buff, rr_box);
+
+ /* Total size of return results */
+ g_snprintf(string_buff, SUM_STR_MAX, "Total number of bytes for Return Results: %.0f", tot_rr_size);
+ add_string_to_box(string_buff, rr_box);
+
+ /* Average size of return results */
+ if (tot_rr)
+ g_snprintf(string_buff, SUM_STR_MAX, "Average number of bytes per Return Result: %.2f", tot_rr_size/tot_rr);
+ else
+ g_snprintf(string_buff, SUM_STR_MAX, "Average number of bytes per Return Result: N/A");
+ add_string_to_box(string_buff, rr_box);
+
+ /* Average size of return results per second */
+ if (seconds)
+ g_snprintf(string_buff, SUM_STR_MAX, "Average number of bytes per second: %.2f", tot_rr_size/seconds);
+ else
+ g_snprintf(string_buff, SUM_STR_MAX, "Average number of bytes per second: N/A");
+ add_string_to_box(string_buff, rr_box);
+
+ /* Totals frame */
+ tot_fr = gtk_frame_new("Totals");
+ gtk_container_add(GTK_CONTAINER(main_vb), tot_fr);
+ gtk_widget_show(tot_fr);
+
+ tot_box = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(tot_fr), tot_box);
+ gtk_widget_show(tot_box);
+
+ /* Total number of return results */
+ g_snprintf(string_buff, SUM_STR_MAX, "Total number of GSM MAP messages: %u", tot_invokes + tot_rr);
+ add_string_to_box(string_buff, tot_box);
+
+ if (seconds)
+ g_snprintf(string_buff, SUM_STR_MAX, "Total number of GSM MAP messages per second: %.2f",
+ (tot_invokes + tot_rr)/seconds);
+ else
+ g_snprintf(string_buff, SUM_STR_MAX, "Total number of GSM MAP messages per second: N/A");
+ add_string_to_box(string_buff, tot_box);
+
+ g_snprintf(string_buff, SUM_STR_MAX, "Total number of bytes for GSM MAP messages: %.0f", tot_invokes_size + tot_rr_size);
+ add_string_to_box(string_buff, tot_box);
+
+ if (tot_invokes + tot_rr)
+ g_snprintf(string_buff, SUM_STR_MAX, "Average number of bytes per GSM MAP messages: %.2f",
+ (tot_invokes_size + tot_rr_size)/(tot_invokes + tot_rr));
+ else
+ g_snprintf(string_buff, SUM_STR_MAX, "Average number of bytes per GSM MAP messages: N/A");
+ add_string_to_box(string_buff, tot_box);
+
+ if (seconds)
+ g_snprintf(string_buff, SUM_STR_MAX, "Average number of bytes second: %.2f",
+ (tot_invokes_size + tot_rr_size)/seconds);
+ else
+ g_snprintf(string_buff, SUM_STR_MAX, "Average number of bytes second: N/A");
+ add_string_to_box(string_buff, tot_box);
+
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_container_add(GTK_CONTAINER(main_vb), bbox);
+ gtk_widget_show(bbox);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(sum_open_w, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(sum_open_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+
+ gtk_widget_show(sum_open_w);
+ window_present(sum_open_w);
+}
+
+
+void
+register_tap_listener_gtkgsm_map_summary(void)
+{
+}
diff --git a/ui/gtk/gtkglobals.h b/ui/gtk/gtkglobals.h
new file mode 100644
index 0000000000..f4d4ab7ac4
--- /dev/null
+++ b/ui/gtk/gtkglobals.h
@@ -0,0 +1,57 @@
+/* gtkglobals.h
+ * GTK-related Global defines, etc.
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTKGLOBALS_H__
+#define __GTKGLOBALS_H__
+
+/** @mainpage GTK subsystem
+ *
+ * @section intro Introduction
+ *
+ * Wireshark uses GTK (the Gimp ToolKit) as its user interface toolkit.
+ *
+ * See Modules for a list of submodules.
+ *
+ */
+
+/** @file
+ * GTK global definitions. For example a pointer to the main application window.
+ */
+
+/** Application window. */
+extern GtkWidget *top_level;
+
+/** Tree view (packet details) pane. */
+extern GtkWidget *tree_view_gbl;
+
+/** Byte notebook (packet bytes) pane. */
+extern GtkWidget *byte_nb_ptr_gbl;
+
+/** The filter text entry in the filter toolbar. */
+extern GtkWidget *main_display_filter_widget;
+
+/** If autoscroll in live captures is active or not */
+extern gboolean auto_scroll_live;
+
+#endif
diff --git a/ui/gtk/gtp_stat.c b/ui/gtk/gtp_stat.c
new file mode 100644
index 0000000000..ba095c760d
--- /dev/null
+++ b/ui/gtk/gtp_stat.c
@@ -0,0 +1,249 @@
+/* gtp_stat.c
+ * gtp_stat 2008 Kari Tiirikainen
+ * Largely based on ldap_stat by Ronnie Sahlberg, all mistakes added by KTi
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/value_string.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-gtp.h>
+
+#include "../timestats.h"
+#include "../simple_dialog.h"
+#include "../file.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/service_response_time_table.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/main.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _gtpstat_t {
+ GtkWidget *win;
+ srt_stat_table gtp_srt_table;
+} gtpstat_t;
+
+static void
+gtpstat_set_title(gtpstat_t *gtp)
+{
+ char *title;
+
+ title = g_strdup_printf("GTP Control Plane Response Time statistics: %s",
+ cf_get_display_name(&cfile));
+ gtk_window_set_title(GTK_WINDOW(gtp->win), title);
+ g_free(title);
+}
+
+static void
+gtpstat_reset(void *pgtp)
+{
+ gtpstat_t *gtp=(gtpstat_t *)pgtp;
+
+ reset_srt_table_data(&gtp->gtp_srt_table);
+ gtpstat_set_title(gtp);
+}
+
+static int
+gtpstat_packet(void *pgtp, packet_info *pinfo, epan_dissect_t *edt _U_, const void *psi)
+{
+ const gtp_msg_hash_t *gtp=psi;
+ gtpstat_t *fs=(gtpstat_t *)pgtp;
+ int idx=0;
+
+ /* we are only interested in reply packets */
+ if(gtp->is_request){
+ return 0;
+ }
+ /* if we have not seen the request, just ignore it */
+ if(!gtp->req_frame){
+ return 0;
+ }
+
+ /* Only use the commands we know how to handle, this is not a comprehensive list */
+ /* Redoing the message indexing is bit reduntant, */
+ /* but using message type as such would yield a long gtp_srt_table. */
+ /* Only a fraction of the messages are matchable req/resp pairs, */
+ /* it just doesn't feel feasible. */
+
+ switch(gtp->msgtype){
+ case GTP_MSG_ECHO_REQ: idx=0;
+ break;
+ case GTP_MSG_CREATE_PDP_REQ: idx=1;
+ break;
+ case GTP_MSG_UPDATE_PDP_REQ: idx=2;
+ break;
+ case GTP_MSG_DELETE_PDP_REQ: idx=3;
+ break;
+ default:
+ return 0;
+ }
+
+ add_srt_table_data(&fs->gtp_srt_table, idx, &gtp->req_time, pinfo);
+
+ return 1;
+}
+
+
+
+static void
+gtpstat_draw(void *pgtp)
+{
+ gtpstat_t *gtp=(gtpstat_t *)pgtp;
+
+ draw_srt_table_data(&gtp->gtp_srt_table);
+}
+
+
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ gtpstat_t *gtp=(gtpstat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(gtp);
+ unprotect_thread_critical_region();
+
+ free_srt_table_data(&gtp->gtp_srt_table);
+ g_free(gtp);
+}
+
+
+static void
+gtk_gtpstat_init(const char *optarg, void *userdata _U_)
+{
+ gtpstat_t *gtp;
+ const char *filter=NULL;
+ GtkWidget *label;
+ char *filter_string;
+ GString *error_string;
+ GtkWidget *vbox;
+ GtkWidget *bbox;
+ GtkWidget *close_bt;
+
+ if(!strncmp(optarg,"gtp,",4)){
+ filter=optarg+4;
+ } else {
+ filter="gtp"; /*NULL doesn't work here like in LDAP. Too little time/lazy to find out why ?*/
+ }
+
+ gtp=g_malloc(sizeof(gtpstat_t));
+
+ gtp->win = dlg_window_new("gtp-stat"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(gtp->win), TRUE);
+
+ gtk_window_set_default_size(GTK_WINDOW(gtp->win), 550, 400);
+ gtpstat_set_title(gtp);
+
+ vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(gtp->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ label=gtk_label_new("GTP Service Response Time statistics");
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ filter_string = g_strdup_printf("Filter: %s", filter ? filter : "");
+ label=gtk_label_new(filter_string);
+ g_free(filter_string);
+ gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ label=gtk_label_new("GTP Requests");
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ /* We must display TOP LEVEL Widget before calling init_srt_table() */
+ gtk_widget_show_all(gtp->win);
+
+ init_srt_table(&gtp->gtp_srt_table, 4, vbox, NULL);
+ init_srt_table_row(&gtp->gtp_srt_table, 0, "Echo");
+ init_srt_table_row(&gtp->gtp_srt_table, 1, "Create PDP context");
+ init_srt_table_row(&gtp->gtp_srt_table, 2, "Update PDP context");
+ init_srt_table_row(&gtp->gtp_srt_table, 3, "Delete PDP context");
+
+ error_string=register_tap_listener("gtp", gtp, filter, 0, gtpstat_reset, gtpstat_packet, gtpstat_draw);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(gtp);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(gtp->win, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(gtp->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(gtp->win, "destroy", G_CALLBACK(win_destroy_cb), gtp);
+
+ gtk_widget_show_all(gtp->win);
+ window_present(gtp->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(gtp->win));
+}
+
+static tap_param gtp_stat_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg gtp_stat_dlg = {
+ "GTP Control Plane Response Time Statistics",
+ "gtp",
+ gtk_gtpstat_init,
+ -1,
+ G_N_ELEMENTS(gtp_stat_params),
+ gtp_stat_params
+};
+
+void
+register_tap_listener_gtkgtpstat(void)
+{
+ register_dfilter_stat(&gtp_stat_dlg, "GTP",
+ REGISTER_STAT_GROUP_RESPONSE_TIME);
+}
+
+void gtp_srt_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &gtp_stat_dlg);
+}
+
diff --git a/ui/gtk/gui_stat_menu.h b/ui/gtk/gui_stat_menu.h
new file mode 100644
index 0000000000..d36580a3af
--- /dev/null
+++ b/ui/gtk/gui_stat_menu.h
@@ -0,0 +1,186 @@
+/* gui_stat_menu.h
+ * GTK+-specific menu definitions for use by stats
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_STAT_MENU_H__
+#define __GTK_STAT_MENU_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/** @file
+ * Add a new menu item for a stat.
+ */
+
+/**
+ * XXX TODO: Rewrite me
+ * NOTE comments refere to old menus.c implementation.
+ *
+ * Same as register_stat_menu_item() but with optional stock item.
+ *
+ * @param name the menu label
+ *
+ * @param group the menu group this stat should be registered to
+ *
+ * @param stock_id the stock_id (icon) to show, or NULL
+ *
+ * @param callback gets called when the menu item is selected; it should do
+ * the work of creating the stat window.
+ *
+ * @param selected_packet_enabled gets called by set_menus_for_selected_packet();
+ * it's passed a pointer to the "frame_data" structure for the current frame,
+ * if any, and to the "epan_dissect_t" structure for that frame, if any, and
+ * should return TRUE if the stat will work now (which might depend on whether
+ * a frame is selected and, if one is, on the frame) and FALSE if not.
+ *
+ * @param selected_tree_row_enabled gets called by
+ * set_menus_for_selected_tree_row(); it's passed a pointer to the
+ * "field_info" structure for the currently selected field, if any,
+ * and should return TRUE if the stat will work now (which might depend on
+ * whether a tree row is selected and, if one is, on the tree row) and
+ * FALSE if not.
+ *
+ * @param callback_data data for callback function
+ */
+void register_lua_menu_bar_menu_items(
+ const char *gui_path,
+ const char *name,
+ const gchar *stock_id,
+ const char *label,
+ const char *accelerator,
+ const gchar *tooltip,
+ gpointer callback,
+ gpointer callback_data,
+ gboolean enabled,
+ gboolean (*selected_packet_enabled)(frame_data *, epan_dissect_t *, gpointer callback_data),
+ gboolean (*selected_tree_row_enabled)(field_info *, gpointer callback_data));
+
+void eth_endpoints_cb(GtkAction *action, gpointer user_data);
+void fc_endpoints_cb(GtkAction *action, gpointer user_data);
+void fddi_endpoints_cb(GtkAction *action, gpointer user_data);
+void ip_endpoints_cb(GtkAction *action, gpointer user_data);
+void ipv6_endpoints_cb(GtkAction *action, gpointer user_data);
+void ipx_endpoints_cb(GtkAction *action, gpointer user_data);
+void jxta_conversation_cb(GtkAction *action, gpointer user_data);
+void ncp_endpoints_cb(GtkAction *action, gpointer user_data);
+void rsvp_endpoints_cb(GtkAction *action, gpointer user_data);
+void sctp_conversation_cb(GtkAction *action, gpointer user_data);
+void tcpip_conversation_cb(GtkAction *action, gpointer user_data);
+void tr_conversation_cb(GtkAction *action, gpointer user_data);
+void udpip_conversation_cb(GtkAction *action, gpointer user_data);
+void usb_endpoints_cb(GtkAction *action, gpointer user_data);
+void wlan_endpoints_cb(GtkAction *action, gpointer user_data);
+
+void gtk_eth_hostlist_cb(GtkAction *action, gpointer user_data);
+void gtk_fc_hostlist_cb(GtkAction *action, gpointer user_data);
+void gtk_fddi_hostlist_cb(GtkAction *action, gpointer user_data);
+void gtk_ip_hostlist_cb(GtkAction *action, gpointer user_data);
+void gtk_ipv6_hostlist_cb(GtkAction *action, gpointer user_data);
+void gtk_ipx_hostlist_cb(GtkAction *action, gpointer user_data);
+void gtk_jxta_hostlist_cb(GtkAction *action, gpointer user_data);
+void gtk_ncp_hostlist_cb(GtkAction *action, gpointer user_data);
+void gtk_rsvp_hostlist_cb(GtkAction *action, gpointer user_data);
+void gtk_sctp_hostlist_cb(GtkAction *action, gpointer user_data);
+void gtk_tcpip_hostlist_cb(GtkAction *action, gpointer user_data);
+void gtk_tr_hostlist_cb(GtkAction *action, gpointer user_data);
+void gtk_udpip_hostlist_cb(GtkAction *action, gpointer user_data);
+void gtk_usb_hostlist_cb(GtkAction *action, gpointer user_data);
+void gtk_wlan_hostlist_cb(GtkAction *action, gpointer user_data);
+
+void gtk_rpcstat_cb(GtkAction *action, gpointer user_data);
+void bootp_dhcp_stat_cb(GtkAction *action, gpointer user_data);
+void gtk_comparestat_cb(GtkAction *action, gpointer user_data);
+
+void flow_graph_launch(GtkAction *action, gpointer user_data);
+
+void iax2_analysis_cb(GtkAction *action, gpointer user_data);
+void mtp3_stat_gtk_cb(GtkAction *action, gpointer user_data);
+void mtp3_sum_gtk_sum_cb(GtkAction *action, gpointer user_data);
+void rtp_analysis_cb(GtkAction *action, gpointer user_data);
+void rtpstream_launch(GtkAction *action, gpointer user_data);
+void sctp_analyse_start(GtkAction *action, gpointer user_data);
+void sctp_chunk_counter_cb(GtkAction *action, gpointer user_data);
+void sctp_stat_start(GtkAction *action, gpointer user_data);
+
+void gui_iostat_cb(GtkAction *action, gpointer user_data);
+
+void voip_calls_launch(GtkAction *action, gpointer user_data);
+
+void ansi_a_stat_gtk_bsmap_cb(GtkAction *action, gpointer user_data);
+void ansi_a_stat_gtk_dtap_cb(GtkAction *action, gpointer user_data);
+void ansi_map_stat_gtk_cb(GtkAction *action, gpointer user_data);
+
+void camel_counter_cb(GtkAction *action, gpointer user_data);
+void h225_counter_cb(GtkAction *action, gpointer user_data);
+void gsm_a_stat_gtk_bssmap_cb(GtkAction *action, gpointer user_data);
+void gsm_a_stat_gtk_dtap_mm_cb(GtkAction *action, gpointer user_data);
+void gsm_a_stat_gtk_dtap_rr_cb(GtkAction *action, gpointer user_data);
+void gsm_a_stat_gtk_dtap_cc_cb(GtkAction *action, gpointer user_data);
+void gsm_a_stat_gtk_dtap_gmm_cb(GtkAction *action, gpointer user_data);
+void gsm_a_stat_gtk_dtap_sms_cb(GtkAction *action, gpointer user_data);
+void gsm_a_stat_gtk_dtap_sm_cb(GtkAction *action, gpointer user_data);
+void gsm_a_stat_gtk_dtap_ss_cb(GtkAction *action, gpointer user_data);
+void gsm_a_stat_gtk_dtap_tp_cb(GtkAction *action, gpointer user_data);
+void gsm_a_stat_gtk_sacch_rr_cb(GtkAction *action, gpointer user_data);
+void mac_lte_stat_cb(GtkAction *action, gpointer user_data);
+void rlc_lte_stat_cb(GtkAction *action, gpointer user_data);
+void sipstat_cb(GtkAction *action, gpointer user_data);
+void wsp_stat_cb(GtkAction *action, gpointer user_data);
+
+void gsm_map_stat_gtk_cb(GtkAction *action, gpointer user_data);
+void gsm_map_stat_gtk_sum_cb(GtkAction *action, gpointer user_data);
+
+void afp_srt_stat_cb(GtkAction *action, gpointer user_data);
+void camel_srt_cb(GtkAction *action, gpointer user_data);
+void gtk_dcerpcstat_cb(GtkAction *action, gpointer user_data);
+void diameter_srt_cb(GtkAction *action, gpointer user_data);
+void fc_srt_cb(GtkAction *action, gpointer user_data);
+void gtp_srt_cb(GtkAction *action, gpointer user_data);
+void h225_srt_cb(GtkAction *action, gpointer user_data);
+void ldap_srt_cb(GtkAction *action, gpointer user_data);
+void megaco_srt_cb(GtkAction *action, gpointer user_data);
+void mgcp_srt_cb(GtkAction *action, gpointer user_data);
+void ncp_srt_cb(GtkAction *action, gpointer user_data);
+void radius_srt_cb(GtkAction *action, gpointer user_data);
+void scsi_srt_cb(GtkAction *action, gpointer user_data);
+void smb2_srt_cb(GtkAction *action, gpointer user_data);
+void smb_srt_cb(GtkAction *action, gpointer user_data);
+
+void gtk_stats_tree_cb(GtkAction *action, gpointer user_data);
+
+void tcp_graph_cb (GtkAction *action, gpointer user_data);
+gboolean tcp_graph_selected_packet_enabled(frame_data *current_frame, epan_dissect_t *edt, gpointer callback_data _U_);
+
+void gtk_rpcprogs_cb(GtkWidget *w, gpointer data);
+void mcaststream_launch(GtkAction *action, gpointer user_data);
+void wlanstat_launch(GtkAction *action, gpointer user_data);
+
+/** Adds a callback to be executed when the menubar is ready to have menus and items added to it */
+void ws_add_build_menubar_items_callback(gpointer callback);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __GTK_STAT_MENU_H__ */
diff --git a/ui/gtk/gui_stat_util.c b/ui/gtk/gui_stat_util.c
new file mode 100644
index 0000000000..8ae18b69eb
--- /dev/null
+++ b/ui/gtk/gui_stat_util.c
@@ -0,0 +1,159 @@
+/* gui_stat_util.c
+ * gui functions used by stats
+ * Copyright 2003 Lars Roland
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "../simple_dialog.h"
+#include "../file.h"
+
+#include "ui/gtk/gui_stat_util.h"
+
+
+/* insert a string into a GTK_TABLE at column x and row y*/
+#if 0
+/* Statistic table */
+typedef struct _gtk_table {
+ GtkWidget *widget; /**< the table widget */
+ int height; /**< the height */
+ int width; /**< the width */
+}gtk_table;
+
+void
+add_table_entry(gtk_table *tab, const char *str, int x, int y)
+{
+ GtkWidget *tmp;
+
+ if(y>=tab->height){
+ tab->height=y+1;
+ gtk_table_resize(GTK_TABLE(tab->widget), tab->height, tab->width);
+ }
+ if(x>=tab->width){
+ tab->width=x+1;
+ gtk_table_resize(GTK_TABLE(tab->widget), tab->height, tab->width);
+ }
+
+ tmp=gtk_label_new(str);
+ gtk_table_attach_defaults(GTK_TABLE(tab->widget), tmp, x, x+1, y, y+1);
+ gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
+ gtk_widget_show(tmp);
+}
+#endif
+
+/* init a main window for stats, set title and display used filter in window */
+
+void
+init_main_stat_window(GtkWidget *window, GtkWidget *mainbox, const char *title, const char *filter)
+{
+ GtkWidget *main_label;
+ GtkWidget *filter_label;
+ char *filter_string;
+
+
+ gtk_window_set_title(GTK_WINDOW(window), title);
+
+ gtk_container_add(GTK_CONTAINER(window), mainbox);
+ gtk_container_set_border_width(GTK_CONTAINER(mainbox), 10);
+ gtk_widget_show(mainbox);
+
+ main_label=gtk_label_new(title);
+ gtk_box_pack_start(GTK_BOX(mainbox), main_label, FALSE, FALSE, 0);
+ gtk_widget_show(main_label);
+
+ filter_string = g_strdup_printf("Filter: %s", filter ? filter : "");
+ filter_label=gtk_label_new(filter_string);
+ g_free(filter_string);
+ gtk_label_set_line_wrap(GTK_LABEL(filter_label), TRUE);
+ gtk_box_pack_start(GTK_BOX(mainbox), filter_label, FALSE, FALSE, 0);
+ gtk_widget_show(filter_label);
+
+}
+
+/* create a table, using a scrollable GtkTreeView */
+
+GtkTreeView *
+create_stat_table(GtkWidget *scrolled_window, GtkWidget *vbox, int columns, const stat_column *headers)
+{
+ GtkTreeView *table;
+ GtkListStore *store;
+ GtkWidget *tree;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *sel;
+ GtkCellRenderer *renderer;
+ GType *types;
+ int i;
+
+ if (columns <= 0)
+ return NULL;
+
+ types = g_malloc(columns *sizeof(GType));
+ for (i = 0; i < columns; i++)
+ types[i] = headers[i].type;
+
+ store = gtk_list_store_newv (columns, types);
+ g_free(types);
+
+ /* create table */
+ tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
+ table = GTK_TREE_VIEW(tree);
+ g_object_unref (G_OBJECT (store));
+
+ for (i = 0; i < columns; i++) {
+ renderer = gtk_cell_renderer_text_new ();
+ if (headers[i].align == RIGHT) {
+ /* right align */
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+ }
+ g_object_set(renderer, "ypad", 0, NULL);
+ column = gtk_tree_view_column_new_with_attributes (headers[i].title, renderer, "text",
+ i, NULL);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_append_column (table, column);
+ }
+ gtk_container_add(GTK_CONTAINER(scrolled_window), GTK_WIDGET (table));
+ gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0);
+
+ /* configure TreeView */
+ gtk_tree_view_set_rules_hint(table, FALSE);
+ gtk_tree_view_set_headers_clickable(table, FALSE);
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(table));
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+
+ gtk_widget_show(scrolled_window);
+
+ return table;
+}
+
diff --git a/ui/gtk/gui_stat_util.h b/ui/gtk/gui_stat_util.h
new file mode 100644
index 0000000000..64f5420aae
--- /dev/null
+++ b/ui/gtk/gui_stat_util.h
@@ -0,0 +1,65 @@
+/* gui_stat_util.h
+ * gui functions used by stats
+ * Copyright 2003 Lars Roland
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_STAT_UTIL_H__
+#define __GTK_STAT_UTIL_H__
+
+#include <gtk/gtk.h>
+
+
+/** @file
+ * Utilities for statistics.
+ */
+
+#define LEFT 0
+#define RIGHT 1
+
+/** Columns definition
+ */
+typedef struct {
+ GType type; /* column type */
+ gint align; /* alignement */
+ const char *title; /* column title */
+} stat_column;
+
+/** Init a window for stats, set title and display used filter in window.
+ *
+ * @param window the window
+ * @param mainbox the vbox for the window
+ * @param title the title for the window
+ * @param filter the filter string
+ */
+extern void init_main_stat_window(GtkWidget *window, GtkWidget *mainbox, const char *title, const char *filter);
+
+/** Create a stats table, using a scrollable gtkclist.
+ *
+ * @param scrolled_window the scrolled window
+ * @param vbox the vbox for the window
+ * @param columns number of columns
+ * @param headers columns title and type, G_TYPE_POINTER is illegal, there's no default cell renderer for it
+ */
+extern GtkTreeView *create_stat_table(GtkWidget *scrolled_window, GtkWidget *vbox, int columns, const stat_column *headers);
+
+#endif /* __GUI_STAT_UTIL_H__ */
diff --git a/ui/gtk/gui_utils.c b/ui/gtk/gui_utils.c
new file mode 100644
index 0000000000..f21c95b42b
--- /dev/null
+++ b/ui/gtk/gui_utils.c
@@ -0,0 +1,1881 @@
+/* gui_utils.c
+ * UI utility routines, some GTK+-specific (declared in gtk/gui_utils.h)
+ * and some with GUI-independent APIs, with this file containing the GTK+
+ * implementations of them (declared in ui_util.h)
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <locale.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#if GTK_CHECK_VERSION(3,0,0)
+# include <gdk/gdkkeysyms-compat.h>
+#endif
+
+#include <epan/prefs.h>
+#include "epan/epan.h"
+
+#include <epan/packet_info.h>
+#include "../ui_util.h"
+#include <wsutil/file_util.h>
+
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/font_utils.h"
+#include "ui/gtk/recent.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+#include "image/wsicon16.xpm"
+#include "image/wsicon32.xpm"
+#include "image/wsicon48.xpm"
+#include "image/wsicon64.xpm"
+
+#include "../version_info.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#define WINDOW_GEOM_KEY "window_geom"
+
+
+/* load the geometry values for a window from previously saved values */
+static gboolean window_geom_load(const gchar *name, window_geometry_t *geom);
+
+
+
+/* Set our window icon. The GDK documentation doesn't provide any
+ actual documentation for gdk_window_set_icon(), so we'll steal
+ libgimp/gimpdialog.c:gimp_dialog_realize_callback() from the Gimp
+ sources and assume it's safe.
+
+ XXX - The current icon size is fixed at 16x16 pixels, which looks fine
+ with kwm (KDE 1.x's window manager), Sawfish (the "default" window
+ manager for GNOME?), and under Windows with Exceed putting X windows
+ on the Windows desktop, using Exceed as the window manager, as those
+ window managers put a 16x16 icon on the title bar.
+
+ The window managers in some windowing environments (e.g. dtwm in CDE)
+ and some stand-alone window managers have larger icon sizes (many window
+ managers put the window icon on the desktop, in the Windows 3.x style,
+ rather than in the titlebar, in the Windows 4.x style), so we need to
+ find a way to size our icon appropriately.
+
+ The X11 Inter-Client Communications Conventions Manual, Version 1.1,
+ in X11R5, specifies that "a window manager that wishes to place
+ constraints on the sizes of icon pixmaps and/or windows should
+ place a property called WM_ICON_SIZE on the root"; that property
+ contains minimum width and height, maximum width and height, and
+ width and height increment values. "XGetIconSizes()" retrieves
+ that property; unfortunately, I've yet to find a window manager
+ that sets it on the root window (kwm, AfterStep, and Exceed don't
+ appear to set it).
+
+ The X Desktop Group's Window Manager Standard specifies, in the section
+ on Application Window Properties, an _NET_WM_ICON property, presumably
+ set by the window manager, which is an array of possible icon sizes
+ for the client. There's no API in GTK+ 1.2[.x] for this; there may
+ eventually be one either in GTK+ 2.0 or GNOME 2.0.
+
+ Some window managers can be configured to take the window name
+ specified by the WM_NAME property of a window or the resource
+ or class name specified by the WM_CLASS property and base the
+ choice of icon for the window on one of those; WM_CLASS for
+ Wireshark's windows has a resource name of "wireshark" and a class
+ name of "Wireshark". However, the way that's done is window-manager-
+ specific, and there's no way to determine what size a particular
+ window manager would want, so there's no way to automate this as
+ part of the installation of Wireshark.
+ */
+static void
+window_icon_realize_cb (GtkWidget *win, gpointer data _U_)
+{
+#ifndef _WIN32
+ GList *ws_icon_list=NULL;
+ GdkPixbuf *icon;
+
+
+ icon = gdk_pixbuf_new_from_xpm_data ((const char **) wsicon16_xpm);
+ ws_icon_list = g_list_append (ws_icon_list, icon);
+ icon = gdk_pixbuf_new_from_xpm_data ((const char **) wsicon32_xpm);
+ ws_icon_list = g_list_append (ws_icon_list, icon);
+ icon = gdk_pixbuf_new_from_xpm_data ((const char **) wsicon48_xpm);
+ ws_icon_list = g_list_append (ws_icon_list, icon);
+ icon = gdk_pixbuf_new_from_xpm_data ((const char **) wsicon64_xpm);
+ ws_icon_list = g_list_append (ws_icon_list, icon);
+ gtk_window_set_icon_list(GTK_WINDOW(win), ws_icon_list);
+
+#endif
+}
+
+
+/* Create a new window, of the specified type, with the specified title
+ (if any) and the Wireshark icon. */
+GtkWidget *
+window_new(GtkWindowType type, const gchar *title)
+{
+ GtkWidget *win;
+
+ win = gtk_window_new(type);
+ if (title != NULL)
+ gtk_window_set_title(GTK_WINDOW(win), title);
+ g_signal_connect(win, "realize", G_CALLBACK(window_icon_realize_cb), NULL);
+
+ /* XXX - which one is the correct default policy? or use a preference for this? */
+ /* GTK_WIN_POS_NONE, GTK_WIN_POS_CENTER or GTK_WIN_POS_MOUSE */
+ /* a lot of people dislike GTK_WIN_POS_MOUSE */
+
+ /* set the initial position (must be done, before show is called!) */
+/* gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER_ON_PARENT);*/
+ gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_NONE);
+
+ return win;
+}
+
+
+/* Same as window_new(), but will keep its geometry values (size, position, ...).
+ * Be sure to use window_present() and window_destroy() appropriately! */
+GtkWidget *
+window_new_with_geom(GtkWindowType type, const gchar *title, const gchar *geom_name)
+{
+ window_geometry_t geom;
+ GtkWidget *win = window_new(type, title);
+
+ g_object_set_data(G_OBJECT(win), WINDOW_GEOM_KEY, (gpointer)g_strdup(geom_name));
+
+ /* do we have a previously saved size and position of this window? */
+ if(geom_name) {
+ /* It's a good idea to set the position and size of the window already here,
+ * as it's still invisible and won't "flicker the screen" while initially resizing. */
+ if(window_geom_load(geom_name, &geom)) {
+ /* XXX - use prefs to select which values to set? */
+ geom.set_pos = TRUE;
+ geom.set_size = TRUE;
+ geom.set_maximized = FALSE; /* don't maximize until window is shown */
+ window_set_geometry(win, &geom);
+ }
+ }
+
+ return win;
+}
+
+
+/* Create a new window for a splash screen; it's a main window, without decoration,
+ positioned in the center of the screen. */
+GtkWidget *
+splash_window_new(void)
+{
+ GtkWidget *win;
+
+ win = window_new(GTK_WINDOW_TOPLEVEL, "Wireshark");
+ gtk_window_set_decorated(GTK_WINDOW(win), FALSE);
+
+ /* set the initial position (must be done, before show is called!) */
+ gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER);
+
+ return win;
+}
+
+
+/* Present the created window on the screen. */
+void
+window_present(GtkWidget *win)
+{
+ window_geometry_t geom;
+ const gchar *name;
+
+ /* present this window */
+ gtk_window_present(GTK_WINDOW(win));
+
+ /* do we have a previously saved size and position of this window? */
+ name = g_object_get_data(G_OBJECT(win), WINDOW_GEOM_KEY);
+ if(name) {
+ if(window_geom_load(name, &geom)) {
+ /* XXX - use prefs to select which values to set? */
+ geom.set_pos = TRUE;
+ geom.set_size = TRUE;
+ geom.set_maximized = TRUE;
+ window_set_geometry(win, &geom);
+ }
+ }
+}
+
+
+static gboolean
+window_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer cancel_button)
+{
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ if (event->keyval == GDK_Escape) {
+ gtk_widget_activate(GTK_WIDGET(cancel_button));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/* Set the "key_press_event" signal for a top-level dialog window to
+ call a routine to activate the "Cancel" button for a dialog box if
+ the key being pressed is the <Esc> key.
+
+ XXX - there should be a GTK+ widget that'll do that for you, and
+ let you specify a "Cancel" button. It should also not impose
+ a requirement that there be a separator in the dialog box, as
+ the GtkDialog widget does; the visual convention that there's
+ such a separator between the rest of the dialog boxes and buttons
+ such as "OK" and "Cancel" is, for better or worse, not universal
+ (not even in GTK+ - look at the GtkFileSelection dialog!). */
+static void
+window_set_cancel(GtkWidget *widget, GtkWidget *cancel_button)
+{
+ g_signal_connect(widget, "key_press_event", G_CALLBACK(window_key_press_cb), cancel_button);
+}
+
+
+/* set the actions needed for the cancel "Close"/"Ok"/"Cancel" button that closes the window */
+void window_set_cancel_button(GtkWidget *win, GtkWidget *bt, window_cancel_button_fct cb)
+{
+ if(cb)
+ g_signal_connect(bt, "clicked", G_CALLBACK(cb), win);
+
+ gtk_widget_grab_default(bt);
+
+ window_set_cancel(win, bt);
+}
+
+
+/* default callback handler for cancel button "clicked" signal */
+void window_cancel_button_cb(GtkWidget *w _U_, gpointer data)
+{
+ window_destroy(GTK_WIDGET(data));
+}
+
+
+/* default callback handler: the window managers X of the window was clicked (delete_event) */
+gboolean
+window_delete_event_cb(GtkWidget *win, GdkEvent *event _U_, gpointer user_data _U_)
+{
+ window_destroy(win);
+
+ /* event handled, don't do anything else */
+ return TRUE;
+}
+
+
+/* get the geometry of a window from window_new() */
+void
+window_get_geometry(GtkWidget *widget, window_geometry_t *geom)
+{
+ GdkWindowState state;
+ GdkWindow *widget_window;
+
+ /* Try to grab our geometry.
+
+ GTK+ provides two routines to get a window's position relative
+ to the X root window. If I understand the documentation correctly,
+ gdk_window_get_deskrelative_origin applies mainly to Enlightenment
+ and gdk_window_get_root_origin applies for all other WMs.
+
+ The code below tries both routines, and picks the one that returns
+ the upper-left-most coordinates.
+
+ More info at:
+
+ http://mail.gnome.org/archives/gtk-devel-list/2001-March/msg00289.html
+ http://www.gtk.org/faq/#AEN606
+
+ As gdk_window_get_deskrelative_origin() is deprecated it has been removed 2011-07-24.
+ */
+
+ memset (geom, 0, sizeof (window_geometry_t));
+
+ widget_window = gtk_widget_get_window(widget);
+
+ gdk_window_get_root_origin(widget_window,
+ &geom->x,
+ &geom->y);
+
+ /* XXX - Is this the "approved" method? */
+#if GTK_CHECK_VERSION(2,24,0)
+ geom->width = gdk_window_get_width(widget_window);
+ geom->height = gdk_window_get_height (widget_window);
+#else
+ gdk_drawable_get_size(widget_window,
+ &geom->width,
+ &geom->height);
+#endif
+ state = gdk_window_get_state(widget_window);
+ geom->maximized = (state == GDK_WINDOW_STATE_MAXIMIZED);
+}
+
+
+/* set the geometry of a window from window_new() */
+void
+window_set_geometry(GtkWidget *widget, window_geometry_t *geom)
+{
+ GdkScreen *default_screen;
+ GdkRectangle viewable_area;
+ gint monitor_num;
+
+ /* as we now have the geometry from the recent file, set it */
+ /* if the window was minimized, x and y are -32000 (at least on Win32) */
+ if (geom->set_pos && geom->x != -32000 && geom->y != -32000) {
+ /* Per Wireshark bug #553, GTK has a problem on MS Windows
+ * where the upper-left corner of the window may appear off
+ * screen when when a single desktop spans multiple monitors
+ * of different resolutions and positions relative to each
+ * other.
+ *
+ * If the requested (x,y) position isn't within the monitor's
+ * viewable area, change it to the viewable area's (0,0). */
+
+ default_screen = gdk_screen_get_default();
+ monitor_num = gdk_screen_get_monitor_at_point(default_screen,
+ geom->x, geom->y);
+ gdk_screen_get_monitor_geometry(default_screen, monitor_num,
+ &viewable_area);
+
+ if(geom->x < viewable_area.x || geom->x > viewable_area.width)
+ geom->x = viewable_area.x;
+
+ if(geom->y < viewable_area.y || geom->y > viewable_area.height)
+ geom->y = viewable_area.y;
+
+ gtk_window_move(GTK_WINDOW(widget),
+ geom->x,
+ geom->y);
+ }
+
+ if (geom->set_size) {
+ gtk_window_resize(GTK_WINDOW(widget),
+ /*gtk_widget_set_size_request(widget,*/
+ geom->width,
+ geom->height);
+ }
+
+ if(geom->set_maximized) {
+ if (geom->maximized) {
+ gdk_window_maximize(gtk_widget_get_window(widget));
+ } else {
+ gdk_window_unmaximize(gtk_widget_get_window(widget));
+ }
+ }
+}
+
+
+/* the geometry hashtable for all known window classes,
+ * the window name is the key, and the geometry struct is the value */
+static GHashTable *window_geom_hash = NULL;
+
+
+/* save the window and it's current geometry into the geometry hashtable */
+static void
+window_geom_save(const gchar *name, window_geometry_t *geom)
+{
+ gchar *key;
+ window_geometry_t *work;
+
+ /* init hashtable, if not already done */
+ if(!window_geom_hash) {
+ window_geom_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ }
+ /* if we have an old one, remove and free it first */
+ work = g_hash_table_lookup(window_geom_hash, name);
+ if(work) {
+ g_hash_table_remove(window_geom_hash, name);
+ g_free(work->key);
+ g_free(work);
+ }
+
+ /* g_malloc and insert the new one */
+ work = g_malloc(sizeof(*geom));
+ *work = *geom;
+ key = g_strdup(name);
+ work->key = key;
+ g_hash_table_insert(window_geom_hash, key, work);
+}
+
+
+/* load the desired geometry for this window from the geometry hashtable */
+static gboolean
+window_geom_load(const gchar *name, window_geometry_t *geom)
+{
+ window_geometry_t *p;
+
+ /* init hashtable, if not already done */
+ if(!window_geom_hash) {
+ window_geom_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ }
+
+ p = g_hash_table_lookup(window_geom_hash, name);
+ if(p) {
+ *geom = *p;
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+
+/* read in a single key value pair from the recent file into the geometry hashtable */
+void
+window_geom_recent_read_pair(const char *name, const char *key, const char *value)
+{
+ window_geometry_t geom;
+
+
+ /* find window geometry maybe already in hashtable */
+ if(!window_geom_load(name, &geom)) {
+ /* not in table, init geom with "basic" values */
+ geom.key = NULL; /* Will be set in window_geom_save () */
+ geom.set_pos = FALSE;
+ geom.x = -1;
+ geom.y = -1;
+ geom.set_size = FALSE;
+ geom.width = -1;
+ geom.height = -1;
+
+ geom.set_maximized = FALSE;/* this is valid in GTK2 only */
+ geom.maximized = FALSE; /* this is valid in GTK2 only */
+ }
+
+ if (strcmp(key, "x") == 0) {
+ geom.x = strtol(value, NULL, 10);
+ geom.set_pos = TRUE;
+ } else if (strcmp(key, "y") == 0) {
+ geom.y = strtol(value, NULL, 10);
+ geom.set_pos = TRUE;
+ } else if (strcmp(key, "width") == 0) {
+ geom.width = strtol(value, NULL, 10);
+ geom.set_size = TRUE;
+ } else if (strcmp(key, "height") == 0) {
+ geom.height = strtol(value, NULL, 10);
+ geom.set_size = TRUE;
+ } else if (strcmp(key, "maximized") == 0) {
+ if (g_ascii_strcasecmp(value, "true") == 0) {
+ geom.maximized = TRUE;
+ }
+ else {
+ geom.maximized = FALSE;
+ }
+ geom.set_maximized = TRUE;
+ } else {
+ /*
+ * Silently ignore the bogus key. We shouldn't abort here,
+ * as this could be due to a corrupt recent file.
+ *
+ * XXX - should we print a message about this?
+ */
+ return;
+ }
+
+ /* save / replace geometry in hashtable */
+ window_geom_save(name, &geom);
+}
+
+
+/* write all geometry values of all windows from the hashtable to the recent file */
+void
+window_geom_recent_write_all(gpointer rf)
+{
+ /* init hashtable, if not already done */
+ if(!window_geom_hash) {
+ window_geom_hash = g_hash_table_new (g_str_hash, g_str_equal);
+ }
+
+ g_hash_table_foreach(window_geom_hash, write_recent_geom, rf);
+}
+
+
+void
+window_destroy(GtkWidget *win)
+{
+ window_geometry_t geom;
+ const gchar *name;
+
+ if (!win)
+ return;
+
+ /* get_geometry must be done *before* destroy is running, as the window geometry
+ * cannot be retrieved at destroy time (so don't use event "destroy" for this) */
+ /* ...and don't do this at all, if we currently have no GdkWindow (e.g. if the
+ * GtkWidget is hidden) */
+ if(gtk_widget_get_has_window (win) && gtk_widget_get_visible(win)) {
+ window_get_geometry(win, &geom);
+
+ name = g_object_get_data(G_OBJECT(win), WINDOW_GEOM_KEY);
+ if(name) {
+ window_geom_save(name, &geom);
+ g_free((gpointer)name);
+ }
+ }
+
+ gtk_widget_destroy(win);
+}
+
+#if 0
+/* Do we need this one ? */
+/* convert an xpm to a GtkWidget, using the window settings from it's parent */
+/* (be sure that the parent window is already being displayed) */
+GtkWidget *xpm_to_widget_from_parent(GtkWidget *parent, const char ** xpm) {
+ GdkPixbuf * pixbuf;
+ GdkPixmap * pixmap;
+ GdkBitmap * bitmap;
+
+
+ pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
+ gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf, gtk_widget_get_colormap(parent), &pixmap, &bitmap, 128);
+
+ return gtk_image_new_from_pixmap (pixmap, bitmap);
+}
+#endif
+
+/* convert an xpm to a GtkWidget */
+GtkWidget *xpm_to_widget(const char ** xpm) {
+ GdkPixbuf *pixbuf;
+
+ pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
+ return gtk_image_new_from_pixbuf(pixbuf);
+}
+
+/* Convert an pixbuf data to a GtkWidget */
+/* Data should be created with "gdk-pixbuf-csource --raw" */
+GtkWidget *pixbuf_to_widget(const char * pb_data) {
+ GdkPixbuf *pixbuf;
+
+ pixbuf = gdk_pixbuf_new_from_inline (-1, pb_data, FALSE, NULL);
+ return gtk_image_new_from_pixbuf(pixbuf);
+}
+
+/*
+ * Key to attach the "un-decorated" title to the window, so that if the
+ * user-specified decoration changes, we can correctly update the
+ * window title.
+ */
+#define MAIN_WINDOW_NAME_KEY "main_window_name"
+
+/* Set the name of the top level main_window_name with the specified string and call
+ update_main_window_title() to construct the full title and display it in the main window
+ and its icon title. */
+void
+set_main_window_name(const gchar *window_name)
+{
+ gchar *old_window_name;
+
+ /* Attach the new un-decorated window name to the window. */
+ old_window_name = g_object_get_data(G_OBJECT(top_level), MAIN_WINDOW_NAME_KEY);
+ g_free(old_window_name);
+ g_object_set_data(G_OBJECT(top_level), MAIN_WINDOW_NAME_KEY, g_strdup(window_name));
+
+ update_main_window_title();
+}
+
+/* Construct the main window's title with the current main_window_name, optionally appended
+ with the user-specified title and/or wireshark version. Display the result in the main
+ window title bar and in its icon title.
+ NOTE: The name was changed from '_name' to '_title' because main_window_name is actually
+ set in set_main_window_name() and is only one of the components of the title. */
+void
+update_main_window_title(void)
+{
+ gchar *window_name;
+ gchar *title;
+
+ /* Get the current filename or other title set in set_main_window_name */
+ window_name = g_object_get_data(G_OBJECT(top_level), MAIN_WINDOW_NAME_KEY);
+ if (window_name != NULL) {
+ /* Optionally append the user-defined window title */
+ title = create_user_window_title(window_name);
+
+ /* Optionally append the version */
+ if (prefs.gui_version_in_start_page) {
+ gchar *old_title = title;
+ title = g_strdup_printf("%s [Wireshark %s %s]", title, VERSION, wireshark_svnversion);
+ g_free(old_title);
+ }
+ gtk_window_set_title(GTK_WINDOW(top_level), title);
+ g_free(title);
+ }
+}
+
+/* update the main window */
+void main_window_update(void)
+{
+ while (gtk_events_pending()) gtk_main_iteration();
+}
+
+/* exit the main window */
+void main_window_exit(void)
+{
+ exit(0);
+}
+
+#ifdef HAVE_LIBPCAP
+
+/* quit a nested main window */
+void main_window_nested_quit(void)
+{
+ if (gtk_main_level() > 0)
+ gtk_main_quit();
+}
+
+/* quit the main window */
+void main_window_quit(void)
+{
+ gtk_main_quit();
+}
+
+
+
+typedef struct pipe_input_tag {
+ gint source;
+ gpointer user_data;
+ int *child_process;
+ pipe_input_cb_t input_cb;
+ guint pipe_input_id;
+#ifdef _WIN32
+#else
+ GIOChannel *channel;
+#endif
+} pipe_input_t;
+
+
+#ifdef _WIN32
+/* The timer has expired, see if there's stuff to read from the pipe,
+ if so, do the callback */
+static gboolean
+pipe_timer_cb(gpointer data)
+{
+ HANDLE handle;
+ DWORD avail = 0;
+ gboolean result, result1;
+ DWORD childstatus;
+ pipe_input_t *pipe_input = data;
+ gint iterations = 0;
+
+
+ /* try to read data from the pipe only 5 times, to avoid blocking */
+ while(iterations < 5) {
+ /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: new iteration");*/
+
+ /* Oddly enough although Named pipes don't work on win9x,
+ PeekNamedPipe does !!! */
+ handle = (HANDLE) _get_osfhandle (pipe_input->source);
+ result = PeekNamedPipe(handle, NULL, 0, NULL, &avail, NULL);
+
+ /* Get the child process exit status */
+ result1 = GetExitCodeProcess((HANDLE)*(pipe_input->child_process),
+ &childstatus);
+
+ /* If the Peek returned an error, or there are bytes to be read
+ or the childwatcher thread has terminated then call the normal
+ callback */
+ if (!result || avail > 0 || childstatus != STILL_ACTIVE) {
+
+ /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: data avail");*/
+
+ if(pipe_input->pipe_input_id != 0) {
+ /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: stop timer");*/
+ /* avoid reentrancy problems and stack overflow */
+ g_source_remove(pipe_input->pipe_input_id);
+ pipe_input->pipe_input_id = 0;
+ }
+
+ /* And call the real handler */
+ if (!pipe_input->input_cb(pipe_input->source, pipe_input->user_data)) {
+ g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: input pipe closed, iterations: %u", iterations);
+ /* pipe closed, return false so that the old timer is not run again */
+ return FALSE;
+ }
+ }
+ else {
+ /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: no data avail");*/
+ /* No data, stop now */
+ break;
+ }
+
+ iterations++;
+ }
+
+ if(pipe_input->pipe_input_id == 0) {
+ /* restore pipe handler */
+ pipe_input->pipe_input_id = g_timeout_add(200, pipe_timer_cb, data);
+ /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: finished with iterations: %u, new timer", iterations);*/
+
+ /* Return false so that the old timer is not run again */
+ return FALSE;
+ } else {
+ /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: finished with iterations: %u, old timer", iterations);*/
+
+ /* we didn't stopped the old timer, so let it run */
+ return TRUE;
+ }
+}
+
+#else /* _WIN32 */
+
+/* There's stuff to read from the sync pipe, meaning the child has sent
+ us a message, or the sync pipe has closed, meaning the child has
+ closed it (perhaps because it exited). */
+static gboolean
+pipe_input_cb(GIOChannel *source _U_, GIOCondition condition _U_,
+ gpointer data)
+{
+ pipe_input_t *pipe_input = data;
+
+
+ /* avoid reentrancy problems and stack overflow */
+ g_source_remove(pipe_input->pipe_input_id);
+
+ if (pipe_input->input_cb(pipe_input->source, pipe_input->user_data)) {
+ /* restore pipe handler */
+ pipe_input->pipe_input_id = g_io_add_watch_full (pipe_input->channel,
+ G_PRIORITY_HIGH,
+ G_IO_IN|G_IO_ERR|G_IO_HUP,
+ pipe_input_cb,
+ pipe_input,
+ NULL);
+ }
+ return TRUE;
+}
+#endif
+
+void pipe_input_set_handler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
+{
+ static pipe_input_t pipe_input;
+
+ pipe_input.source = source;
+ pipe_input.child_process = child_process;
+ pipe_input.user_data = user_data;
+ pipe_input.input_cb = input_cb;
+
+#ifdef _WIN32
+ /* Tricky to use pipes in win9x, as no concept of wait. NT can
+ do this but that doesn't cover all win32 platforms. GTK can do
+ this but doesn't seem to work over processes. Attempt to do
+ something similar here, start a timer and check for data on every
+ timeout. */
+ /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
+ pipe_input.pipe_input_id = g_timeout_add(200, pipe_timer_cb, &pipe_input);
+#else
+ pipe_input.channel = g_io_channel_unix_new(source);
+ g_io_channel_set_encoding(pipe_input.channel, NULL, NULL);
+ pipe_input.pipe_input_id = g_io_add_watch_full(pipe_input.channel,
+ G_PRIORITY_HIGH,
+ G_IO_IN|G_IO_ERR|G_IO_HUP,
+ pipe_input_cb,
+ &pipe_input,
+ NULL);
+#endif
+}
+
+
+#endif /* HAVE_LIBPCAP */
+
+/* Given a pointer to a GtkWidget for a top-level window, raise it and
+ de-iconify it. This routine is used if the user has done something to
+ ask that a window of a certain type be popped up when there can be only
+ one such window and such a window has already been popped up - we
+ pop up the existing one rather than creating a new one.
+
+ XXX - we should request that it be given the input focus, too. Alas,
+ GDK has nothing to do that, e.g. by calling "XSetInputFocus()" in a
+ window in X. Besides, using "XSetInputFocus()" doesn't work anyway,
+ apparently due to the way GTK+/GDK manages the input focus.
+
+ The X Desktop Group's Window Manager Standard specifies, in the section
+ on Root Window Properties, an _NET_ACTIVE_WINDOW client message that
+ can be sent to the root window, containing the window ID of the
+ window to activate; I infer that this might be the way to give the
+ window the input focus - I assume that means it's also de-iconified,
+ but I wouldn't assume it'd raise it.
+
+ XXX - will this do the right thing on window systems other than X? */
+void
+reactivate_window(GtkWidget *win)
+{
+ GdkWindow *win_window;
+
+ win_window = gtk_widget_get_window(win);
+
+ gdk_window_show(win_window);
+ gdk_window_raise(win_window);
+}
+
+/* List of all GtkScrolledWindows, so we can globally set the scrollbar
+ placement of all of them. */
+static GList *scrolled_windows;
+
+static void setup_scrolled_window(GtkWidget *scrollw);
+static void forget_scrolled_window(GtkWidget *scrollw, gpointer data);
+static void set_scrollbar_placement_scrollw(GtkWidget *scrollw);
+
+/* Create a GtkScrolledWindow, set its scrollbar placement appropriately,
+ and remember it. */
+GtkWidget *
+scrolled_window_new(GtkAdjustment *hadjustment, GtkAdjustment *vadjustment)
+{
+ GtkWidget *scrollw;
+
+ scrollw = gtk_scrolled_window_new(hadjustment, vadjustment);
+ setup_scrolled_window(scrollw);
+ return scrollw;
+}
+
+/* Set a GtkScrolledWindow's scrollbar placement and add it to the list
+ of GtkScrolledWindows. */
+static void
+setup_scrolled_window(GtkWidget *scrollw)
+{
+ set_scrollbar_placement_scrollw(scrollw);
+
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollw),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+ scrolled_windows = g_list_append(scrolled_windows, scrollw);
+
+ /* Catch the "destroy" event on the widget, so that we remove it from
+ the list when it's destroyed. */
+ g_signal_connect(scrollw, "destroy", G_CALLBACK(forget_scrolled_window), NULL);
+}
+
+/* Remove a GtkScrolledWindow from the list of GtkScrolledWindows. */
+static void
+forget_scrolled_window(GtkWidget *scrollw, gpointer data _U_)
+{
+ scrolled_windows = g_list_remove(scrolled_windows, scrollw);
+}
+
+/* Set the scrollbar placement of a GtkScrolledWindow based upon user
+ preference. */
+static void
+set_scrollbar_placement_scrollw(GtkWidget *scrollw)
+{
+ if (prefs.gui_scrollbar_on_right) {
+ gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
+ GTK_CORNER_TOP_LEFT);
+ } else {
+ gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
+ GTK_CORNER_TOP_RIGHT);
+ }
+}
+
+static void
+set_scrollbar_placement_cb(gpointer data, gpointer user_data _U_)
+{
+ set_scrollbar_placement_scrollw((GtkWidget *)data);
+}
+
+/* Set the scrollbar placement of all GtkScrolledWindows based on
+ user preference. */
+void
+set_scrollbar_placement_all(void)
+{
+ g_list_foreach(scrolled_windows, set_scrollbar_placement_cb, NULL);
+}
+
+/* List of all CTrees/TreeViews, so we can globally set the line and
+ * expander style of all of them. */
+static GList *trees;
+
+static void setup_tree(GtkWidget *tree);
+static void forget_tree(GtkWidget *tree, gpointer data);
+static void set_tree_styles(GtkWidget *tree);
+static gboolean tree_view_key_pressed_cb(GtkWidget *tree, GdkEventKey *event, gpointer user_data _U_);
+
+/* Create a Tree, give it the right styles, and remember it. */
+GtkWidget *
+tree_view_new(GtkTreeModel *model)
+{
+ GtkWidget *tree;
+
+ tree = gtk_tree_view_new_with_model(model);
+ setup_tree(tree);
+ return tree;
+}
+
+/* Set a Tree's styles and add it to the list of Trees. */
+static void
+setup_tree(GtkWidget *tree)
+{
+ set_tree_styles(tree);
+
+ trees = g_list_append(trees, tree);
+
+ /* Catch the "destroy" event on the widget, so that we remove it from
+ the list when it's destroyed. */
+ g_signal_connect(tree, "destroy", G_CALLBACK(forget_tree), NULL);
+ g_signal_connect(tree, "key-press-event", G_CALLBACK(tree_view_key_pressed_cb), NULL );
+}
+
+/* Remove a Tree from the list of Trees. */
+static void
+forget_tree(GtkWidget *tree, gpointer data _U_)
+{
+ trees = g_list_remove(trees, tree);
+}
+
+/* Set the styles of a Tree based upon user preferences. */
+static void
+set_tree_styles(GtkWidget *tree)
+{
+ g_assert(prefs.gui_altern_colors >= 0 && prefs.gui_altern_colors <= 1);
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree),
+ prefs.gui_altern_colors);
+}
+
+static void
+set_tree_styles_cb(gpointer data, gpointer user_data _U_)
+{
+ set_tree_styles((GtkWidget *)data);
+}
+
+/* Set the styles of all Trees based upon style values. */
+void
+set_tree_styles_all(void)
+{
+ g_list_foreach(trees, set_tree_styles_cb, NULL);
+}
+
+/* Move the currently-selected item in a list store up or down one position. */
+gboolean
+tree_view_list_store_move_selection(GtkTreeView *tree, gboolean move_up)
+{
+ GtkTreeIter from, to;
+ GtkTreeModel *model;
+ GtkTreeSelection *sel;
+ GtkTreePath *path_from, *path_to;
+
+ sel = gtk_tree_view_get_selection(tree);
+ if (! gtk_tree_selection_get_selected(sel, &model, &from)) {
+ return FALSE;
+ }
+
+ path_from = gtk_tree_model_get_path(model, &from);
+ if (!path_from) {
+ return FALSE;
+ }
+
+ path_to = gtk_tree_path_copy(path_from);
+ /* XXX - Why does one return void and the other return a gboolean? */
+ if (move_up) {
+ gtk_tree_path_prev(path_to);
+ } else {
+ gtk_tree_path_next(path_to);
+ }
+
+ if (gtk_tree_path_compare(path_from, path_to) == 0) {
+ gtk_tree_path_free(path_from);
+ gtk_tree_path_free(path_to);
+ return FALSE;
+ }
+
+ gtk_tree_model_get_iter(model, &to, path_to);
+ gtk_list_store_swap(GTK_LIST_STORE(model), &from, &to);
+ gtk_tree_path_free(path_from);
+ gtk_tree_path_free(path_to);
+ return TRUE;
+}
+
+/* Find the selected row number in a list store. */
+gint
+tree_view_list_store_get_selected_row(GtkTreeView *tree) {
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreeSelection *sel;
+ GtkTreePath *path;
+ gchar *path_str;
+ gint row;
+
+ sel = gtk_tree_view_get_selection(tree);
+ if (! gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ return -1;
+ }
+
+ path = gtk_tree_model_get_path(model, &iter);
+ if (!path) {
+ return FALSE;
+ }
+
+ path_str = gtk_tree_path_to_string(path);
+ gtk_tree_path_free(path);
+
+ row = (gint) strtol(path_str, NULL, 10);
+ g_free(path_str);
+
+ return row;
+}
+
+/* append a row to the simple list */
+/* use it like: simple_list_append(list, 0, "first", 1, "second", -1) */
+void
+simple_list_append(GtkWidget *list, ...)
+{
+ va_list ap;
+
+ GtkTreeIter iter;
+ GtkListStore *store;
+
+ va_start(ap, list);
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set_valist(store, &iter, ap);
+ va_end(ap);
+}
+
+/* create a simple list widget */
+GtkWidget *
+simple_list_new(gint cols, const gchar **titles) {
+ GtkWidget *plugins_list;
+ int i;
+ GtkListStore *store;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+
+ g_assert(cols <= 10);
+ store = gtk_list_store_new(cols,
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+ plugins_list = tree_view_new(GTK_TREE_MODEL(store));
+ g_object_unref(G_OBJECT(store));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(plugins_list), (titles != NULL));
+ for(i=0; i<cols; i++) {
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(titles ? titles[i] : "", renderer,
+ "text", i, NULL);
+ gtk_tree_view_column_set_sort_column_id(column, i);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(plugins_list), column);
+ }
+
+ return plugins_list;
+}
+
+void render_as_url(GtkCellRenderer *cell)
+{
+ g_object_set(cell, "foreground", "blue", NULL);
+ g_object_set(cell, "foreground-set", TRUE, NULL);
+
+ g_object_set(cell, "underline", PANGO_UNDERLINE_SINGLE, NULL);
+ g_object_set(cell, "underline-set", TRUE, NULL);
+}
+
+void simple_list_url_col(GtkWidget *list, gint col)
+{
+ GtkTreeViewColumn *ul_column;
+ GList *renderers_list;
+ GtkCellRenderer *ul_renderer;
+
+ /* make the column look like a link ... */
+ ul_column = gtk_tree_view_get_column(GTK_TREE_VIEW(list), col);
+
+ renderers_list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(ul_column));
+
+ if(renderers_list != NULL) {
+ /* it is simple list - there should be only one renderer */
+ ul_renderer = (GtkCellRenderer*)renderers_list->data;
+
+ render_as_url(ul_renderer);
+
+ g_list_free(renderers_list);
+ }
+
+}
+
+void
+copy_to_clipboard(GString *str)
+{
+ GtkClipboard *cb;
+
+ cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); /* Get the default clipboard */
+ gtk_clipboard_set_text(cb, str->str, -1); /* Copy the byte data into the clipboard */
+}
+
+
+typedef struct _copy_binary_t {
+ guint8* data;
+ int len;
+} copy_binary_t;
+
+static
+copy_binary_t* create_copy_binary_t(const guint8* data, int len)
+{
+ copy_binary_t* copy_data;
+
+ g_assert(len > 0);
+ copy_data = g_new(copy_binary_t,1);
+ copy_data->data = g_new(guint8,len);
+ copy_data->len = len;
+ memcpy(copy_data->data,data,len * sizeof(guint8));
+ return copy_data;
+}
+
+static void destroy_copy_binary_t(copy_binary_t* copy_data) {
+ g_free(copy_data->data);
+ g_free(copy_data);
+}
+
+static
+void copy_binary_free_cb(GtkClipboard *clipboard _U_, gpointer user_data_or_owner)
+{
+ copy_binary_t* copy_data;
+ copy_data = user_data_or_owner;
+ destroy_copy_binary_t(copy_data);
+}
+
+static
+void copy_binary_get_cb(GtkClipboard *clipboard _U_, GtkSelectionData *selection_data, guint info _U_, gpointer user_data_or_owner)
+{
+ copy_binary_t* copy_data;
+
+ copy_data = user_data_or_owner;
+
+ /* Just do a dumb set as binary data */
+ gtk_selection_data_set(selection_data, GDK_NONE, 8, copy_data->data, copy_data->len);
+}
+
+void copy_binary_to_clipboard(const guint8* data_p, int len)
+{
+ static GtkTargetEntry target_entry[] = {
+ {"application/octet_stream", 0, 0}}; /* XXX - this not understood by most applications,
+ * but can be pasted into the better hex editors - is
+ * there something better that we can do?
+ */
+
+ GtkClipboard *cb;
+ copy_binary_t* copy_data;
+ gboolean ret;
+
+ if(len <= 0) {
+ return; /* XXX would it be better to clear the clipboard? */
+ }
+ cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); /* Get the default clipboard */
+ copy_data = create_copy_binary_t(data_p,len);
+
+ ret = gtk_clipboard_set_with_data(cb,target_entry,1,
+ copy_binary_get_cb, copy_binary_free_cb,copy_data);
+
+ if(!ret) {
+ destroy_copy_binary_t(copy_data);
+ }
+}
+
+/*
+ * Create a new window title string with user-defined title preference.
+ * (Or ignore it if unspecified).
+ */
+gchar *
+create_user_window_title(const gchar *caption)
+{
+ /* fail-safe */
+ if (caption == NULL)
+ return g_strdup("");
+
+ /* no user-defined title specified */
+ if ((prefs.gui_window_title == NULL) || (*prefs.gui_window_title == '\0'))
+ return g_strdup(caption);
+
+ return g_strdup_printf("%s [%s]", caption, prefs.gui_window_title);
+}
+
+/* XXX move toggle_tree over from proto_draw.c to handle GTK+ 1 */
+/*
+ * This callback is invoked when keyboard focus is within either
+ * the packetlist view or the detail view. The keystrokes processed
+ * within this callback are attempting to modify the detail view.
+ * Within the detail view we special case the Left Arrow, Backspace
+ * and Enter keys depending on the state of the expander (if any)
+ * for the item in focus.
+ *
+ * Returning FALSE allows processing of the original key_press_event
+ * by other callbacks. Left/Right scrolling of the packetlist
+ * view and expanding/collapsing of the detail view lists is
+ * handled by the default GtkTreeView key-press-event call back.
+ *
+ * XXX - Would an improved version of this callback test to see which
+ * of the two GtkTreeView lists has focus? Left/Right scrolling of
+ * the packetlist is currently not optimal. It will take several
+ * right or left keypress events before the packetlist responds.
+ * The problem appears to be that the focus is on a particular cell
+ * within the highlighted row cell (like a spreadsheet). Scrolling
+ * of the view right or left will not occur until the focus is
+ * moved to a cell off the left or right edge of the packet list
+ * view. Also TAB/SHIFT-TAB events can move keyboard focus to
+ * the packetlist header where there is currently visual hint
+ * a header cell has focus.
+ */
+static gboolean
+tree_view_key_pressed_cb(GtkWidget *tree, GdkEventKey *event, gpointer user_data _U_)
+{
+ GtkTreeSelection* selection;
+ GtkTreeIter iter;
+ GtkTreeIter parent;
+ GtkTreeModel* model;
+ GtkTreePath* path;
+ gboolean expanded, expandable;
+ int rc = FALSE;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
+ if(!selection) {
+ return FALSE;
+ }
+
+ if(!gtk_tree_selection_get_selected (selection, &model, &iter)) {
+ return FALSE;
+ }
+
+ path = gtk_tree_model_get_path(model, &iter);
+ if(!path) {
+ return FALSE;
+ }
+
+ /* Always FALSE when we're in the packet list (at least until we add sub-packets) */
+ expanded = gtk_tree_view_row_expanded(GTK_TREE_VIEW(tree), path);
+ expandable = gtk_tree_model_iter_has_child(model, &iter);
+
+ switch (event->keyval) {
+ case GDK_Left:
+ if(expanded) {
+ /* Subtree is expanded. Collapse it. */
+ gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree), path);
+ rc = TRUE;
+ break;
+ }
+ /* No break - fall through to jumping to the parent */
+ case GDK_BackSpace:
+ if (!expanded) {
+ /* subtree is already collapsed, jump to parent node */
+ if(! gtk_tree_model_iter_parent(model, &parent, &iter)) {
+ rc = FALSE;
+ break;
+ }
+ gtk_tree_path_free(path);
+ path = gtk_tree_model_get_path(model, &parent);
+ if(!path) {
+ return FALSE;
+ }
+ gtk_tree_view_set_cursor(GTK_TREE_VIEW(tree), path,
+ NULL /* focus_column */,
+ FALSE /* !start_editing */);
+ rc = TRUE;
+ break;
+ }
+ break;
+ case GDK_Right:
+ if (expandable) {
+ /* We have a subtree. Try to expand it. */
+ gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), path, FALSE /* !open_all */);
+ rc = TRUE;
+ break;
+ } else {
+ rc = FALSE;
+ break;
+ }
+ case GDK_Return:
+ case GDK_KP_Enter:
+ /* Reverse the current state. */
+ if (expanded)
+ gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree), path);
+ else
+ gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), path, FALSE /* !open_all */);
+ rc = TRUE;
+ break;
+ }
+
+ if(path) {
+ gtk_tree_path_free(path);
+ }
+ return rc;
+}
+
+void
+switch_to_fixed_col(GtkTreeView *view)
+{
+ gint size;
+ GtkTreeViewColumn *column;
+ GList *columns, *list;
+
+ columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(view));
+ list = columns;
+ while(columns) {
+ column = columns->data;
+ size = gtk_tree_view_column_get_width (column);
+ gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_FIXED);
+ if (size > gtk_tree_view_column_get_fixed_width(column))
+ gtk_tree_view_column_set_fixed_width(column, size);
+ columns = g_list_next(columns);
+ }
+ g_list_free(list);
+
+ gtk_tree_view_set_fixed_height_mode(view, TRUE);
+}
+
+gint
+get_default_col_size(GtkWidget *view, const gchar *str)
+{
+ PangoLayout *layout;
+ gint col_width;
+
+ layout = gtk_widget_create_pango_layout(view, str);
+ pango_layout_get_pixel_size(layout,
+ &col_width, /* width */
+ NULL); /* height */
+ g_object_unref(G_OBJECT(layout));
+ /* Add a single character's width to get some spacing between columns */
+ return col_width + (pango_font_description_get_size(user_font_get_regular()) / PANGO_SCALE);
+}
+
+
+/*
+ * This function can be called from gtk_tree_view_column_set_cell_data_func()
+ * the user data must be the colum number.
+ * Present floats with two decimals
+ */
+void
+float_data_func (GtkTreeViewColumn *column _U_,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gfloat float_val;
+ gchar buf[20];
+ char *savelocale;
+
+ /* the col to get data from is in userdata */
+ gint float_col = GPOINTER_TO_INT(user_data);
+
+ gtk_tree_model_get(model, iter, float_col, &float_val, -1);
+
+ /* save the current locale */
+ savelocale = setlocale(LC_NUMERIC, NULL);
+ /* switch to "C" locale to avoid problems with localized decimal separators
+ * in g_snprintf("%f") functions
+ */
+ setlocale(LC_NUMERIC, "C");
+
+ g_snprintf(buf, sizeof(buf), "%.2f", float_val);
+ /* restore previous locale setting */
+ setlocale(LC_NUMERIC, savelocale);
+
+ g_object_set(renderer, "text", buf, NULL);
+}
+
+/*
+ * This function can be called from gtk_tree_view_column_set_cell_data_func()
+ * the user data must be the colum number.
+ * Present value as hexadecimal.
+ */
+void
+present_as_hex_func (GtkTreeViewColumn *column _U_,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ guint val;
+ gchar buf[35];
+
+ /* the col to get data from is in userdata */
+ gint col = GPOINTER_TO_INT(user_data);
+
+ gtk_tree_model_get(model, iter, col, &val, -1);
+
+ g_snprintf(buf, sizeof(buf), "0x%02x", val);
+
+ g_object_set(renderer, "text", buf, NULL);
+}
+
+void
+u64_data_func (GtkTreeViewColumn *column _U_,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ guint64 val;
+ int i = 0;
+ gchar *bp;
+ gchar buf[35];
+
+ /* the col to get data from is in userdata */
+ gint col = GPOINTER_TO_INT(user_data);
+
+ gtk_tree_model_get(model, iter, col, &val, -1);
+
+ bp = &buf[34];
+ *bp = 0;
+ do {
+ *--bp = (gchar)(val % 10) +'0';
+ if (!(++i % 3)) {
+ *--bp = ' ';
+ }
+ } while ((val /= 10) != 0 && bp > buf);
+ g_object_set(renderer, "text", bp, NULL);
+}
+
+/*
+ * This function can be called from gtk_tree_view_column_set_cell_data_func()
+ * The user data must be the column number.
+ * Renders the const static string whose pointer is stored.
+ */
+void
+str_ptr_data_func (GtkTreeViewColumn *column _U_,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ const gchar *str = NULL;
+
+ /* The col to get data from is in userdata */
+ gint data_column = GPOINTER_TO_INT(user_data);
+
+ gtk_tree_model_get(model, iter, data_column, &str, -1);
+ /* XXX should we check that str is non NULL and print a warning or do assert? */
+
+ g_object_set(renderer, "text", str, NULL);
+}
+
+gint
+str_ptr_sort_func(GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ const gchar *str_a = NULL;
+ const gchar *str_b = NULL;
+ gint ret = 0;
+
+ /* The col to get data from is in userdata */
+ gint data_column = GPOINTER_TO_INT(user_data);
+
+ gtk_tree_model_get(model, a, data_column, &str_a, -1);
+ gtk_tree_model_get(model, b, data_column, &str_b, -1);
+
+ if (str_a == str_b) {
+ /* it's worth testing because a lot of rows point to the same data */
+ return 0;
+ }
+ else if (str_a == NULL || str_b == NULL) {
+ ret = (str_a == NULL) ? -1 : 1;
+ }
+ else {
+ ret = g_ascii_strcasecmp(str_a,str_b);
+ }
+ return ret;
+}
+
+/** --------------------------------------------------
+ * ws_combo_box_text_and_pointer convenience functions
+ * (Code adapted from GtkComboBox.c)
+ */
+
+/**
+ * ws_combo_box_new_text_and_pointer_full:
+ *
+ * Convenience function which constructs a new "text and pointer" combo box, which
+ * is a #GtkComboBox just displaying strings and storing a pointer associated with
+ * each combo_box entry; The pointer can be retrieved when an entry is selected.
+ * Also: optionally returns the cell renderer for the combo box.
+ * If you use this function to create a text_and_pointer combo_box,
+ * you should only manipulate its data source with the
+ * following convenience functions:
+ * ws_combo_box_append_text_and_pointer()
+ * ws_combo_box_append_text_and_pointer_full()
+ *
+ * @param cell_p pointer to return the 'GtkCellRenderer *' for the combo box (or NULL).
+ * @return A pointer to a new text_and_pointer combo_box.
+ */
+
+/* Note:
+ * GtkComboBox style property: "appears-as-list":
+ * Default: 0: ie: displays as menus
+ * Wireshark Windows gtkrc: 1: ie: displays as lists (treeview)
+ */
+GtkWidget *
+ws_combo_box_new_text_and_pointer_full(GtkCellRenderer **cell_p) {
+ GtkWidget *combo_box;
+ GtkCellRenderer *cell;
+ GtkTreeStore *store;
+
+ /* The Tree store for the GtkComboBox has 3 columns:
+ 0: text string for display in GtkComboBox list;
+ 1: pointer (data) associated with the entry;
+ 2: True/False depending upon whether this entry is selectable ("sensitive" attribute).
+ */
+
+ store = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
+ combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL (store));
+ g_object_unref(store);
+ cell = gtk_cell_renderer_text_new();
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), cell, TRUE);
+ gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell,
+ "text", 0, "sensitive", 2,
+ NULL);
+ if (cell_p != NULL) {
+ *cell_p = cell;
+ }
+ return combo_box;
+}
+
+/**
+ * ws_combo_box_new_text_and_pointer:
+ *
+ * Convenience function which constructs a new "text and pointer" combo box, which
+ * is a #GtkComboBox just displaying strings and storing a pointer associated with
+ * each combo_box entry; The pointer can be retrieved when an entry is selected.
+ * If you use this function to create a text_and_pointer combo_box,
+ * you should only manipulate its data source with the
+ * following convenience functions:
+ * ws_combo_box_append_text_and_pointer()
+ * ws_combo_box_append_text_and_pointer_full()
+ *
+ * @return A pointer to a new text_and_pointer combo_box.
+ */
+
+GtkWidget *
+ws_combo_box_new_text_and_pointer(void) {
+ return ws_combo_box_new_text_and_pointer_full(NULL);
+}
+
+
+/**
+ * ws_combo_box_clear_text_and_pointer:
+ * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
+ *
+ * Clears all the text_and_pointer entries in the text_and_pointer combo_box.
+ * Note: A "changed" signal will be emitted after the clear if there was
+ * an active (selected) entry before the clear.
+ * You should use this function only with combo boxes constructed with
+ * ws_combo_box_new_text_and_pointer().
+ */
+void
+ws_combo_box_clear_text_and_pointer(GtkComboBox *combo_box)
+{
+ gtk_tree_store_clear(GTK_TREE_STORE(gtk_combo_box_get_model(combo_box)));
+}
+
+/**
+ * ws_combo_box_append_text_and_pointer_full:
+ * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
+ * @param parent_iter Parent row for apending; NULL if appending to tree top-level;
+ * @param text A string to be displayed as an entry in the dropdown list of the combo_box
+ * @param ptr A pointer to be associated with this entry of the combo_box
+ * @param sensitive TRUE/FALSE to set sensitivity of the entry
+ * @return A GtkTreeIter pointing to the appended GtkVomboBox entry.
+ *
+ * Appends text and ptr to the list of strings and pointers stored in combo_box.
+ * The text and ptr can be appended to any existing level of the tree_store.
+ * The sensitivity of the row will be set as requested.
+ * Note that you can only use this function with combo boxes constructed with
+ * ws_combo_box_new_text_and_pointer().
+ */
+GtkTreeIter
+ws_combo_box_append_text_and_pointer_full(GtkComboBox *combo_box,
+ GtkTreeIter *parent_iter,
+ const gchar *text,
+ const gpointer ptr,
+ const gboolean sensitive)
+{
+ GtkTreeIter iter;
+ GtkTreeStore *store;
+
+ store = GTK_TREE_STORE(gtk_combo_box_get_model(combo_box));
+
+ gtk_tree_store_append(store, &iter, parent_iter);
+ gtk_tree_store_set(store, &iter, 0, text, 1, ptr, 2, sensitive, -1);
+
+ return iter;
+}
+
+/**
+ * ws_combo_box_append_text_and_pointer:
+ * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
+ * @param text A string to be displayed as an entry in the dropdown list of the combo_box
+ * @param ptr A pointer to be associated with this entry of the combo_box
+ * @return A GtkTreeIter pointing to the appended GtkComboBox entry.
+ *
+ * Appends text and ptr to the list of strings and pointers stored in combo_box. Note that
+ * you can only use this function with combo boxes constructed with
+ * ws_combo_box_new_text_and_pointer().
+ */
+GtkTreeIter
+ws_combo_box_append_text_and_pointer(GtkComboBox *combo_box,
+ const gchar *text,
+ const gpointer ptr)
+{
+ return ws_combo_box_append_text_and_pointer_full(combo_box, NULL, text, ptr, TRUE);
+}
+
+
+/**
+ * ws_combo_box_get_active_pointer:
+ * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
+ * @param ptr A pointer to a location in which to store the pointer associated with the active entry
+ * @return TRUE if an entry is selected (i.e: an active entry exists); FALSE otherwise
+ *
+ * You can only use this function with combo boxes constructed with
+ * ws_combo_box_new_text_and_pointer().
+ */
+gboolean
+ws_combo_box_get_active_pointer(GtkComboBox *combo_box, gpointer *ptr)
+{
+ GtkTreeStore *store;
+ GtkTreeIter iter;
+
+ *ptr = NULL;
+
+ if (gtk_combo_box_get_active_iter(combo_box, &iter)) {
+ store = GTK_TREE_STORE(gtk_combo_box_get_model(combo_box));
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
+ 1, ptr, -1);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * ws_combo_box_get_active:
+ * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
+ * @return Index of the active entry; -1 if no entry is selected;
+ * Note: If the active item is not an immediate child of root of the tree then
+ * the index returned is that of the top-level for the acftive entry.
+ */
+gint
+ws_combo_box_get_active(GtkComboBox *combo_box)
+{
+ return gtk_combo_box_get_active(combo_box);
+}
+
+/**
+ * ws_combo_box_set_active:
+ * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
+ * @param idx of the entry which is to be set as active (ie: selected).
+ * Index refers to the immediate children of the tree.
+ */
+void
+ws_combo_box_set_active(GtkComboBox *combo_box, gint idx)
+{
+ gtk_combo_box_set_active(combo_box, idx);
+}
+
+/**
+ * ws_combo_box_set_active_iter:
+ * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
+ * @param iter of the entry which is to be set as active (ie: selected).
+ */
+void
+ws_combo_box_set_active_iter(GtkComboBox *combo_box, GtkTreeIter *iter)
+{
+ gtk_combo_box_set_active_iter(combo_box, iter);
+}
+
+
+/* Copy functions from GTK 3.0 to be used if GTK version is 2.22 or 2.24 to be able save Graphs to file */
+#if GTK_CHECK_VERSION(2,22,0)
+#if !GTK_CHECK_VERSION(3,0,0)
+static cairo_format_t
+gdk_cairo_format_for_content (cairo_content_t content)
+{
+ switch (content)
+ {
+ case CAIRO_CONTENT_COLOR:
+ return CAIRO_FORMAT_RGB24;
+ case CAIRO_CONTENT_ALPHA:
+ return CAIRO_FORMAT_A8;
+ case CAIRO_CONTENT_COLOR_ALPHA:
+ default:
+ return CAIRO_FORMAT_ARGB32;
+ }
+}
+
+static cairo_surface_t *
+gdk_cairo_surface_coerce_to_image (cairo_surface_t *surface,
+ cairo_content_t content,
+ int src_x,
+ int src_y,
+ int width,
+ int height)
+{
+ cairo_surface_t *copy;
+ cairo_t *cr;
+
+ copy = cairo_image_surface_create (gdk_cairo_format_for_content (content),
+ width,
+ height);
+
+ cr = cairo_create (copy);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_surface (cr, surface, -src_x, -src_y);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ return copy;
+}
+
+static void
+convert_alpha (guchar *dest_data,
+ int dest_stride,
+ guchar *src_data,
+ int src_stride,
+ int src_x,
+ int src_y,
+ int width,
+ int height)
+{
+ int x, y;
+
+ src_data += src_stride * src_y + src_x * 4;
+
+ for (y = 0; y < height; y++) {
+ guint32 *src = (guint32 *) src_data;
+
+ for (x = 0; x < width; x++) {
+ guint alpha = src[x] >> 24;
+
+ if (alpha == 0)
+ {
+ dest_data[x * 4 + 0] = 0;
+ dest_data[x * 4 + 1] = 0;
+ dest_data[x * 4 + 2] = 0;
+ }
+ else
+ {
+ dest_data[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
+ dest_data[x * 4 + 1] = (((src[x] & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
+ dest_data[x * 4 + 2] = (((src[x] & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
+ }
+ dest_data[x * 4 + 3] = alpha;
+ }
+
+ src_data += src_stride;
+ dest_data += dest_stride;
+ }
+}
+
+static void
+convert_no_alpha (guchar *dest_data,
+ int dest_stride,
+ guchar *src_data,
+ int src_stride,
+ int src_x,
+ int src_y,
+ int width,
+ int height)
+{
+ int x, y;
+
+ src_data += src_stride * src_y + src_x * 4;
+
+ for (y = 0; y < height; y++) {
+ guint32 *src = (guint32 *) src_data;
+
+ for (x = 0; x < width; x++) {
+ dest_data[x * 3 + 0] = src[x] >> 16;
+ dest_data[x * 3 + 1] = src[x] >> 8;
+ dest_data[x * 3 + 2] = src[x];
+ }
+
+ src_data += src_stride;
+ dest_data += dest_stride;
+ }
+}
+
+/**
+ * gdk_pixbuf_get_from_surface:
+ * @surface: surface to copy from
+ * @src_x: Source X coordinate within @surface
+ * @src_y: Source Y coordinate within @surface
+ * @width: Width in pixels of region to get
+ * @height: Height in pixels of region to get
+ *
+ * Transfers image data from a #cairo_surface_t and converts it to an RGB(A)
+ * representation inside a #GdkPixbuf. This allows you to efficiently read
+ * individual pixels from cairo surfaces. For #GdkWindows, use
+ * gdk_pixbuf_get_from_window() instead.
+ *
+ * This function will create an RGB pixbuf with 8 bits per channel.
+ * The pixbuf will contain an alpha channel if the @surface contains one.
+ *
+ * Return value: (transfer full): A newly-created pixbuf with a reference
+ * count of 1, or %NULL on error
+ */
+GdkPixbuf *
+gdk_pixbuf_get_from_surface (cairo_surface_t *surface,
+ gint src_x,
+ gint src_y,
+ gint width,
+ gint height)
+{
+ cairo_content_t content;
+ GdkPixbuf *dest;
+
+ /* General sanity checks */
+ g_return_val_if_fail (surface != NULL, NULL);
+ g_return_val_if_fail (width > 0 && height > 0, NULL);
+
+ content = cairo_surface_get_content (surface) | CAIRO_CONTENT_COLOR;
+ dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+ !!(content & CAIRO_CONTENT_ALPHA),
+ 8,
+ width, height);
+
+ surface = gdk_cairo_surface_coerce_to_image (surface, content,
+ src_x, src_y,
+ width, height);
+ cairo_surface_flush (surface);
+ if (cairo_surface_status (surface) || dest == NULL)
+ {
+ cairo_surface_destroy (surface);
+ return NULL;
+ }
+
+ if (gdk_pixbuf_get_has_alpha (dest))
+ convert_alpha (gdk_pixbuf_get_pixels (dest),
+ gdk_pixbuf_get_rowstride (dest),
+ cairo_image_surface_get_data (surface),
+ cairo_image_surface_get_stride (surface),
+ 0, 0,
+ width, height);
+ else
+ convert_no_alpha (gdk_pixbuf_get_pixels (dest),
+ gdk_pixbuf_get_rowstride (dest),
+ cairo_image_surface_get_data (surface),
+ cairo_image_surface_get_stride (surface),
+ 0, 0,
+ width, height);
+
+ cairo_surface_destroy (surface);
+ return dest;
+}
+#endif /* !GTK_CHECK_VERSION(3,0,0) */
+#endif /* GTK_CHECK_VERSION(2,22,0) */
diff --git a/ui/gtk/gui_utils.h b/ui/gtk/gui_utils.h
new file mode 100644
index 0000000000..9720a2a0d6
--- /dev/null
+++ b/ui/gtk/gui_utils.h
@@ -0,0 +1,538 @@
+/* gui_utils.h
+ * Declarations of GTK+-specific UI utility routines
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GUI_UTILS_H__
+#define __GUI_UTILS_H__
+
+/** @defgroup windows_group Windows
+ *
+ * There are the following toplevel windows:
+ *
+ * - @ref main_window_group
+ * - Statistic Windows (several different statistic windows)
+ *
+ * See: @ref howto_window_page for details.
+ *
+ */
+
+/** @page howto_window_page How to develop a window / dialog
+ *
+ * Windows and dialogs are related to each other. Dialogs are special kind of windows, but they behave
+ * slightly different. A dialog sticks on its parent window; A normal window will be much more independent
+ * from its parent window. Dialogs should be used to ask or tell the user something, while windows should
+ * show data which is independent of the main window.
+ * Dialogs are created by calling dlg_window_new() which in turn will call window_new().
+ * After that, dialogs can be developed the same way as windows; all window related functions in gui_utils.h
+ * can be used for both.
+ *
+ * @section window_create Create a window
+ *
+ * A typical window / dialog will be created by the following calls:
+ *
+ * - window_new() will create a new window with default position and size,
+ * use dlg_window_new() if you need a dialog (transient to the main window)
+ * - gtk_window_set_default_size() will set the default size of the window. Only
+ * needed, if the initial size is not appropriate, e.g. when a scrolled_window_new() is used.
+ * - g_signal_connect(my_win, "destroy", my_destroy_cb, NULL) will create a callback if some cleanup
+ * needs to be done after the window is destroyed, e.g. free up memory, or set the window pointer
+ * of a singleton window (only one instance allowed, e.g. about dialog) back to zero
+ * - create and fill in the content and button widgets
+ * - gtk_widget_show_all() shows all the widgets in the window
+ * - window_present() will present the window on screen and
+ * (if available) set previously saved position and size
+ *
+ * @section window_events Events
+ *
+ * The following events are usually interesting:
+ *
+ * - "delete_event": the window manager's "X" (e.g. upper right edge) of the window
+ * was clicked; the default handler will call gtk_widget_destroy()
+ * - "destroy": everything is already gone; only cleanup of left over resources
+ * can/should be done now
+ *
+ * @section window_hints Hints
+ *
+ * If you want to save size and position, be sure to call window_destroy() instead of only
+ * gtk_widget_destroy(), so you will probably have to g_signal_connect() to the "delete_event"!
+ *
+ * Don't use gtk_widget_set_size_request() to set the size of a window;
+ * use gtk_window_set_default_size() for that purpose!
+ *
+ * Be sure to call window_present() / window_destroy() appropriately, if you
+ * want to have size and position of the window handled by ui_util.
+ *
+ */
+
+/** @file
+ * Utilities for Windows and other user interface functions. See: @ref howto_window_page for details.
+ * @ingroup dialog_group
+ * @ingroup windows_group
+ */
+
+/** @name Window Functions
+ * @todo Move these window functions to a new file win_utils.h?
+ * @{ */
+
+/** Create a new window with the Wireshark icon.
+ * If you want to create a dialog, use dlg_window_new() instead.
+ *
+ * @param type window type, typical GTK_WINDOW_TOPLEVEL
+ * @param title the title for the new window
+ * @return the newly created window
+ */
+extern GtkWidget *window_new(GtkWindowType type, const gchar *title);
+
+/** Same as window_new(), but will keep its geometry values (size, position, ...).
+ * Be sure to use window_present() and window_destroy() appropriately!
+ *
+ * @param type window type, typical GTK_WINDOW_TOPLEVEL
+ * @param title the title for the new window
+ * @param geom_name the name to distinguish this window; will also be used for the recent file (don't use special chars)
+ * @return the newly created window
+ */
+extern GtkWidget *window_new_with_geom(GtkWindowType type, const gchar *title, const gchar *geom_name);
+
+/** Create a new splash window, with no icon or title bar.
+ *
+ * @return the newly created window
+ */
+extern GtkWidget *splash_window_new(void);
+
+/** Present the created window on the top of the screen. This will put the window on top and
+ * (if available) set previously saved position and size.
+ *
+ * @param win the window from window_new()
+ */
+extern void window_present(GtkWidget *win);
+
+/** callback function for window_set_cancel_button() */
+typedef void (*window_cancel_button_fct) (GtkWidget *w, gpointer data);
+
+/** Register the default cancel button "Cancel"/"Close"/"Ok" of this window.
+ * This will set the callback function for this button, grab this button as the default one and
+ * set the "ESC" key handler to call the callback function if key is pressed.
+ *
+ * @param win the window from window_new()
+ * @param bt the default button of this window
+ * @param cb callback function to be called, when this button is pressed
+ */
+extern void window_set_cancel_button(GtkWidget *win, GtkWidget *bt, window_cancel_button_fct cb);
+
+/** Remember the current window position / size and then destroy the window.
+ * It's important to call this instead of gtk_widget_destroy() when using window_new_with_geom().
+ *
+ * @param win the window from window_new()
+ */
+extern void window_destroy(GtkWidget *win);
+
+/** Default callback handler for cancel button "clicked" signal.
+ * Use this for window_set_cancel_button(), if no user specific functionality required,
+ * will simply call window_destroy()
+ */
+extern void window_cancel_button_cb(GtkWidget *w _U_, gpointer data);
+
+/** Default callback handler if the window manager's X of the window was clicked (delete_event).
+ * Use this for g_signal_connect(), if no user specific functionality required,
+ * will simply call window_destroy()
+ */
+extern gboolean window_delete_event_cb(GtkWidget *win, GdkEvent *event _U_, gpointer user_data _U_);
+
+/** geometry values for use in window_get_geometry() and window_set_geometry() */
+typedef struct window_geometry_s {
+ gchar *key; /**< current key in hashtable (internally used only) */
+ gboolean set_pos; /**< set the x and y position values */
+ gint x; /**< the windows x position */
+ gint y; /**< the windows y position */
+ gboolean set_size; /**< set the width and height values */
+ gint width; /**< the windows width */
+ gint height; /**< the windows height */
+
+ gboolean set_maximized; /**< set the maximized state (GTK2 only) */
+ gboolean maximized; /**< the windows maximized state (GTK2 only) */
+} window_geometry_t;
+
+/** Get the geometry of a window.
+ *
+ * @param win the window from window_new()
+ * @param geom the current geometry values of the window; the set_xy values will not be used
+ * @todo if main uses the window_new_with_geom() to save size and such, make this function static
+ */
+extern void window_get_geometry(GtkWidget *win, window_geometry_t *geom);
+/** Set the geometry of a window.
+ *
+ * @param win the window from window_new()
+ * @param geom the new geometry values of the window
+ * @todo if main uses the window_new_with_geom() to save size and such, make this function static
+ */
+extern void window_set_geometry(GtkWidget *win, window_geometry_t *geom);
+
+/** Write all geometry values of all windows to the recent file.
+ * Will call write_recent_geom() for every existing window type.
+ *
+ * @param rf recent file handle from caller
+ */
+extern void window_geom_recent_write_all(gpointer rf);
+
+/** Read in a single geometry key value pair from the recent file.
+ *
+ * @param name the geom_name of the window
+ * @param key the subkey of this pair (e.g. "x")
+ * @param value the new value (e.g. "123")
+ */
+extern void window_geom_recent_read_pair(const char *name, const char *key, const char *value);
+
+/** Raise a top-level window and de-iconify it.
+ * This routine is used if the user has done something to
+ * ask that a window of a certain type be popped up when there can be only
+ * one such window and such a window has already been popped up - we
+ * pop up the existing one rather than creating a new one.
+ *
+ * @param win the window from window_new() to be reactivated
+ */
+extern void reactivate_window(GtkWidget *win);
+
+/** @} */
+
+/** Create a GtkScrolledWindow, set its scrollbar placement appropriately,
+ * and remember it.
+ *
+ * @param hadjustment horizontal adjustment
+ * @param vadjustment vertical adjustment
+ * @return the new scrolled window
+ */
+extern GtkWidget *scrolled_window_new(GtkAdjustment *hadjustment,
+ GtkAdjustment *vadjustment);
+
+/** Set the scrollbar placement of all scrolled windows based on user
+ preference. */
+extern void set_scrollbar_placement_all(void);
+
+/** Create a GtkTreeView, give it the right styles, and remember it.
+ *
+ * @param model The model (the data) of this tree view.
+ */
+extern GtkWidget *tree_view_new(GtkTreeModel *model);
+
+/** Move the currently-selected item in a list store up or down one position.
+ *
+ * @param tree GtkTreeView using a GtkListStore.
+ * @param move_up TRUE to move the selected item up or FALSE to move it down.
+ * @return TRUE if successful, FALSE otherwise.
+ */
+extern gboolean tree_view_list_store_move_selection(GtkTreeView *tree, gboolean move_up);
+
+/** Find the selected row in a list store.
+ *
+ * @param tree GtkTreeView using a GtkListStore.
+ * @return The selected row number or -1 if no row is selected.
+ */
+extern gint tree_view_list_store_get_selected_row(GtkTreeView *tree);
+
+/** Create a simple list widget.
+ *
+ * @param cols number of columns
+ * @param titles the titles of all columns
+ * @return the new simple list widget
+ */
+extern GtkWidget *simple_list_new(gint cols, const gchar **titles);
+/** Append a row to the simple list.
+ *
+ * @param list the list from simple_list_new()
+ * @param ... row and title, finished by -1 (e.g.: 0, "first", 1, "second", -1).
+ */
+extern void simple_list_append(GtkWidget *list, ...);
+
+/*** Make a column look like a url
+ *
+ * @param list the list from simple_list_new()
+ * @param col the column to make the values lookk like urls
+ */
+extern void simple_list_url_col(GtkWidget *list, gint col);
+
+/*** Make a cell underline to look like links
+ *
+ * @param cell the cell renderer that will show the text as a link
+ */
+
+extern void render_as_url(GtkCellRenderer *cell);
+
+/** Set the styles of all Trees based upon user preferences. */
+extern void set_tree_styles_all(void);
+
+/** Convert an xpm picture into a GtkWidget showing it.
+ * Beware: Wireshark's main window must already be visible!
+ *
+ * @param xpm the character array containing the picture
+ * @return a newly created GtkWidget showing the picture
+ */
+extern GtkWidget *xpm_to_widget(const char ** xpm);
+
+/** Convert an xpm picture into a GtkWidget showing it.
+ * Beware: the given parent window must already be visible!
+ *
+ * @param parent the parent window of to widget to be generated
+ * @param xpm the character array containing the picture
+ * @return a newly created GtkWidget showing the picture
+ */
+/*extern GtkWidget *xpm_to_widget_from_parent(GtkWidget *parent, const char ** xpm);*/
+
+/** Convert an pixbuf data to a GtkWidget
+ *
+ * @param pb_data Inline pixbuf data. This should be created with "gdk-pixbuf-csource --raw"
+ */
+extern GtkWidget *pixbuf_to_widget(const char * pb_data);
+
+/** Copy a GString to the clipboard.
+ *
+ * @param str GString that is to be copied to the clipboard.
+ */
+extern void copy_to_clipboard(GString *str);
+
+/** Copy an array of bytes to the clipboard.
+ * Copies as mime-type application/octet_stream in GTK 2.
+ *
+ * @param data_p Pointer to data to be copied.
+ * @param len Number of bytes in the data to be copied.
+ */
+extern void copy_binary_to_clipboard(const guint8* data_p, int len);
+
+/** Create a new window title that includes user-defined preference string.
+ *
+ * @param caption string you want included in title (appended to user-defined string)
+ * @return a newly created title string including user-defined preference (if specified)
+ */
+extern gchar *create_user_window_title(const gchar *caption);
+
+/** Construct the main window's title with the current main_window_name optionally appended
+ * with the user-specified title and/or wireshark version.
+ * Display the result in the main window's title bar and in its icon title
+ */
+extern void update_main_window_title(void);
+
+/** Renders a float with two decimals precission, called from gtk_tree_view_column_set_cell_data_func().
+ * the user data must be the colum number.
+ * Present floats with two decimals
+ *
+ * @param column A GtkTreeColumn
+ * @param renderer The GtkCellRenderer that is being rendered by tree_column
+ * @param model The GtkTreeModel being rendered
+ * @param iter A GtkTreeIter of the current row rendered
+ * @param user_data must be the colum number to fetch the data from
+ */
+void float_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
+
+/** Renders a unsinged integer as a hexadecimal value, called from gtk_tree_view_column_set_cell_data_func()
+ * The user data must be the colum number.
+ * Present value as hexadecimal.
+ * @param column A GtkTreeColumn
+ * @param renderer The GtkCellRenderer that is being rendered by tree_column
+ * @param model The GtkTreeModel being rendered
+ * @param iter A GtkTreeIter of the current row rendered
+ * @param user_data must be the colum number to fetch the data from
+ */
+void present_as_hex_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
+
+/** Renders an unsigned 64 bits integer with space as thousand separator, called from gtk_tree_view_column_set_cell_data_func()
+ * The user data must be the colum number.
+ * Present value as hexadecimal.
+ * @param column A GtkTreeColumn
+ * @param renderer The GtkCellRenderer that is being rendered by tree_column
+ * @param model The GtkTreeModel being rendered
+ * @param iter A GtkTreeIter of the current row rendered
+ * @param user_data must be the colum number to fetch the data from
+ */
+void u64_data_func (GtkTreeViewColumn *column, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
+
+/** This function can be called from gtk_tree_view_column_set_cell_data_func()
+ * the user data must be the colum number.
+ * Present value as hexadecimal.
+ * @param column A GtkTreeColumn
+ * @param renderer The GtkCellRenderer that is being rendered by tree_column
+ * @param model The GtkTreeModel being rendered
+ * @param iter A GtkTreeIter of the current row rendered
+ * @param user_data must be the colum number to fetch the data from
+ */
+void str_ptr_data_func(GtkTreeViewColumn *column, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
+
+/** This function can be called from gtk_tree_sortable_set_sort_func()
+ * the user data must be the colum number.
+ * Used together with str_ptr_data_func to sort the corresponding column.
+ * @param model The GtkTreeModel the comparison is within
+ * @param a A GtkTreeIter in model
+ * @param b Another GtkTreeIter in model
+ * @param user_data must be the colum number to fetch the data from
+ */
+
+gint str_ptr_sort_func(GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data);
+
+/** Switch a GtkTReeView to fixed columns (speed optimization)
+ * @param view A GtkTreeView
+ */
+void switch_to_fixed_col(GtkTreeView *view);
+
+/** Return the size in pixels of a string displayed with the GtkWidget's font.
+ * @param view A GtkWidget
+ * @param str UTF8 string
+ */
+gint get_default_col_size(GtkWidget *view, const gchar *str);
+
+
+/** --------------------------------------------------
+ * ws_combo_box_text_and_pointer convenience functions
+ * (Code adapted from GtkComboBox.c)
+ */
+
+/**
+ * ws_combo_box_new_text_and_pointer_full:
+ *
+ * Convenience function which constructs a new "text and pointer" combo box, which
+ * is a #GtkComboBox just displaying strings and storing a pointer associated with
+ * each combo_box entry; The pointer can be retrieved when an entry is selected.
+ * Also: optionally returns the cell renderer for the combo box.
+ * If you use this function to create a text_and_pointer combo_box,
+ * you should only manipulate its data source with the
+ * following convenience functions:
+ * ws_combo_box_append_text_and_pointer()
+ * ws_combo_box_append_text_and_pointer_full()
+ *
+ * @param cell_p pointer to return the 'GtkCellRenderer *' for the combo box (or NULL).
+ * @return A pointer to a new text_and_pointer combo_box.
+ */
+GtkWidget *ws_combo_box_new_text_and_pointer_full(GtkCellRenderer **cell_p);
+
+/**
+ * ws_combo_box_new_text_and_pointer:
+ *
+ * Convenience function which constructs a new "text and pointer" combo box, which
+ * is a #GtkComboBox just displaying strings and storing a pointer associated with
+ * each combo_box entry; The pointer can be retrieved when an entry is selected.
+ * If you use this function to create a text_and_pointer combo_box,
+ * you should only manipulate its data source with the
+ * following convenience functions:
+ * ws_combo_box_append_text_and_pointer()
+ * ws_combo_box_append_text_and_pointer_full()
+ *
+ * @return A pointer to a new text_and_pointer combo_box.
+ */
+GtkWidget *ws_combo_box_new_text_and_pointer(void);
+
+/**
+ * ws_combo_box_clear_text_and_pointer:
+ * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
+ *
+ * Clears all the text_and_pointer entries in the text_and_pointer combo_box.
+ * Note: A "changed" signal will be emitted after the clear if there was
+ * an active (selected) entry before the clear.
+ * You should use this function only with combo boxes constructed with
+ * ws_combo_box_new_text_and_pointer().
+ */
+void ws_combo_box_clear_text_and_pointer(GtkComboBox *combo_box);
+
+/**
+ * ws_combo_box_append_text_and_pointer_full:
+ * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
+ * @param parent_iter Parent row for apending; NULL if appending to tree top-level;
+ * @param text A string to be displayed as an entry in the dropdown list of the combo_box
+ * @param ptr A pointer to be associated with this entry of the combo_box
+ * @param sensitive TRUE/FALSE to set sensitivity of the entry
+ * @return A GtkTreeIter pointing to the appended GtkVomboBox entry.
+ *
+ * Appends text and ptr to the list of strings and pointers stored in combo_box.
+ * The text and ptr can be appended to any existing level of the tree_store.
+ * The sensitivity of the row will be set as requested.
+ * Note that you can only use this function with combo boxes constructed with
+ * ws_combo_box_new_text_and_pointer().
+ */
+GtkTreeIter
+ws_combo_box_append_text_and_pointer_full(GtkComboBox *combo_box,
+ GtkTreeIter *parent_iter,
+ const gchar *text,
+ const gpointer ptr,
+ const gboolean sensitive);
+
+/**
+ * ws_combo_box_append_text_and_pointer:
+ * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
+ * @param text A string to be displayed as an entry in the dropdown list of the combo_box
+ * @param ptr A pointer to be associated with this entry of the combo_box
+ * @return A GtkTreeIter pointing to the appended GtkComboBox entry.
+ *
+ * Appends text and ptr to the list of strings and pointers stored in combo_box. Note that
+ * you can only use this function with combo boxes constructed with
+ * ws_combo_box_new_text_and_pointer().
+ */
+GtkTreeIter
+ws_combo_box_append_text_and_pointer(GtkComboBox *combo_box,
+ const gchar *text,
+ const gpointer ptr);
+
+/**
+ * ws_combo_box_get_active_pointer:
+ * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
+ * @param ptr A pointer to a location in which to store the pointer associated with the active entry
+ * @return TRUE if an entry is selected (i.e: an active entry exists); FALSE otherwise
+ *
+ * You can only use this function with combo boxes constructed with
+ * ws_combo_box_new_text_and_pointer().
+ */
+gboolean ws_combo_box_get_active_pointer(GtkComboBox *combo_box, gpointer *ptr);
+
+/**
+ * ws_combo_box_get_active:
+ * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
+ * @return Index of the active entry; -1 if no entry is selected;
+ * Note: If the active item is not an immediate child of root of the tree then
+ * the index returned is that of the top-level for the acftive entry.
+ */
+gint ws_combo_box_get_active(GtkComboBox *combo_box);
+
+/**
+ * ws_combo_box_set_active:
+ * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
+ * @param idx Index of the entry which is to be set as active (ie: selected).
+ * Index refers to the immediate children of the tree.
+ */
+void ws_combo_box_set_active(GtkComboBox *combo_box, gint idx);
+
+/**
+ * ws_combo_box_set_active_iter:
+ * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
+ * @param iter of the entry which is to be set as active (ie: selected).
+ */
+void
+ws_combo_box_set_active_iter(GtkComboBox *combo_box, GtkTreeIter *iter);
+
+#if GTK_CHECK_VERSION(2,22,0)
+#if !GTK_CHECK_VERSION(3,0,0)
+GdkPixbuf *gdk_pixbuf_get_from_surface (cairo_surface_t *surface,
+ gint src_x,
+ gint src_y,
+ gint width,
+ gint height);
+#endif
+#endif
+#endif /* __GUI_UTIL__H__ */
diff --git a/ui/gtk/h225_counter.c b/ui/gtk/h225_counter.c
new file mode 100644
index 0000000000..bd988947de
--- /dev/null
+++ b/ui/gtk/h225_counter.c
@@ -0,0 +1,583 @@
+/* h225_counter.c
+ * H.225 message counter for Wireshark
+ * Copyright 2003 Lars Roland
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/value_string.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-h225.h>
+
+#include "../simple_dialog.h"
+#include "../file.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_util.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/main.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+static void gtk_h225counter_init(const char *optarg, void *userdata);
+
+static tap_param h225_counter_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg h225_counter_dlg = {
+ "H.225 Messages and Message Reasons",
+ "h225,counter",
+ gtk_h225counter_init,
+ -1,
+ G_N_ELEMENTS(h225_counter_params),
+ h225_counter_params
+};
+
+/* following values represent the size of their valuestring arrays */
+
+#define RAS_MSG_TYPES 33
+#define CS_MSG_TYPES 13
+
+#define GRJ_REASONS 8
+#define RRJ_REASONS 18
+#define URQ_REASONS 6
+#define URJ_REASONS 6
+#define ARJ_REASONS 22
+#define BRJ_REASONS 8
+#define DRQ_REASONS 3
+#define DRJ_REASONS 4
+#define LRJ_REASONS 16
+#define IRQNAK_REASONS 4
+#define REL_CMP_REASONS 26
+#define FACILITY_REASONS 11
+
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _h225counter_t {
+ GtkWidget *win;
+ GtkWidget *vbox;
+ char *filter;
+ GtkWidget *scrolled_window;
+ GtkTreeView *table;
+ guint32 ras_msg[RAS_MSG_TYPES + 1];
+ guint32 cs_msg[CS_MSG_TYPES + 1];
+ guint32 grj_reason[GRJ_REASONS + 1];
+ guint32 rrj_reason[RRJ_REASONS + 1];
+ guint32 urq_reason[URQ_REASONS + 1];
+ guint32 urj_reason[URJ_REASONS + 1];
+ guint32 arj_reason[ARJ_REASONS + 1];
+ guint32 brj_reason[BRJ_REASONS + 1];
+ guint32 drq_reason[DRQ_REASONS + 1];
+ guint32 drj_reason[DRJ_REASONS + 1];
+ guint32 lrj_reason[LRJ_REASONS + 1];
+ guint32 irqnak_reason[IRQNAK_REASONS + 1];
+ guint32 rel_cmp_reason[REL_CMP_REASONS + 1];
+ guint32 facility_reason[FACILITY_REASONS + 1];
+} h225counter_t;
+
+
+static void
+h225counter_reset(void *phs)
+{
+ h225counter_t *hs=(h225counter_t *)phs;
+ int i;
+
+ for(i=0;i<=RAS_MSG_TYPES;i++) {
+ hs->ras_msg[i] = 0;
+ }
+ for(i=0;i<=CS_MSG_TYPES;i++) {
+ hs->cs_msg[i] = 0;
+ }
+ for(i=0;i<=GRJ_REASONS;i++) {
+ hs->grj_reason[i] = 0;
+ }
+ for(i=0;i<=RRJ_REASONS;i++) {
+ hs->rrj_reason[i] = 0;
+ }
+ for(i=0;i<=URQ_REASONS;i++) {
+ hs->urq_reason[i] = 0;
+ }
+ for(i=0;i<=URJ_REASONS;i++) {
+ hs->urj_reason[i] = 0;
+ }
+ for(i=0;i<=ARJ_REASONS;i++) {
+ hs->arj_reason[i] = 0;
+ }
+ for(i=0;i<=BRJ_REASONS;i++) {
+ hs->brj_reason[i] = 0;
+ }
+ for(i=0;i<=DRQ_REASONS;i++) {
+ hs->drq_reason[i] = 0;
+ }
+ for(i=0;i<=DRJ_REASONS;i++) {
+ hs->drj_reason[i] = 0;
+ }
+ for(i=0;i<=LRJ_REASONS;i++) {
+ hs->lrj_reason[i] = 0;
+ }
+ for(i=0;i<=IRQNAK_REASONS;i++) {
+ hs->irqnak_reason[i] = 0;
+ }
+ for(i=0;i<=REL_CMP_REASONS;i++) {
+ hs->rel_cmp_reason[i] = 0;
+ }
+ for(i=0;i<=FACILITY_REASONS;i++) {
+ hs->facility_reason[i] = 0;
+ }
+}
+
+static int
+h225counter_packet(void *phs, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *phi)
+{
+ h225counter_t *hs=(h225counter_t *)phs;
+ const h225_packet_info *pi=phi;
+
+ switch (pi->msg_type) {
+
+ case H225_RAS:
+ if(pi->msg_tag==-1) { /* uninitialized */
+ return 0;
+ }
+ else if (pi->msg_tag >= RAS_MSG_TYPES) { /* unknown */
+ hs->ras_msg[RAS_MSG_TYPES]++;
+ }
+ else {
+ hs->ras_msg[pi->msg_tag]++;
+ }
+
+ /* Look for reason tag */
+ if(pi->reason==-1) { /* uninitialized */
+ break;
+ }
+
+ switch(pi->msg_tag) {
+
+ case 2: /* GRJ */
+ if(pi->reason < GRJ_REASONS)
+ hs->grj_reason[pi->reason]++;
+ else
+ hs->grj_reason[GRJ_REASONS]++;
+ break;
+ case 5: /* RRJ */
+ if(pi->reason < RRJ_REASONS)
+ hs->rrj_reason[pi->reason]++;
+ else
+ hs->rrj_reason[RRJ_REASONS]++;
+ break;
+ case 6: /* URQ */
+ if(pi->reason < URQ_REASONS)
+ hs->urq_reason[pi->reason]++;
+ else
+ hs->urq_reason[URQ_REASONS]++;
+ break;
+ case 8: /* URJ */
+ if(pi->reason < URJ_REASONS)
+ hs->urj_reason[pi->reason]++;
+ else
+ hs->urj_reason[URJ_REASONS]++;
+ break;
+ case 11: /* ARJ */
+ if(pi->reason < ARJ_REASONS)
+ hs->arj_reason[pi->reason]++;
+ else
+ hs->arj_reason[ARJ_REASONS]++;
+ break;
+ case 14: /* BRJ */
+ if(pi->reason < BRJ_REASONS)
+ hs->brj_reason[pi->reason]++;
+ else
+ hs->brj_reason[BRJ_REASONS]++;
+ break;
+ case 15: /* DRQ */
+ if(pi->reason < DRQ_REASONS)
+ hs->drq_reason[pi->reason]++;
+ else
+ hs->drq_reason[DRQ_REASONS]++;
+ break;
+ case 17: /* DRJ */
+ if(pi->reason < DRJ_REASONS)
+ hs->drj_reason[pi->reason]++;
+ else
+ hs->drj_reason[DRJ_REASONS]++;
+ break;
+ case 20: /* LRJ */
+ if(pi->reason < LRJ_REASONS)
+ hs->lrj_reason[pi->reason]++;
+ else
+ hs->lrj_reason[LRJ_REASONS]++;
+ break;
+ case 29: /* IRQ Nak */
+ if(pi->reason < IRQNAK_REASONS)
+ hs->irqnak_reason[pi->reason]++;
+ else
+ hs->irqnak_reason[IRQNAK_REASONS]++;
+ break;
+
+ default:
+ /* do nothing */
+ break;
+ }
+
+ break;
+
+ case H225_CS:
+ if(pi->msg_tag==-1) { /* uninitialized */
+ return 0;
+ }
+ else if (pi->msg_tag >= CS_MSG_TYPES) { /* unknown */
+ hs->cs_msg[CS_MSG_TYPES]++;
+ }
+ else {
+ hs->cs_msg[pi->msg_tag]++;
+ }
+
+ /* Look for reason tag */
+ if(pi->reason==-1) { /* uninitialized */
+ break;
+ }
+
+ switch(pi->msg_tag) {
+
+ case 5: /* ReleaseComplete */
+ if(pi->reason < REL_CMP_REASONS)
+ hs->rel_cmp_reason[pi->reason]++;
+ else
+ hs->rel_cmp_reason[REL_CMP_REASONS]++;
+ break;
+ case 6: /* Facility */
+ if(pi->reason < FACILITY_REASONS)
+ hs->facility_reason[pi->reason]++;
+ else
+ hs->facility_reason[FACILITY_REASONS]++;
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+h225counter_draw(void *phs)
+{
+ h225counter_t *hs=(h225counter_t *)phs;
+ int i,j;
+ char str[256];
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ /* Now print Message and Reason Counter Table */
+ /* clear list before printing */
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(hs->table));
+ gtk_list_store_clear(store);
+
+ for(i=0;i<=RAS_MSG_TYPES;i++) {
+ if(hs->ras_msg[i]!=0) {
+ g_snprintf(str, 256, "%s", val_to_str(i,h225_RasMessage_vals,"unknown ras-messages"));
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, val_to_str(i,h225_RasMessage_vals,"unknown ras-messages"),
+ 1, hs->ras_msg[i], -1);
+
+ /* reason counter */
+ switch(i) {
+ case 2: /* GRJ */
+ for(j=0;j<=GRJ_REASONS;j++) {
+ if(hs->grj_reason[j]!=0) {
+ g_snprintf(str, 256," %s",
+ val_to_str(j,GatekeeperRejectReason_vals,"unknown reason"));
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, str,
+ 1, hs->grj_reason[j], -1);
+ }
+ }
+ break;
+ case 5: /* RRJ */
+ for(j=0;j<=RRJ_REASONS;j++) {
+ if(hs->rrj_reason[j]!=0) {
+ g_snprintf(str, 256," %s",
+ val_to_str(j,RegistrationRejectReason_vals,"unknown reason"));
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, str,
+ 1, hs->rrj_reason[j], -1);
+ }
+ }
+ break;
+ case 6: /* URQ */
+ for(j=0;j<=URQ_REASONS;j++) {
+ if(hs->urq_reason[j]!=0) {
+ g_snprintf(str, 256," %s",
+ val_to_str(j,UnregRequestReason_vals,"unknown reason"));
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, str,
+ 1, hs->urq_reason[j], -1);
+ }
+ }
+ break;
+ case 8: /* URJ */
+ for(j=0;j<=URJ_REASONS;j++) {
+ if(hs->urj_reason[j]!=0) {
+ g_snprintf(str, 256," %s",
+ val_to_str(j,UnregRejectReason_vals,"unknown reason"));
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, str,
+ 1, hs->urj_reason[j], -1);
+ }
+ }
+ break;
+ case 11: /* ARJ */
+ for(j=0;j<=ARJ_REASONS;j++) {
+ if(hs->arj_reason[j]!=0) {
+ g_snprintf(str, 256," %s",
+ val_to_str(j,AdmissionRejectReason_vals,"unknown reason"));
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, str,
+ 1, hs->arj_reason[j], -1);
+ }
+ }
+ break;
+ case 14: /* BRJ */
+ for(j=0;j<=BRJ_REASONS;j++) {
+ if(hs->brj_reason[j]!=0) {
+ g_snprintf(str, 256," %s",
+ val_to_str(j,BandRejectReason_vals,"unknown reason"));
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, str,
+ 1, hs->brj_reason[j], -1);
+ }
+ }
+ break;
+ case 15: /* DRQ */
+ for(j=0;j<=DRQ_REASONS;j++) {
+ if(hs->drq_reason[j]!=0) {
+ g_snprintf(str, 256," %s",
+ val_to_str(j,DisengageReason_vals,"unknown reason"));
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, str,
+ 1, hs->drq_reason[j], -1);
+ }
+ }
+ break;
+ case 17: /* DRJ */
+ for(j=0;j<=DRJ_REASONS;j++) {
+ if(hs->drj_reason[j]!=0) {
+ g_snprintf(str, 256," %s",
+ val_to_str(j,DisengageRejectReason_vals,"unknown reason"));
+ gtk_list_store_set(store, &iter,
+ 0, str,
+ 1, hs->drj_reason[j], -1);
+ }
+ }
+ break;
+ case 20: /* LRJ */
+ for(j=0;j<=LRJ_REASONS;j++) {
+ if(hs->lrj_reason[j]!=0) {
+ g_snprintf(str, 256," %s",
+ val_to_str(j,LocationRejectReason_vals,"unknown reason"));
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, str,
+ 1, hs->lrj_reason[j], -1);
+ }
+ }
+ break;
+ case 29: /* IRQNak */
+ for(j=0;j<=IRQNAK_REASONS;j++) {
+ if(hs->irqnak_reason[j]!=0) {
+ g_snprintf(str, 256," %s",
+ val_to_str(j,InfoRequestNakReason_vals,"unknown reason"));
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, str,
+ 1, hs->irqnak_reason[j], -1);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ /* end of reason counter*/
+ }
+ }
+
+ for(i=0;i<=CS_MSG_TYPES;i++) {
+ if(hs->cs_msg[i]!=0) {
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, val_to_str(i,T_h323_message_body_vals,"unknown cs-messages"),
+ 1, hs->cs_msg[i], -1);
+
+ /* reason counter */
+ switch(i) {
+ case 5: /* ReleaseComplete */
+ for(j=0;j<=REL_CMP_REASONS;j++) {
+ if(hs->rel_cmp_reason[j]!=0) {
+ g_snprintf(str, 256," %s",
+ val_to_str(j,h225_ReleaseCompleteReason_vals,"unknown reason"));
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, str,
+ 1, hs->rel_cmp_reason[j], -1);
+ }
+ }
+ break;
+ case 6: /* Facility */
+ for(j=0;j<=FACILITY_REASONS;j++) {
+ if(hs->facility_reason[j]!=0) {
+ g_snprintf(str, 256," %s",
+ val_to_str(j,FacilityReason_vals,"unknown reason"));
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, str,
+ 1, hs->facility_reason[j], -1);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ h225counter_t *hs=(h225counter_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(hs);
+ unprotect_thread_critical_region();
+
+ if(hs->filter){
+ g_free(hs->filter);
+ hs->filter=NULL;
+ }
+ g_free(hs);
+}
+
+static const stat_column titles[]={
+ {G_TYPE_STRING, LEFT, "Message Type or Reason"},
+ {G_TYPE_UINT, RIGHT, "Count" }
+};
+
+static void
+gtk_h225counter_init(const char *optarg, void *userdata _U_)
+{
+ h225counter_t *hs;
+ GString *error_string;
+ GtkWidget *bbox;
+ GtkWidget *close_bt;
+
+ hs=g_malloc(sizeof(h225counter_t));
+
+ if(strncmp(optarg,"h225,counter,",13) == 0){
+ hs->filter=g_strdup(optarg+13);
+ } else {
+ hs->filter=NULL;
+ }
+
+ h225counter_reset(hs);
+
+ hs->win = dlg_window_new("Wireshark: H.225 counters"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(hs->win), TRUE);
+
+ gtk_window_set_default_size(GTK_WINDOW(hs->win), 400, 200);
+
+ hs->vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(hs->vbox), 12);
+
+ init_main_stat_window(hs->win, hs->vbox, "H.225 Message and Message Reason Counter", hs->filter);
+
+ /* init a scrolled window*/
+ hs->scrolled_window = scrolled_window_new(NULL, NULL);
+
+ hs->table = create_stat_table(hs->scrolled_window, hs->vbox, 2, titles);
+
+ error_string=register_tap_listener("h225", hs, hs->filter, 0, h225counter_reset, h225counter_packet, h225counter_draw);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(hs->filter);
+ g_free(hs);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_end(GTK_BOX(hs->vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(hs->win, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(hs->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(hs->win, "destroy", G_CALLBACK(win_destroy_cb), hs);
+
+ gtk_widget_show_all(hs->win);
+ window_present(hs->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(hs->win));
+}
+
+void
+register_tap_listener_gtk_h225counter(void)
+{
+ register_dfilter_stat(&h225_counter_dlg, "_H.225",
+ REGISTER_STAT_GROUP_TELEPHONY);
+}
+
+void h225_counter_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &h225_counter_dlg);
+}
diff --git a/ui/gtk/h225_ras_srt.c b/ui/gtk/h225_ras_srt.c
new file mode 100644
index 0000000000..2f2974ee6e
--- /dev/null
+++ b/ui/gtk/h225_ras_srt.c
@@ -0,0 +1,351 @@
+/* h225_ras_srt.c
+ * H.225 RAS Service Response Time statistics for Wireshar
+ * Copyright 2003 Lars Roland
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/value_string.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-h225.h>
+
+#include "../timestats.h"
+#include "../simple_dialog.h"
+#include "../file.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_util.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/main.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+static void gtk_h225rassrt_init(const char *optarg, void *userdata);
+
+static tap_param h225_rassrt_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg h225_rassrt_dlg = {
+ "H.225 RAS Service Response Time",
+ "h225,srt",
+ gtk_h225rassrt_init,
+ -1,
+ G_N_ELEMENTS(h225_rassrt_params),
+ h225_rassrt_params
+};
+
+/* following values represent the size of their valuestring arrays */
+#define NUM_RAS_STATS 7
+
+static const value_string ras_message_category[] = {
+ { 0, "Gatekeeper "},
+ { 1, "Registration "},
+ { 2, "UnRegistration"},
+ { 3, "Admission "},
+ { 4, "Bandwidth "},
+ { 5, "Disengage "},
+ { 6, "Location "},
+ { 0, NULL }
+};
+
+typedef enum _ras_type {
+ RAS_REQUEST,
+ RAS_CONFIRM,
+ RAS_REJECT,
+ RAS_OTHER
+}ras_type;
+
+typedef enum _ras_category {
+ RAS_GATEKEEPER,
+ RAS_REGISTRATION,
+ RAS_UNREGISTRATION,
+ RAS_ADMISSION,
+ RAS_BANDWIDTH,
+ RAS_DISENGAGE,
+ RAS_LOCATION,
+ RAS_OTHERS
+}ras_category;
+
+/* Summary of response-time calculations*/
+typedef struct _h225_rtd_t {
+ guint32 open_req_num;
+ guint32 disc_rsp_num;
+ guint32 req_dup_num;
+ guint32 rsp_dup_num;
+ timestat_t stats;
+} h225_rtd_t;
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _h225rassrt_t {
+ GtkWidget *win;
+ GtkWidget *vbox;
+ char *filter;
+ GtkWidget *scrolled_window;
+ GtkTreeView *table;
+ h225_rtd_t ras_rtd[NUM_RAS_STATS];
+} h225rassrt_t;
+
+
+static void
+h225rassrt_reset(void *phs)
+{
+ h225rassrt_t *hs=(h225rassrt_t *)phs;
+ int i;
+
+ for(i=0;i<NUM_RAS_STATS;i++) {
+ hs->ras_rtd[i].stats.num = 0;
+ hs->ras_rtd[i].stats.min_num = 0;
+ hs->ras_rtd[i].stats.max_num = 0;
+ hs->ras_rtd[i].stats.min.secs = 0;
+ hs->ras_rtd[i].stats.min.nsecs = 0;
+ hs->ras_rtd[i].stats.max.secs = 0;
+ hs->ras_rtd[i].stats.max.nsecs = 0;
+ hs->ras_rtd[i].stats.tot.secs = 0;
+ hs->ras_rtd[i].stats.tot.nsecs = 0;
+ hs->ras_rtd[i].open_req_num = 0;
+ hs->ras_rtd[i].disc_rsp_num = 0;
+ hs->ras_rtd[i].req_dup_num = 0;
+ hs->ras_rtd[i].rsp_dup_num = 0;
+ }
+
+}
+
+
+static int
+h225rassrt_packet(void *phs, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *phi)
+{
+ h225rassrt_t *hs=(h225rassrt_t *)phs;
+ const h225_packet_info *pi=phi;
+
+ ras_type rasmsg_type = RAS_OTHER;
+ ras_category rascategory = RAS_OTHERS;
+
+ if (pi->msg_type != H225_RAS || pi->msg_tag == -1) {
+ /* No RAS Message or uninitialized msg_tag -> return */
+ return 0;
+ }
+
+ if (pi->msg_tag < 21) {
+ /* */
+ rascategory = pi->msg_tag / 3;
+ rasmsg_type = pi->msg_tag % 3;
+ }
+ else {
+ /* No SRT yet (ToDo) */
+ return 0;
+ }
+
+ switch(rasmsg_type) {
+
+ case RAS_REQUEST:
+ if(pi->is_duplicate){
+ hs->ras_rtd[rascategory].req_dup_num++;
+ }
+ else {
+ hs->ras_rtd[rascategory].open_req_num++;
+ }
+ break;
+
+ case RAS_CONFIRM:
+ /* no break - delay stats are identical for Confirm and Reject */
+ case RAS_REJECT:
+ if(pi->is_duplicate){
+ /* Duplicate is ignored */
+ hs->ras_rtd[rascategory].rsp_dup_num++;
+ }
+ else if (!pi->request_available) {
+ /* no request was seen, ignore response */
+ hs->ras_rtd[rascategory].disc_rsp_num++;
+ }
+ else {
+ hs->ras_rtd[rascategory].open_req_num--;
+ time_stat_update(&(hs->ras_rtd[rascategory].stats),&(pi->delta_time), pinfo);
+ }
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+static void
+h225rassrt_draw(void *phs)
+{
+ h225rassrt_t *hs=(h225rassrt_t *)phs;
+ int i;
+ char str[3][256];
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ /* Now print Message and Reason Counter Table */
+ /* clear list before printing */
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(hs->table));
+ gtk_list_store_clear(store);
+
+ for(i=0;i<NUM_RAS_STATS;i++) {
+ /* nothing seen, nothing to do */
+ if(hs->ras_rtd[i].stats.num==0){
+ continue;
+ }
+ g_snprintf(str[0], sizeof(char[256]),
+ "%8.2f msec", nstime_to_msec(&(hs->ras_rtd[i].stats.min)));
+ g_snprintf(str[1], sizeof(char[256]),
+ "%8.2f msec", nstime_to_msec(&(hs->ras_rtd[i].stats.max)));
+ g_snprintf(str[2], sizeof(char[256]),
+ "%8.2f msec", get_average(&(hs->ras_rtd[i].stats.tot), hs->ras_rtd[i].stats.num));
+
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, val_to_str(i,ras_message_category,"Other"),
+ 1, hs->ras_rtd[i].stats.num,
+ 2, str[0],
+ 3, str[1],
+ 4, str[2],
+ 5, hs->ras_rtd[i].stats.min_num,
+ 6, hs->ras_rtd[i].stats.max_num,
+ 7, hs->ras_rtd[i].open_req_num,
+ 8, hs->ras_rtd[i].disc_rsp_num,
+ 9, hs->ras_rtd[i].req_dup_num,
+ 10, hs->ras_rtd[i].rsp_dup_num,
+ -1);
+ }
+}
+
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ h225rassrt_t *hs=(h225rassrt_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(hs);
+ unprotect_thread_critical_region();
+
+ if(hs->filter){
+ g_free(hs->filter);
+ hs->filter=NULL;
+ }
+ g_free(hs);
+}
+
+
+static const stat_column titles[]={
+ {G_TYPE_STRING, LEFT, "RAS-Type" },
+ {G_TYPE_UINT, RIGHT, "Measurements" },
+ {G_TYPE_STRING, RIGHT, "Min RTT" },
+ {G_TYPE_STRING, RIGHT, "Max RTT" },
+ {G_TYPE_STRING, RIGHT, "Avg RTT" },
+ {G_TYPE_UINT, RIGHT, "Min in Frame" },
+ {G_TYPE_UINT, RIGHT, "Max in Frame" },
+ {G_TYPE_UINT, RIGHT, "Open Requests" },
+ {G_TYPE_UINT, RIGHT, "Discarded Responses" },
+ {G_TYPE_UINT, RIGHT, "Repeated Requests" },
+ {G_TYPE_UINT, RIGHT, "Repeated Responses"}
+};
+
+static void
+gtk_h225rassrt_init(const char *optarg, void *userdata _U_)
+{
+ h225rassrt_t *hs;
+ GString *error_string;
+ GtkWidget *bbox;
+ GtkWidget *close_bt;
+
+ hs=g_malloc(sizeof(h225rassrt_t));
+
+ if(strncmp(optarg,"h225,srt,",9) == 0){
+ hs->filter=g_strdup(optarg+9);
+ } else {
+ hs->filter=NULL;
+ }
+
+ h225rassrt_reset(hs);
+
+ hs->win = dlg_window_new("h225-ras-srt"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(hs->win), TRUE);
+ gtk_window_set_default_size(GTK_WINDOW(hs->win), 600, 300);
+
+ hs->vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(hs->vbox), 12);
+
+ init_main_stat_window(hs->win, hs->vbox, "H.225 RAS Service Response Time", hs->filter);
+
+ /* init a scrolled window*/
+ hs->scrolled_window = scrolled_window_new(NULL, NULL);
+
+ hs->table = create_stat_table(hs->scrolled_window, hs->vbox, 11, titles);
+
+ error_string=register_tap_listener("h225", hs, hs->filter, 0, h225rassrt_reset, h225rassrt_packet, h225rassrt_draw);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(hs->filter);
+ g_free(hs);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_end(GTK_BOX(hs->vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(hs->win, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(hs->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(hs->win, "destroy", G_CALLBACK(win_destroy_cb), hs);
+
+ gtk_widget_show_all(hs->win);
+ window_present(hs->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(hs->win));
+}
+
+void
+register_tap_listener_gtk_h225rassrt(void)
+{
+ register_dfilter_stat(&h225_rassrt_dlg, "H.225 RAS",
+ REGISTER_STAT_GROUP_RESPONSE_TIME);
+}
+
+void h225_srt_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &h225_rassrt_dlg);
+}
+
diff --git a/ui/gtk/help_dlg.c b/ui/gtk/help_dlg.c
new file mode 100644
index 0000000000..43f647412b
--- /dev/null
+++ b/ui/gtk/help_dlg.c
@@ -0,0 +1,375 @@
+/* help_dlg.c
+ *
+ * $Id$
+ *
+ * Laurent Deniel <laurent.deniel@free.fr>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2000 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/filesystem.h"
+#include <epan/prefs.h>
+
+#include "../simple_dialog.h"
+
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/text_page_utils.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/webbrowser.h"
+
+#ifdef HHC_DIR
+#include <windows.h>
+#include <htmlhelp.h>
+#include <wsutil/unicode-utils.h>
+#endif
+
+
+#define HELP_DIR "help"
+
+
+#define NOTEBOOK_KEY "notebook_key"
+
+/*
+ * Keep a static pointer to the current "Help" window, if any, so that
+ * if somebody tries to do "Help->Help" while there's already a
+ * "Help" window up, we just pop up the existing one, rather than
+ * creating a new one.
+*/
+static GtkWidget *help_w = NULL;
+
+/*
+ * Keep a list of text widgets and corresponding file names as well
+ * (for text format changes).
+ */
+typedef struct {
+ char *topic;
+ char *pathname;
+ GtkWidget *page;
+} help_page_t;
+
+static GSList *help_text_pages = NULL;
+
+
+/*
+ * Open the help dialog and show a specific HTML help page.
+ */
+void help_topic_html(const gchar *topic) {
+ GString *url;
+
+ /* try to open local .chm file */
+#ifdef HHC_DIR
+ HWND hw;
+
+ url = g_string_new("");
+
+ g_string_append_printf(url, "%s\\user-guide.chm::/wsug_chm/%s>Wireshark Help",
+ get_datafile_dir(), topic);
+
+ hw = HtmlHelpW(NULL,
+ utf_8to16(url->str),
+ HH_DISPLAY_TOPIC, 0);
+
+ g_string_free(url, TRUE /* free_segment */);
+
+ /* if the .chm file could be opened, stop here */
+ if(hw != NULL) {
+ return;
+ }
+#endif /* HHC_DIR */
+
+ url = g_string_new("");
+
+#ifdef DOC_DIR
+ if (g_file_test(DOC_DIR "/guides/wsug_html_chunked", G_FILE_TEST_IS_DIR)) {
+ /* try to open the HTML page from wireshark.org instead */
+ g_string_append_printf(url, "file://" DOC_DIR "/guides/wsug_html_chunked/%s", topic);
+ } else {
+#endif /* ifdef DOC_DIR */
+ /* try to open the HTML page from wireshark.org instead */
+ g_string_append_printf(url, "http://www.wireshark.org/docs/wsug_html_chunked/%s", topic);
+#ifdef DOC_DIR
+ }
+#endif /* ifdef DOC_DIR */
+
+ browser_open_url(url->str);
+
+ g_string_free(url, TRUE /* free_segment */);
+}
+
+
+/**
+ * Redraw all help pages, to use a new font.
+ */
+void help_redraw(void)
+{
+ GSList *help_page_ent;
+ help_page_t *help_page;
+
+ if (help_w != NULL) {
+ for (help_page_ent = help_text_pages; help_page_ent != NULL;
+ help_page_ent = g_slist_next(help_page_ent))
+ {
+ help_page = (help_page_t *)help_page_ent->data;
+ text_page_redraw(help_page->page, help_page->pathname);
+ }
+ }
+}
+
+
+const char *
+topic_online_url(topic_action_e action)
+{
+ switch(action) {
+ case(ONLINEPAGE_HOME):
+ return "http://www.wireshark.org";
+ break;
+ case(ONLINEPAGE_WIKI):
+ return "http://wiki.wireshark.org";
+ break;
+ case(ONLINEPAGE_DOWNLOAD):
+ return "http://www.wireshark.org/download.html";
+ break;
+ case(ONLINEPAGE_USERGUIDE):
+ return "http://www.wireshark.org/docs/wsug_html_chunked/";
+ break;
+ case(ONLINEPAGE_FAQ):
+ return "http://www.wireshark.org/faq.html";
+ break;
+ case(ONLINEPAGE_SAMPLE_FILES):
+ return "http://wiki.wireshark.org/SampleCaptures";
+ break;
+ case(ONLINEPAGE_CAPTURE_SETUP):
+ return "http://wiki.wireshark.org/CaptureSetup";
+ break;
+ case(ONLINEPAGE_NETWORK_MEDIA):
+ return "http://wiki.wireshark.org/CaptureSetup/NetworkMedia";
+ break;
+ case(ONLINEPAGE_SAMPLE_CAPTURES):
+ return "http://wiki.wireshark.org/SampleCaptures";
+ break;
+ case(ONLINEPAGE_SECURITY):
+ return "http://wiki.wireshark.org/Security";
+ break;
+ case(ONLINEPAGE_CHIMNEY):
+ return "http://wiki.wireshark.org/CaptureSetup/Offloading#chimney";
+ break;
+ default:
+ return NULL;
+ }
+}
+
+
+static void
+topic_action(topic_action_e action)
+{
+ const char *online_url;
+
+
+ /* pages online at www.wireshark.org */
+ online_url = topic_online_url(action);
+ if(online_url != NULL) {
+ browser_open_url (online_url);
+ return;
+ }
+
+ switch(action) {
+ /* local manual pages */
+ case(LOCALPAGE_MAN_WIRESHARK):
+ browser_open_data_file("wireshark.html");
+ break;
+ case(LOCALPAGE_MAN_WIRESHARK_FILTER):
+ browser_open_data_file("wireshark-filter.html");
+ break;
+ case(LOCALPAGE_MAN_TSHARK):
+ browser_open_data_file("tshark.html");
+ break;
+ case(LOCALPAGE_MAN_RAWSHARK):
+ browser_open_data_file("rawshark.html");
+ break;
+ case(LOCALPAGE_MAN_DUMPCAP):
+ browser_open_data_file("dumpcap.html");
+ break;
+ case(LOCALPAGE_MAN_MERGECAP):
+ browser_open_data_file("mergecap.html");
+ break;
+ case(LOCALPAGE_MAN_EDITCAP):
+ browser_open_data_file("editcap.html");
+ break;
+ case(LOCALPAGE_MAN_TEXT2PCAP):
+ browser_open_data_file("text2pcap.html");
+ break;
+
+ /* local help pages (User's Guide) */
+ case(HELP_CONTENT):
+ help_topic_html( "index.html");
+ break;
+ case(HELP_CAPTURE_OPTIONS_DIALOG):
+ help_topic_html("ChCapCaptureOptions.html");
+ break;
+ case(HELP_CAPTURE_FILTERS_DIALOG):
+ help_topic_html("ChWorkDefineFilterSection.html");
+ break;
+ case(HELP_DISPLAY_FILTERS_DIALOG):
+ help_topic_html("ChWorkDefineFilterSection.html");
+ break;
+ case(HELP_COLORING_RULES_DIALOG):
+ help_topic_html("ChCustColorizationSection.html");
+ break;
+ case(HELP_CONFIG_PROFILES_DIALOG):
+ help_topic_html("ChCustConfigProfilesSection.html");
+ break;
+ case (HELP_MANUAL_ADDR_RESOLVE_DIALOG):
+ help_topic_html("ChManualAddressResolveSection.html");
+ break;
+ case(HELP_PRINT_DIALOG):
+ help_topic_html("ChIOPrintSection.html");
+ break;
+ case(HELP_FIND_DIALOG):
+ help_topic_html("ChWorkFindPacketSection.html");
+ break;
+ case(HELP_FIREWALL_DIALOG):
+ help_topic_html("ChUseToolsMenuSection.html");
+ break;
+ case(HELP_GOTO_DIALOG):
+ help_topic_html("ChWorkGoToPacketSection.html");
+ break;
+ case(HELP_CAPTURE_INTERFACES_DIALOG):
+ help_topic_html("ChCapInterfaceSection.html");
+ break;
+ case(HELP_CAPTURE_INFO_DIALOG):
+ help_topic_html("ChCapRunningSection.html");
+ break;
+ case(HELP_ENABLED_PROTOCOLS_DIALOG):
+ help_topic_html("ChCustProtocolDissectionSection.html");
+ break;
+ case(HELP_DECODE_AS_DIALOG):
+ help_topic_html("ChCustProtocolDissectionSection.html");
+ break;
+ case(HELP_DECODE_AS_SHOW_DIALOG):
+ help_topic_html("ChCustProtocolDissectionSection.html");
+ break;
+ case(HELP_FOLLOW_STREAM_DIALOG):
+ help_topic_html("ChAdvFollowTCPSection.html");
+ break;
+ case(HELP_EXPERT_INFO_DIALOG):
+ help_topic_html("ChAdvExpert.html");
+ break;
+ case(HELP_STATS_SUMMARY_DIALOG):
+ help_topic_html("ChStatSummary.html");
+ break;
+ case(HELP_STATS_PROTO_HIERARCHY_DIALOG):
+ help_topic_html("ChStatHierarchy.html");
+ break;
+ case(HELP_STATS_ENDPOINTS_DIALOG):
+ help_topic_html("ChStatEndpoints.html");
+ break;
+ case(HELP_STATS_CONVERSATIONS_DIALOG):
+ help_topic_html("ChStatConversations.html");
+ break;
+ case(HELP_STATS_IO_GRAPH_DIALOG):
+ help_topic_html("ChStatIOGraphs.html");
+ break;
+ case(HELP_STATS_COMPARE_FILES_DIALOG):
+ help_topic_html("ChStatCompareCaptureFiles.html");
+ break;
+ case(HELP_STATS_LTE_MAC_TRAFFIC_DIALOG):
+ help_topic_html("ChTelLTEMACTraffic.html");
+ break;
+ case(HELP_STATS_LTE_RLC_TRAFFIC_DIALOG):
+ help_topic_html("ChTelLTERLCTraffic.html");
+ break;
+ case(HELP_STATS_WLAN_TRAFFIC_DIALOG):
+ help_topic_html("ChStatWLANTraffic.html");
+ break;
+ case(HELP_FILESET_DIALOG):
+ help_topic_html("ChIOFileSetSection.html");
+ break;
+ case(HELP_CAPTURE_INTERFACE_OPTIONS_DIALOG):
+ help_topic_html("ChCustPreferencesSection.html#ChCustInterfaceOptionsSection");
+ break;
+ case(HELP_CAPTURE_INTERFACES_DETAILS_DIALOG):
+ help_topic_html("ChCapInterfaceDetailsSection.html");
+ break;
+ case(HELP_PREFERENCES_DIALOG):
+ help_topic_html("ChCustPreferencesSection.html");
+ break;
+ case(HELP_EXPORT_FILE_DIALOG):
+ case(HELP_EXPORT_FILE_WIN32_DIALOG):
+ help_topic_html("ChIOExportSection.html");
+ break;
+ case(HELP_EXPORT_BYTES_DIALOG):
+ case(HELP_EXPORT_BYTES_WIN32_DIALOG):
+ help_topic_html("ChIOExportSection.html#ChIOExportSelectedDialog");
+ break;
+ case(HELP_EXPORT_OBJECT_LIST):
+ help_topic_html("ChIOExportSection.html#ChIOExportObjectsDialog");
+ break;
+ case(HELP_OPEN_DIALOG):
+ case(HELP_OPEN_WIN32_DIALOG):
+ help_topic_html("ChIOOpenSection.html");
+ break;
+ case(HELP_MERGE_DIALOG):
+ case(HELP_MERGE_WIN32_DIALOG):
+ help_topic_html("ChIOMergeSection.html");
+ break;
+ case(HELP_IMPORT_DIALOG):
+ help_topic_html("ChIOImportSection.html");
+ break;
+ case(HELP_SAVE_DIALOG):
+ case(HELP_SAVE_WIN32_DIALOG):
+ help_topic_html("ChIOSaveSection.html");
+ break;
+ case(HELP_TIME_SHIFT_DIALOG):
+ help_topic_html("ChWorkShiftTimePacketSection.html");
+ break;
+ case(HELP_FILTER_SAVE_DIALOG):
+ help_topic_html("ChWorkFilterSaveSection.html");
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+}
+
+
+void
+topic_cb(GtkWidget *w _U_, topic_action_e action)
+{
+ topic_action(action);
+}
+
+
+gboolean
+topic_menu_cb(GtkWidget *w _U_, GdkEventButton *event _U_, gpointer user_data)
+{
+ topic_action((topic_action_e)GPOINTER_TO_INT(user_data));
+ return TRUE;
+}
+
diff --git a/ui/gtk/help_dlg.h b/ui/gtk/help_dlg.h
new file mode 100644
index 0000000000..1626ff5093
--- /dev/null
+++ b/ui/gtk/help_dlg.h
@@ -0,0 +1,136 @@
+/* help_dlg.h
+ *
+ * $Id$
+ *
+ * Laurent Deniel <laurent.deniel@free.fr>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2000 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __HELP_DLG_H__
+#define __HELP_DLG_H__
+
+/** @file
+ * "Help" dialog box.
+ * @ingroup dialog_group
+ */
+
+typedef enum {
+ /* pages online at www.wireshark.org */
+ ONLINEPAGE_HOME,
+ ONLINEPAGE_WIKI,
+ ONLINEPAGE_USERGUIDE,
+ ONLINEPAGE_FAQ,
+ ONLINEPAGE_DOWNLOAD,
+ ONLINEPAGE_SAMPLE_FILES,
+ ONLINEPAGE_CAPTURE_SETUP,
+ ONLINEPAGE_NETWORK_MEDIA,
+ ONLINEPAGE_SAMPLE_CAPTURES,
+ ONLINEPAGE_SECURITY,
+ ONLINEPAGE_CHIMNEY,
+
+ /* local manual pages */
+ LOCALPAGE_MAN_WIRESHARK = 100,
+ LOCALPAGE_MAN_WIRESHARK_FILTER,
+ LOCALPAGE_MAN_TSHARK,
+ LOCALPAGE_MAN_RAWSHARK,
+ LOCALPAGE_MAN_DUMPCAP,
+ LOCALPAGE_MAN_MERGECAP,
+ LOCALPAGE_MAN_EDITCAP,
+ LOCALPAGE_MAN_TEXT2PCAP,
+
+ /* help pages (textfiles or local HTML User's Guide) */
+ HELP_CONTENT = 200,
+ HELP_GETTING_STARTED, /* currently unused */
+ HELP_CAPTURE_OPTIONS_DIALOG,
+ HELP_CAPTURE_FILTERS_DIALOG,
+ HELP_DISPLAY_FILTERS_DIALOG,
+ HELP_COLORING_RULES_DIALOG,
+ HELP_CONFIG_PROFILES_DIALOG,
+ HELP_MANUAL_ADDR_RESOLVE_DIALOG,
+ HELP_PRINT_DIALOG,
+ HELP_FIND_DIALOG,
+ HELP_FILESET_DIALOG,
+ HELP_FIREWALL_DIALOG,
+ HELP_GOTO_DIALOG,
+ HELP_CAPTURE_INTERFACES_DIALOG,
+ HELP_ENABLED_PROTOCOLS_DIALOG,
+ HELP_DECODE_AS_DIALOG,
+ HELP_DECODE_AS_SHOW_DIALOG,
+ HELP_FOLLOW_STREAM_DIALOG,
+ HELP_EXPERT_INFO_DIALOG,
+ HELP_STATS_SUMMARY_DIALOG,
+ HELP_STATS_PROTO_HIERARCHY_DIALOG,
+ HELP_STATS_ENDPOINTS_DIALOG,
+ HELP_STATS_CONVERSATIONS_DIALOG,
+ HELP_STATS_IO_GRAPH_DIALOG,
+ HELP_STATS_COMPARE_FILES_DIALOG,
+ HELP_STATS_LTE_MAC_TRAFFIC_DIALOG,
+ HELP_STATS_LTE_RLC_TRAFFIC_DIALOG,
+ HELP_STATS_WLAN_TRAFFIC_DIALOG,
+ HELP_CAPTURE_INTERFACE_OPTIONS_DIALOG,
+ HELP_CAPTURE_INTERFACES_DETAILS_DIALOG,
+ HELP_PREFERENCES_DIALOG,
+ HELP_CAPTURE_INFO_DIALOG,
+ HELP_EXPORT_FILE_DIALOG,
+ HELP_EXPORT_BYTES_DIALOG,
+ HELP_EXPORT_OBJECT_LIST,
+ HELP_OPEN_DIALOG,
+ HELP_MERGE_DIALOG,
+ HELP_IMPORT_DIALOG,
+ HELP_SAVE_DIALOG,
+ HELP_EXPORT_FILE_WIN32_DIALOG,
+ HELP_EXPORT_BYTES_WIN32_DIALOG,
+ HELP_OPEN_WIN32_DIALOG,
+ HELP_MERGE_WIN32_DIALOG,
+ HELP_SAVE_WIN32_DIALOG,
+ HELP_TIME_SHIFT_DIALOG,
+ HELP_FILTER_SAVE_DIALOG
+} topic_action_e;
+
+
+/** Open a specific topic (create a "Help" dialog box or open a webpage).
+ *
+ * @param widget parent widget (unused)
+ * @param topic the topic to display
+ */
+void topic_cb(GtkWidget *widget, topic_action_e topic);
+
+/** Open a specific topic called from a menu item.
+ *
+ * @param widget parent widget (unused)
+ * @param event A GdkEventButton *event
+ * @param user_data the topic to display
+ * @return TRUE
+ */
+gboolean topic_menu_cb(GtkWidget *widget _U_, GdkEventButton *event _U_, gpointer user_data);
+
+/** Redraw all the help dialog text widgets, to use a new font. */
+void help_redraw(void);
+
+
+/** Open the help dialog and show a specific HTML help page. */
+void help_topic_html(const gchar *topic);
+
+/** get the url string of one of the ONLINEPAGE_xxx values */
+const char *topic_online_url(topic_action_e action);
+
+
+#endif
diff --git a/ui/gtk/hostlist_eth.c b/ui/gtk/hostlist_eth.c
new file mode 100644
index 0000000000..9bc195e449
--- /dev/null
+++ b/ui/gtk/hostlist_eth.c
@@ -0,0 +1,91 @@
+/* hostlist_eth.c 2004 Ian Schorr
+ * modified from endpoint_talkers_eth.c 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/packet.h"
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-eth.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/hostlist_table.h"
+
+
+static int
+eth_hostlist_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ hostlist_table *hosts=(hostlist_table *)pit;
+ const eth_hdr *ehdr=vip;
+
+ /* Take two "add" passes per packet, adding for each direction, ensures that all
+ packets are counted properly (even if address is sending to itself)
+ XXX - this could probably be done more efficiently inside hostlist_table */
+ add_hostlist_table_data(hosts, &ehdr->src, 0, TRUE, 1, pinfo->fd->pkt_len, SAT_ETHER, PT_NONE);
+ add_hostlist_table_data(hosts, &ehdr->dst, 0, FALSE, 1, pinfo->fd->pkt_len, SAT_ETHER, PT_NONE);
+
+ return 1;
+}
+
+
+
+static void
+gtk_eth_hostlist_init(const char *optarg,
+ void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"hosts,eth,",10)){
+ filter=optarg+10;
+ } else {
+ filter=NULL;
+ }
+
+ init_hostlist_table(TRUE, "Ethernet", "eth", filter, eth_hostlist_packet);
+
+}
+
+void
+gtk_eth_hostlist_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ gtk_eth_hostlist_init("hosts,eth",NULL);
+}
+
+void
+register_tap_listener_eth_hostlist(void)
+{
+ register_stat_cmd_arg("hosts,eth", gtk_eth_hostlist_init,NULL);
+ register_hostlist_table(TRUE, "Ethernet", "eth", NULL /*filter*/, eth_hostlist_packet);
+}
diff --git a/ui/gtk/hostlist_fc.c b/ui/gtk/hostlist_fc.c
new file mode 100644
index 0000000000..f60bf91063
--- /dev/null
+++ b/ui/gtk/hostlist_fc.c
@@ -0,0 +1,92 @@
+/* hostlist_fc.c 2004 Ian Schorr
+ * modified from endpoint_talkers_fc.c 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/packet.h"
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/conversation.h>
+#include <epan/dissectors/packet-scsi.h>
+#include <epan/dissectors/packet-fc.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/hostlist_table.h"
+
+
+static int
+fc_hostlist_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ hostlist_table *hosts=(hostlist_table *)pit;
+ const fc_hdr *fchdr=vip;
+
+ /* Take two "add" passes per packet, adding for each direction, ensures that all
+ packets are counted properly (even if address is sending to itself)
+ XXX - this could probably be done more efficiently inside hostlist_table */
+ add_hostlist_table_data(hosts, &fchdr->s_id, 0, TRUE, 1, pinfo->fd->pkt_len, SAT_NONE, PT_NONE);
+ add_hostlist_table_data(hosts, &fchdr->d_id, 0, FALSE, 1, pinfo->fd->pkt_len, SAT_NONE, PT_NONE);
+
+ return 1;
+}
+
+
+
+static void
+gtk_fc_hostlist_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"hosts,fc,",9)){
+ filter=optarg+9;
+ } else {
+ filter=NULL;
+ }
+
+ init_hostlist_table(TRUE, "Fibre Channel", "fc", filter, fc_hostlist_packet);
+
+}
+
+void
+gtk_fc_hostlist_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ gtk_fc_hostlist_init("hosts,fc",NULL);
+}
+
+void
+register_tap_listener_fc_hostlist(void)
+{
+ register_stat_cmd_arg("hosts,fc", gtk_fc_hostlist_init,NULL);
+ register_hostlist_table(TRUE, "Fibre Channel", "fc", NULL /*filter*/, fc_hostlist_packet);
+}
diff --git a/ui/gtk/hostlist_fddi.c b/ui/gtk/hostlist_fddi.c
new file mode 100644
index 0000000000..6a01134d7f
--- /dev/null
+++ b/ui/gtk/hostlist_fddi.c
@@ -0,0 +1,89 @@
+/* hostlist_fddi.c 2004 Ian Schorr
+ * modified from endpoint_talkers_fddi.c 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/packet.h"
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-fddi.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/hostlist_table.h"
+
+static int
+fddi_hostlist_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ hostlist_table *hosts=(hostlist_table *)pit;
+ const fddi_hdr *ehdr=vip;
+
+ /* Take two "add" passes per packet, adding for each direction, ensures that all
+ packets are counted properly (even if address is sending to itself)
+ XXX - this could probably be done more efficiently inside hostlist_table */
+ add_hostlist_table_data(hosts, &ehdr->src, 0, TRUE, 1, pinfo->fd->pkt_len, SAT_FDDI, PT_NONE);
+ add_hostlist_table_data(hosts, &ehdr->dst, 0, FALSE, 1, pinfo->fd->pkt_len, SAT_FDDI, PT_NONE);
+
+ return 1;
+}
+
+
+
+static void
+gtk_fddi_hostlist_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"hosts,fddi,",11)){
+ filter=optarg+11;
+ } else {
+ filter=NULL;
+ }
+
+ init_hostlist_table(TRUE, "FDDI", "fddi", filter, fddi_hostlist_packet);
+
+}
+
+void
+gtk_fddi_hostlist_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ gtk_fddi_hostlist_init("hosts,fddi",NULL);
+}
+
+void
+register_tap_listener_fddi_hostlist(void)
+{
+ register_stat_cmd_arg("hosts,fddi", gtk_fddi_hostlist_init,NULL);
+ register_hostlist_table(TRUE, "FDDI", "fddi", NULL /*filter*/, fddi_hostlist_packet);
+}
diff --git a/ui/gtk/hostlist_ip.c b/ui/gtk/hostlist_ip.c
new file mode 100644
index 0000000000..7d7ac874df
--- /dev/null
+++ b/ui/gtk/hostlist_ip.c
@@ -0,0 +1,88 @@
+/* hostlist_ip.c 2004 Ian Schorr
+ * modified from endpoint_talkers_ip.c 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/packet.h"
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-ip.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/hostlist_table.h"
+
+static int
+ip_hostlist_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ hostlist_table *hosts=(hostlist_table *)pit;
+ const ws_ip *iph=vip;
+
+ /* Take two "add" passes per packet, adding for each direction, ensures that all
+ packets are counted properly (even if address is sending to itself)
+ XXX - this could probably be done more efficiently inside hostlist_table */
+ add_hostlist_table_data(hosts, &iph->ip_src, 0, TRUE, 1, pinfo->fd->pkt_len, SAT_NONE, PT_NONE);
+ add_hostlist_table_data(hosts, &iph->ip_dst, 0, FALSE, 1, pinfo->fd->pkt_len, SAT_NONE, PT_NONE);
+ return 1;
+}
+
+static void
+gtk_ip_hostlist_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"hosts,ip,",9)){
+ filter=optarg+9;
+ } else {
+ filter=NULL;
+ }
+
+ init_hostlist_table(TRUE, "IPv4", "ip", filter, ip_hostlist_packet);
+
+}
+
+void
+gtk_ip_hostlist_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ gtk_ip_hostlist_init("hosts,ip",NULL);
+}
+
+void
+register_tap_listener_ip_hostlist(void)
+{
+ register_stat_cmd_arg("hosts,ip", gtk_ip_hostlist_init,NULL);
+ register_hostlist_table(TRUE, "IPv4", "ip", NULL /*filter*/, ip_hostlist_packet);
+}
diff --git a/ui/gtk/hostlist_ipv6.c b/ui/gtk/hostlist_ipv6.c
new file mode 100644
index 0000000000..fe0991999c
--- /dev/null
+++ b/ui/gtk/hostlist_ipv6.c
@@ -0,0 +1,95 @@
+/* hostlist_ipv6.c 2009 Clif Bratcher
+ * Modified from hostlist_ip.c 2004 Ian Schorr
+ * modified from endpoint_talkers_ip.c 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/packet.h"
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-ipv6.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/hostlist_table.h"
+
+static int
+ipv6_hostlist_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ hostlist_table *hosts = (hostlist_table *)pit;
+ const struct ip6_hdr *ip6h = vip;
+ address src;
+ address dst;
+
+ /* Addresses aren't implemented as 'address' type in struct ip6_hdr */
+ src.type = dst.type = AT_IPv6;
+ src.len = dst.len = sizeof(struct e_in6_addr);
+ src.data = &ip6h->ip6_src;
+ dst.data = &ip6h->ip6_dst;
+
+ add_hostlist_table_data(hosts, &src, 0, TRUE, 1, pinfo->fd->pkt_len, SAT_NONE, PT_NONE);
+ add_hostlist_table_data(hosts, &dst, 0, FALSE, 1, pinfo->fd->pkt_len, SAT_NONE, PT_NONE);
+
+ return 1;
+}
+
+
+static void
+gtk_ipv6_hostlist_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"hosts,ipv6,",10)){
+ filter = optarg + 10;
+ } else {
+ filter = NULL;
+ }
+
+ init_hostlist_table(TRUE, "IPv6", "ipv6", filter, ipv6_hostlist_packet);
+}
+
+void
+gtk_ipv6_hostlist_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ gtk_ipv6_hostlist_init("hosts,ipv6", NULL);
+}
+
+void
+register_tap_listener_ipv6_hostlist(void)
+{
+ register_stat_cmd_arg("hosts,ipv6", gtk_ipv6_hostlist_init, NULL);
+ register_hostlist_table(TRUE, "IPv6", "ipv6", NULL /*filter*/, ipv6_hostlist_packet);
+}
diff --git a/ui/gtk/hostlist_ipx.c b/ui/gtk/hostlist_ipx.c
new file mode 100644
index 0000000000..802c055ac2
--- /dev/null
+++ b/ui/gtk/hostlist_ipx.c
@@ -0,0 +1,89 @@
+/* hostlist_ipx.c 2004 Ian Schorr
+ * modified from endpoint_talkers_ipx.c 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/packet.h"
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-ipx.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/hostlist_table.h"
+
+static int
+ipx_hostlist_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ hostlist_table *hosts=(hostlist_table *)pit;
+ const ipxhdr_t *ipxh=vip;
+
+ /* Take two "add" passes per packet, adding for each direction, ensures that all
+ packets are counted properly (even if address is sending to itself)
+ XXX - this could probably be done more efficiently inside hostlist_table */
+ add_hostlist_table_data(hosts, &ipxh->ipx_src, 0, TRUE, 1, pinfo->fd->pkt_len, SAT_NONE, PT_NONE);
+ add_hostlist_table_data(hosts, &ipxh->ipx_dst, 0, FALSE, 1, pinfo->fd->pkt_len, SAT_NONE, PT_NONE);
+
+ return 1;
+}
+
+
+
+static void
+gtk_ipx_hostlist_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"hosts,ipx,",10)){
+ filter=optarg+10;
+ } else {
+ filter=NULL;
+ }
+
+ init_hostlist_table(TRUE, "IPX", "ipx", filter, ipx_hostlist_packet);
+
+}
+
+void
+gtk_ipx_hostlist_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ gtk_ipx_hostlist_init("hosts,ipx",NULL);
+}
+
+void
+register_tap_listener_ipx_hostlist(void)
+{
+ register_stat_cmd_arg("hosts,ipx", gtk_ipx_hostlist_init,NULL);
+ register_hostlist_table(TRUE, "IPX", "ipx", NULL /*filter*/, ipx_hostlist_packet);
+}
diff --git a/ui/gtk/hostlist_jxta.c b/ui/gtk/hostlist_jxta.c
new file mode 100644
index 0000000000..95c0f517c8
--- /dev/null
+++ b/ui/gtk/hostlist_jxta.c
@@ -0,0 +1,86 @@
+/* hostlist_jxta.c 2005 Mike Duigou
+ * modified from hostlist_eth.c 2004 Ian Schorr
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/packet.h"
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-jxta.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/hostlist_table.h"
+
+static int
+jxta_hostlist_packet(void *pit, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip)
+{
+ hostlist_table *hosts = (hostlist_table *) pit;
+ const jxta_tap_header *jxtahdr = vip;
+
+ /* Take two "add" passes per packet, adding for each direction, ensures that all
+ packets are counted properly (even if address is sending to itself)
+ XXX - this could probably be done more efficiently inside hostlist_table */
+ add_hostlist_table_data(hosts, &jxtahdr->src_address, 0, TRUE, 1, jxtahdr->size, SAT_JXTA, PT_NONE);
+ add_hostlist_table_data(hosts, &jxtahdr->dest_address, 0, FALSE, 1, jxtahdr->size, SAT_JXTA, PT_NONE);
+ return 1;
+}
+
+static void
+gtk_jxta_hostlist_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"hosts,jxta,",11)){
+ filter=optarg+11;
+ } else {
+ filter=NULL;
+ }
+
+ init_hostlist_table(TRUE, "JXTA", "jxta", filter, jxta_hostlist_packet);
+
+}
+
+void
+gtk_jxta_hostlist_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ gtk_jxta_hostlist_init("hosts,jxta",NULL);
+}
+
+void
+register_tap_listener_jxta_hostlist(void)
+{
+ register_stat_cmd_arg("hosts,jxta", gtk_jxta_hostlist_init,NULL);
+ register_hostlist_table(TRUE, "JXTA", "jxta", NULL /*filter*/, jxta_hostlist_packet);
+}
diff --git a/ui/gtk/hostlist_ncp.c b/ui/gtk/hostlist_ncp.c
new file mode 100644
index 0000000000..2a03d780d3
--- /dev/null
+++ b/ui/gtk/hostlist_ncp.c
@@ -0,0 +1,87 @@
+/* hostlist_ncp.c 2006 Greg Morris
+ * modified from endpoint_talkers_eth.c 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/packet.h"
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+/*#include <epan/dissectors/packet-ncp-int.h>*/
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/hostlist_table.h"
+
+static int
+ncp_hostlist_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip _U_)
+{
+ hostlist_table *hosts=(hostlist_table *)pit;
+ /*const ncp_common_header *ncphdr=vip;*/
+
+ /* Take two "add" passes per packet, adding for each direction, ensures that all
+ packets are counted properly (even if address is sending to itself)
+ XXX - this could probably be done more efficiently inside hostlist_table */
+ add_hostlist_table_data(hosts, &pinfo->src, 0, TRUE, 1, pinfo->fd->pkt_len, SAT_ETHER, PT_NCP);
+ add_hostlist_table_data(hosts, &pinfo->dst, 0, FALSE, 1, pinfo->fd->pkt_len, SAT_ETHER, PT_NCP);
+
+ return 1;
+}
+
+static void
+gtk_ncp_hostlist_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"hosts,ncp,",10)){
+ filter=optarg+10;
+ } else {
+ filter=NULL;
+ }
+
+ init_hostlist_table(TRUE, "NCP", "ncp_hdr", filter, ncp_hostlist_packet);
+
+}
+
+void
+gtk_ncp_hostlist_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ gtk_ncp_hostlist_init("hosts,ncp",NULL);
+}
+
+void
+register_tap_listener_ncp_hostlist(void)
+{
+ register_stat_cmd_arg("hosts,ncp", gtk_ncp_hostlist_init,NULL);
+ register_hostlist_table(TRUE, "NCP", "ncp_hdr", NULL /*filter*/, ncp_hostlist_packet);
+}
diff --git a/ui/gtk/hostlist_rsvp.c b/ui/gtk/hostlist_rsvp.c
new file mode 100644
index 0000000000..ccbe88cb37
--- /dev/null
+++ b/ui/gtk/hostlist_rsvp.c
@@ -0,0 +1,94 @@
+/* hostlist_rsvp.c
+ * hostlist_rsvp.c August 2005, Manu Pathak <mapathak@cisco.com>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <gtk/gtk.h>
+
+#include "epan/packet.h"
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-rsvp.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/hostlist_table.h"
+
+static int
+rsvp_hostlist_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ hostlist_table *hosts=(hostlist_table *)pit;
+ const rsvp_conversation_info *rsvph = vip;
+
+ /* Take two "add" passes per packet, adding for each direction, ensures
+ * that all packets are counted properly (even if address is sending to
+ * itself). XXX - this could probably be done more efficiently inside
+ * hostlist_table
+ */
+ add_hostlist_table_data(hosts, &rsvph->source, 0, TRUE, 1,
+ pinfo->fd->pkt_len, SAT_NONE, PT_NONE);
+ add_hostlist_table_data(hosts, &rsvph->destination, 0, FALSE, 1,
+ pinfo->fd->pkt_len, SAT_NONE, PT_NONE);
+ return 1;
+}
+
+static void
+gtk_rsvp_hostlist_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"hosts,rsvp,",11)){
+ filter=optarg+11;
+ } else {
+ filter=NULL;
+ }
+
+ init_hostlist_table(TRUE, "RSVP", "rsvp", filter,
+ rsvp_hostlist_packet);
+
+}
+
+void
+gtk_rsvp_hostlist_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ gtk_rsvp_hostlist_init("hosts,rsvp",NULL);
+}
+
+void
+register_tap_listener_rsvp_hostlist(void)
+{
+ register_stat_cmd_arg("hosts,rsvp", gtk_rsvp_hostlist_init,NULL);
+ register_hostlist_table(TRUE, "RSVP", "rsvp", NULL /*filter*/,
+ rsvp_hostlist_packet);
+}
diff --git a/ui/gtk/hostlist_sctp.c b/ui/gtk/hostlist_sctp.c
new file mode 100644
index 0000000000..132e831a93
--- /dev/null
+++ b/ui/gtk/hostlist_sctp.c
@@ -0,0 +1,87 @@
+/* hostlist_sctp.c 2008 Stig Bjorlykke
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/packet.h"
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-sctp.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/hostlist_table.h"
+
+static int
+sctp_hostlist_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ hostlist_table *hosts=(hostlist_table *)pit;
+ const struct _sctp_info *sctphdr=vip;
+
+ /* Take two "add" passes per packet, adding for each direction, ensures that all
+ packets are counted properly (even if address is sending to itself)
+ XXX - this could probably be done more efficiently inside hostlist_table */
+ add_hostlist_table_data(hosts, &sctphdr->ip_src, sctphdr->sport, TRUE, 1,
+ pinfo->fd->pkt_len, SAT_NONE, PT_SCTP);
+ add_hostlist_table_data(hosts, &sctphdr->ip_dst, sctphdr->dport, FALSE, 1,
+ pinfo->fd->pkt_len, SAT_NONE, PT_SCTP);
+
+ return 1;
+}
+
+static void
+gtk_sctp_hostlist_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"hosts,sctp,",11)){
+ filter=optarg+11;
+ } else {
+ filter=NULL;
+ }
+
+ init_hostlist_table(FALSE, "SCTP", "sctp", filter, sctp_hostlist_packet);
+}
+
+void
+gtk_sctp_hostlist_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ gtk_sctp_hostlist_init("hosts,sctp",NULL);
+}
+
+void
+register_tap_listener_sctp_hostlist(void)
+{
+ register_stat_cmd_arg("hosts,sctp", gtk_sctp_hostlist_init,NULL);
+ register_hostlist_table(FALSE, "SCTP", "sctp", NULL /*filter*/, sctp_hostlist_packet);
+}
diff --git a/ui/gtk/hostlist_table.c b/ui/gtk/hostlist_table.c
new file mode 100644
index 0000000000..923262777e
--- /dev/null
+++ b/ui/gtk/hostlist_table.c
@@ -0,0 +1,1800 @@
+/* hostlist_table.c 2004 Ian Schorr
+ * modified from endpoint_talkers_table.c 2003 Ronnie Sahlberg
+ * Helper routines common to all host list taps.
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <locale.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/to_str.h>
+#include <epan/addr_resolv.h>
+#include <epan/tap.h>
+#include <epan/strutil.h>
+#ifdef HAVE_GEOIP
+#include "GeoIP.h"
+#include <epan/geoip_db.h>
+#include <epan/pint.h>
+#include <epan/filesystem.h>
+#endif
+
+#include <wsutil/file_util.h>
+
+#include "../simple_dialog.h"
+#include "../alert_box.h"
+#include "../tempfile.h"
+
+#include "ui/gtk/hostlist_table.h"
+#include "ui/gtk/filter_utils.h"
+#include "ui/gtk/gtkglobals.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/utf8_entities.h"
+#ifdef HAVE_GEOIP
+#include "ui/gtk/webbrowser.h"
+#include "ui/gtk/stock_icons.h"
+#endif
+
+#include "ui/gtk/old-gtk-compat.h"
+
+#define HOST_PTR_KEY "hostlist-pointer"
+#define NB_PAGES_KEY "notebook-pages"
+
+#define CMP_INT(i1, i2) \
+ if ((i1) > (i2)) \
+ return 1; \
+ else if ((i1) < (i2)) \
+ return -1; \
+ else \
+ return 0;
+
+#define COL_STR_LEN 32
+
+/* convert a port number into a string */
+static char *
+hostlist_port_to_str(int port_type_val, guint32 port)
+{
+ static int i=0;
+ static gchar *strp, str[4][12];
+ gchar *bp;
+
+ switch(port_type_val){
+ case PT_TCP:
+ case PT_UDP:
+ case PT_SCTP:
+ i = (i+1)%4;
+ strp=str[i];
+ bp = &strp[11];
+
+ *bp = 0;
+ do {
+ *--bp = (port % 10) +'0';
+ } while ((port /= 10) != 0 && bp > strp);
+ return bp;
+ }
+ return NULL;
+}
+
+
+#define FN_ANY_ADDRESS 0
+#define FN_ANY_PORT 1
+
+/* Given an address (to distinguish between ipv4 and ipv6 for tcp/udp,
+ a port_type and a name_type (FN_...)
+ return a string for the filter name.
+
+ Some addresses, like AT_ETHER may actually be any of multiple types
+ of protocols, either ethernet, tokenring, fddi etc so we must be more
+ specific there; that's why we need specific_addr_type.
+*/
+static const char *
+hostlist_get_filter_name(address *addr, int specific_addr_type_val, int port_type_val, int name_type_val)
+{
+ switch(name_type_val){
+ case FN_ANY_ADDRESS:
+ switch(addr->type){
+ case AT_ETHER:
+ switch(specific_addr_type_val){
+ case SAT_ETHER:
+ return "eth.addr";
+ case SAT_WLAN:
+ return "wlan.addr";
+ case SAT_FDDI:
+ return "fddi.addr";
+ case SAT_TOKENRING:
+ return "tr.addr";
+ default:
+ break;
+ }
+ break;
+ case AT_IPv4:
+ return "ip.addr";
+ case AT_IPv6:
+ return "ipv6.addr";
+ case AT_IPX:
+ return "ipx.addr";
+ case AT_FC:
+ return "fc.id";
+ case AT_URI:
+ switch(specific_addr_type_val){
+ case SAT_JXTA:
+ return "jxta.message.address";
+ default:
+ break;
+ }
+ break;
+ case AT_USB:
+ return "usb.addr";
+ default:
+ break;
+ }
+ break;
+ case FN_ANY_PORT:
+ switch(port_type_val){
+ case PT_TCP:
+ return "tcp.port";
+ case PT_UDP:
+ return "udp.port";
+ case PT_SCTP:
+ return "sctp.port";
+ }
+ break;
+ }
+
+ g_assert_not_reached();
+ return NULL;
+}
+
+static void
+reset_hostlist_table_data(hostlist_table *hosts)
+{
+ guint32 i;
+ char title[256];
+ GString *error_string;
+ const char *filter;
+ GtkListStore *store;
+
+ if (hosts->use_dfilter) {
+ filter = gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
+ } else {
+ filter = hosts->filter;
+ }
+ error_string = set_tap_dfilter (hosts, filter);
+ if (error_string) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ return;
+ }
+
+
+ if(hosts->page_lb) {
+ g_snprintf(title, sizeof(title), "Endpoints: %s", cf_get_display_name(&cfile));
+ gtk_window_set_title(GTK_WINDOW(hosts->win), title);
+ g_snprintf(title, sizeof(title), "%s", hosts->name);
+ gtk_label_set_text(GTK_LABEL(hosts->page_lb), title);
+ gtk_widget_set_sensitive(hosts->page_lb, FALSE);
+
+ if (hosts->use_dfilter) {
+ if (filter && strlen(filter)) {
+ g_snprintf(title, sizeof(title), "%s Endpoints - Filter: %s", hosts->name, filter);
+ } else {
+ g_snprintf(title, sizeof(title), "%s Endpoints - No Filter", hosts->name);
+ }
+ } else {
+ g_snprintf(title, sizeof(title), "%s Endpoints", hosts->name);
+ }
+ gtk_label_set_text(GTK_LABEL(hosts->name_lb), title);
+ } else {
+ g_snprintf(title, sizeof(title), "%s Endpoints: %s", hosts->name, cf_get_display_name(&cfile));
+ gtk_window_set_title(GTK_WINDOW(hosts->win), title);
+ }
+
+ /* remove all entries from the list */
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(hosts->table)));
+ gtk_list_store_clear(store);
+
+ /* delete all hosts */
+ for(i=0;i<hosts->num_hosts;i++){
+ hostlist_talker_t *host = &g_array_index(hosts->hosts, hostlist_talker_t, i);
+ g_free((gpointer)host->address.data);
+ }
+
+ if (hosts->hosts)
+ g_array_free(hosts->hosts, TRUE);
+
+ if (hosts->hashtable != NULL)
+ g_hash_table_destroy(hosts->hashtable);
+
+ hosts->hosts=NULL;
+ hosts->hashtable=NULL;
+ hosts->num_hosts=0;
+}
+
+static void
+reset_hostlist_table_data_cb(void *arg)
+{
+ reset_hostlist_table_data(arg);
+}
+
+static void
+hostlist_win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ hostlist_table *hosts=(hostlist_table *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(hosts);
+ unprotect_thread_critical_region();
+
+ reset_hostlist_table_data(hosts);
+ g_free(hosts);
+}
+
+enum
+{
+ ADR_COLUMN,
+ PORT_COLUMN,
+ PACKETS_COLUMN,
+ BYTES_COLUMN,
+ PKT_AB_COLUMN,
+ BYTES_AB_COLUMN,
+ PKT_BA_COLUMN,
+ BYTES_BA_COLUMN,
+#ifdef HAVE_GEOIP
+ GEOIP1_COLUMN,
+ GEOIP2_COLUMN,
+ GEOIP3_COLUMN,
+ GEOIP4_COLUMN,
+ GEOIP5_COLUMN,
+ GEOIP6_COLUMN,
+ GEOIP7_COLUMN,
+ GEOIP8_COLUMN,
+ GEOIP9_COLUMN,
+ GEOIP10_COLUMN,
+ GEOIP11_COLUMN,
+ GEOIP12_COLUMN,
+ GEOIP13_COLUMN,
+#endif
+ INDEX_COLUMN,
+ N_COLUMNS
+};
+
+static gint
+hostlist_sort_column(GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+
+{
+ guint32 idx1, idx2;
+ gint data_column = GPOINTER_TO_INT(user_data);
+ hostlist_table *hl = g_object_get_data(G_OBJECT(model), HOST_PTR_KEY);
+ hostlist_talker_t *host1 = NULL;
+ hostlist_talker_t *host2 = NULL;
+
+ gtk_tree_model_get(model, a, INDEX_COLUMN, &idx1, -1);
+ gtk_tree_model_get(model, b, INDEX_COLUMN, &idx2, -1);
+
+ if (!hl || idx1 >= hl->num_hosts || idx2 >= hl->num_hosts)
+ return 0;
+
+ host1 = &g_array_index(hl->hosts, hostlist_talker_t, idx1);
+ host2 = &g_array_index(hl->hosts, hostlist_talker_t, idx2);
+
+ switch(data_column){
+ case 0: /* Address */
+ return(CMP_ADDRESS(&host1->address, &host2->address));
+ case 1: /* (Port) */
+ CMP_INT(host1->port, host2->port);
+#ifdef HAVE_GEOIP
+ default:
+ {
+ gchar *text1, *text2;
+ double loc1 = 0, loc2 = 0;
+
+ gtk_tree_model_get(model, a, data_column, &text1, -1);
+ gtk_tree_model_get(model, b, data_column, &text2, -1);
+
+ if (text1) {
+ loc1 = atof(text1);
+ g_free(text1);
+ }
+
+ if (text2) {
+ loc2 = atof(text2);
+ g_free(text2);
+ }
+ CMP_INT(loc1, loc2);
+ }
+ break;
+#endif
+ }
+ g_assert_not_reached();
+ return 0;
+}
+
+static void
+hostlist_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callback_action)
+{
+ guint idx;
+ hostlist_table *hl=(hostlist_table *)callback_data;
+ char *str = NULL;
+ char *sport;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreeSelection *sel;
+ hostlist_talker_t *host = NULL;
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(hl->table));
+ if (!gtk_tree_selection_get_selected(sel, &model, &iter))
+ return;
+
+ gtk_tree_model_get (model, &iter,
+ INDEX_COLUMN, &idx,
+ -1);
+
+ if(idx>= hl->num_hosts){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No hostlist selected");
+ return;
+ }
+ host = &g_array_index(hl->hosts, hostlist_talker_t, idx);
+
+ sport=hostlist_port_to_str(host->port_type, host->port);
+
+ str = g_strdup_printf("%s==%s%s%s%s%s",
+ hostlist_get_filter_name(&host->address, host->sat, host->port_type, FN_ANY_ADDRESS),
+ ep_address_to_str(&host->address),
+ sport?" && ":"",
+ sport?hostlist_get_filter_name(&host->address, host->sat, host->port_type, FN_ANY_PORT):"",
+ sport?"==":"",
+ sport?sport:"");
+
+ apply_selected_filter (callback_action, str);
+
+ g_free (str);
+}
+static gboolean
+hostlist_show_popup_menu_cb(void *widg _U_, GdkEvent *event, hostlist_table *et)
+{
+ GdkEventButton *bevent = (GdkEventButton *)event;
+
+ if(event->type==GDK_BUTTON_PRESS && bevent->button==3){
+ gtk_menu_popup(GTK_MENU(et->menu), NULL, NULL, NULL, NULL,
+ bevent->button, bevent->time);
+ }
+
+ return FALSE;
+}
+
+/* Action callbacks */
+static void
+apply_as_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ hostlist_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_SELECTED, 0));
+}
+static void
+apply_as_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ hostlist_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_NOT_SELECTED, 0));
+}
+static void
+apply_as_and_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ hostlist_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_SELECTED, 0));
+}
+static void
+apply_as_or_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ hostlist_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_SELECTED, 0));
+}
+static void
+apply_as_and_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ hostlist_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, 0));
+}
+static void
+apply_as_or_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ hostlist_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, 0));
+}
+
+static void
+prep_as_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ hostlist_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_SELECTED, 0));
+}
+static void
+prep_as_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ hostlist_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, 0));
+}
+static void
+prep_as_and_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ hostlist_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_SELECTED, 0));
+}
+static void
+prep_as_or_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ hostlist_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_SELECTED, 0));
+}
+static void
+prep_as_and_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ hostlist_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, 0));
+}
+static void
+prep_as_or_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ hostlist_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, 0));
+}
+
+static void
+find_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ hostlist_select_filter_cb( widget , user_data, CALLBACK_FIND_FRAME(ACTYPE_SELECTED, 0));
+}
+static void
+find_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ hostlist_select_filter_cb( widget , user_data, CALLBACK_FIND_FRAME(ACTYPE_NOT_SELECTED, 0));
+}
+static void
+find_prev_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ hostlist_select_filter_cb( widget , user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, 0));
+}
+static void
+find_prev_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ hostlist_select_filter_cb( widget , user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_NOT_SELECTED, 0));
+}
+static void
+find_next_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ hostlist_select_filter_cb( widget , user_data, CALLBACK_FIND_NEXT(ACTYPE_SELECTED, 0));
+}
+static void
+find_next_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ hostlist_select_filter_cb( widget , user_data, CALLBACK_FIND_NEXT(ACTYPE_NOT_SELECTED, 0));
+}
+static void
+color_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ hostlist_select_filter_cb( widget , user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, 0));
+}
+
+static const char *ui_desc_hostlist_table_popup =
+"<ui>\n"
+" <popup name='HostlistTableFilterPopup'>\n"
+" <menu action='/Apply as Filter'>\n"
+" <menuitem action='/Apply as Filter/Selected'/>\n"
+" <menuitem action='/Apply as Filter/Not Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected'/>\n"
+" </menu>\n"
+" <menu action='/Prepare a Filter'>\n"
+" <menuitem action='/Prepare a Filter/Selected'/>\n"
+" <menuitem action='/Prepare a Filter/Not Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected'/>\n"
+" </menu>\n"
+" <menu action='/Find Frame'>\n"
+" <menu action='/Find Frame/Find Frame'>\n"
+" <menuitem action='/Find Frame/Selected'/>\n"
+" <menuitem action='/Find Frame/Not Selected'/>\n"
+" </menu>\n"
+" <menu action='/Find Frame/Find Next'>\n"
+" <menuitem action='/Find Next/Selected'/>\n"
+" <menuitem action='/Find Next/Not Selected'/>\n"
+" </menu>\n"
+" <menu action='/Find Frame/Find Previous'>\n"
+" <menuitem action='/Find Previous/Selected'/>\n"
+" <menuitem action='/Find Previous/Not Selected'/>\n"
+" </menu>\n"
+" </menu>\n"
+" <menu action='/Colorize Procedure'>\n"
+" <menuitem action='/Colorize Procedure/Colorize Host Traffic'/>\n"
+" </menu>\n"
+" </popup>\n"
+"</ui>\n";
+
+/*
+ * GtkActionEntry
+ * typedef struct {
+ * const gchar *name;
+ * const gchar *stock_id;
+ * const gchar *label;
+ * const gchar *accelerator;
+ * const gchar *tooltip;
+ * GCallback callback;
+ * } GtkActionEntry;
+ * const gchar *name; The name of the action.
+ * const gchar *stock_id; The stock id for the action, or the name of an icon from the icon theme.
+ * const gchar *label; The label for the action. This field should typically be marked for translation,
+ * see gtk_action_group_set_translation_domain().
+ * If label is NULL, the label of the stock item with id stock_id is used.
+ * const gchar *accelerator; The accelerator for the action, in the format understood by gtk_accelerator_parse().
+ * const gchar *tooltip; The tooltip for the action. This field should typically be marked for translation,
+ * see gtk_action_group_set_translation_domain().
+ * GCallback callback; The function to call when the action is activated.
+ *
+ */
+static const GtkActionEntry service_resp_t__popup_entries[] = {
+ { "/Apply as Filter", NULL, "Apply as Filter", NULL, NULL, NULL },
+ { "/Prepare a Filter", NULL, "Prepare a Filter", NULL, NULL, NULL },
+ { "/Find Frame", NULL, "Find Frame", NULL, NULL, NULL },
+ { "/Find Frame/Find Frame", NULL, "Find Frame", NULL, NULL, NULL },
+ { "/Find Frame/Find Next", NULL, "Find Next" , NULL, NULL, NULL },
+ { "/Find Frame/Find Previous", NULL, "Find Previous", NULL, NULL, NULL },
+ { "/Colorize Procedure", NULL, "Colorize Procedure", NULL, NULL, NULL },
+ { "/Apply as Filter/Selected", NULL, "Selected", NULL, "Selected", G_CALLBACK(apply_as_selected_cb) },
+ { "/Apply as Filter/Not Selected", NULL, "Not Selected", NULL, "Not Selected", G_CALLBACK(apply_as_not_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", G_CALLBACK(apply_as_and_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", G_CALLBACK(apply_as_or_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", G_CALLBACK(apply_as_and_not_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", G_CALLBACK(apply_as_or_not_selected_cb) },
+ { "/Prepare a Filter/Selected", NULL, "Selected", NULL, "selcted", G_CALLBACK(prep_as_selected_cb) },
+ { "/Prepare a Filter/Not Selected", NULL, "Not Selected", NULL, "Not Selected", G_CALLBACK(prep_as_not_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", G_CALLBACK(prep_as_and_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", G_CALLBACK(prep_as_or_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", G_CALLBACK(prep_as_and_not_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", G_CALLBACK(prep_as_or_not_selected_cb) },
+ { "/Find Frame/Selected", NULL, "Selected", NULL, "Selected", G_CALLBACK(find_selected_cb) },
+ { "/Find Frame/Not Selected", NULL, "Not Selected", NULL, "Not Selected", G_CALLBACK(find_not_selected_cb) },
+ { "/Find Previous/Selected", NULL, "Selected", NULL, "Selected", G_CALLBACK(find_prev_selected_cb) },
+ { "/Find Previous/Not Selected", NULL, "Not Selected", NULL, "Not Selected", G_CALLBACK(find_prev_not_selected_cb) },
+ { "/Find Next/Selected", NULL, "Selected", NULL, "Selected", G_CALLBACK(find_next_selected_cb) },
+ { "/Find Next/Not Selected", NULL, "Not Selected", NULL, "Not Selected", G_CALLBACK(find_next_not_selected_cb) },
+ { "/Colorize Procedure/Colorize Host Traffic",NULL, "Colorize Host Traffic", NULL, "Colorize Host Traffic", G_CALLBACK(color_selected_cb) },
+};
+
+static void
+hostlist_create_popup_menu(hostlist_table *hl)
+{
+ GtkUIManager *ui_manager;
+ GtkActionGroup *action_group;
+ GError *error = NULL;
+
+ action_group = gtk_action_group_new ("HostlistTablePopupActionGroup");
+ gtk_action_group_add_actions (action_group, /* the action group */
+ (gpointer)service_resp_t__popup_entries, /* an array of action descriptions */
+ G_N_ELEMENTS(service_resp_t__popup_entries),/* the number of entries */
+ hl); /* data to pass to the action callbacks */
+
+ ui_manager = gtk_ui_manager_new ();
+ gtk_ui_manager_insert_action_group (ui_manager,
+ action_group,
+ 0); /* the position at which the group will be inserted */
+ gtk_ui_manager_add_ui_from_string (ui_manager,ui_desc_hostlist_table_popup, -1, &error);
+ if (error != NULL)
+ {
+ fprintf (stderr, "Warning: building hostlist table filter popup failed: %s\n",
+ error->message);
+ g_error_free (error);
+ error = NULL;
+ }
+ hl->menu = gtk_ui_manager_get_widget(ui_manager, "/HostlistTableFilterPopup");
+ g_signal_connect(hl->table, "button_press_event", G_CALLBACK(hostlist_show_popup_menu_cb), hl);
+}
+
+
+/* Draw/refresh the address field of a single entry at the specified index */
+static void
+get_hostlist_table_address(hostlist_table *hl, hostlist_talker_t *host, char **entries)
+{
+ char *port;
+ guint32 pt;
+
+ if (!hl->resolve_names)
+ entries[0] = ep_address_to_str(&host->address);
+ else
+ entries[0] = (char *)get_addr_name(&host->address);
+
+ pt = host->port_type;
+ if(!hl->resolve_names) pt = PT_NONE;
+ switch(pt) {
+ case(PT_TCP):
+ entries[1] = get_tcp_port(host->port);
+ break;
+ case(PT_UDP):
+ entries[1] = get_udp_port(host->port);
+ break;
+ case(PT_SCTP):
+ entries[1] = get_sctp_port(host->port);
+ break;
+ default:
+ port=hostlist_port_to_str(host->port_type, host->port);
+ entries[1] = port?port:"";
+ }
+}
+
+/* Refresh the address fields of all entries in the list */
+static void
+draw_hostlist_table_addresses(hostlist_table *hl)
+{
+ guint32 i;
+ char *entries[2];
+ GtkListStore *store;
+
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(hl->table));
+ g_object_ref(store);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(hl->table), NULL);
+
+ for(i=0;i<hl->num_hosts;i++){
+ hostlist_talker_t *host = &g_array_index(hl->hosts, hostlist_talker_t, i);
+ get_hostlist_table_address(hl, host, entries);
+ gtk_list_store_set (store, &host->iter,
+ ADR_COLUMN, entries[0],
+ PORT_COLUMN, entries[1],
+ -1);
+ }
+ gtk_tree_view_set_model(GTK_TREE_VIEW(hl->table), GTK_TREE_MODEL(store));
+ g_object_unref(store);
+}
+
+
+static void
+draw_hostlist_table_data(hostlist_table *hl)
+{
+ guint32 i;
+ char title[256];
+ GtkListStore *store;
+ gboolean first = TRUE;
+
+ if (hl->page_lb) {
+ if(hl->num_hosts) {
+ g_snprintf(title, sizeof(title), "%s: %u", hl->name, hl->num_hosts);
+ } else {
+ g_snprintf(title, sizeof(title), "%s", hl->name);
+ }
+ gtk_label_set_text(GTK_LABEL(hl->page_lb), title);
+ gtk_widget_set_sensitive(hl->page_lb, hl->num_hosts);
+ } else {
+ if(hl->num_hosts) {
+ g_snprintf(title, sizeof(title), "%s Endpoints: %u", hl->name, hl->num_hosts);
+ } else {
+ g_snprintf(title, sizeof(title), "%s Endpoints", hl->name);
+ }
+ gtk_label_set_text(GTK_LABEL(hl->name_lb), title);
+ }
+
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(hl->table));
+ for(i=0;i<hl->num_hosts;i++){
+ hostlist_talker_t *host = &g_array_index(hl->hosts, hostlist_talker_t, i);
+
+ if (!host->modified)
+ continue;
+
+ if (first) {
+ g_object_ref(store);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(hl->table), NULL);
+
+ first = FALSE;
+ }
+ host->modified = FALSE;
+ if (!host->iter_valid) {
+ char *entries[2];
+#ifdef HAVE_GEOIP
+ char geoip[NUM_GEOIP_COLS][COL_STR_LEN];
+ guint j;
+
+ if ((host->address.type == AT_IPv4 || host->address.type == AT_IPv6) && !hl->geoip_visible) {
+ GList *columns, *list;
+ GtkTreeViewColumn *column;
+ columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(hl->table));
+ list = columns;
+ while(columns) {
+ const gchar *title_p;
+ gint id;
+
+ column = 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 >= GEOIP1_COLUMN) {
+ gtk_tree_view_column_set_visible(column, TRUE);
+ }
+ columns = g_list_next(columns);
+ }
+ g_list_free(list);
+ hl->geoip_visible = TRUE;
+ }
+
+ /* Filled in from the GeoIP config, if any */
+ for (j = 0; j < NUM_GEOIP_COLS; j++) {
+ if (host->address.type == AT_IPv4 && j < geoip_db_num_dbs()) {
+ const guchar *name = geoip_db_lookup_ipv4(j, pntohl(host->address.data), "-");
+ g_strlcpy(geoip[j], format_text (name, strlen(name)), COL_STR_LEN);
+ } else if (host->address.type == AT_IPv6 && j < geoip_db_num_dbs()) {
+ const guchar *name;
+ const struct e_in6_addr *addr = (struct e_in6_addr *) host->address.data;
+
+ name = geoip_db_lookup_ipv6(j, *addr, "-");
+ g_strlcpy(geoip[j], format_text (name, strlen(name)), COL_STR_LEN);
+ } else {
+ geoip[j][0] = 0;
+ }
+ }
+#endif /* HAVE_GEOIP */
+
+ get_hostlist_table_address(hl, host, entries);
+ host->iter_valid = TRUE;
+ gtk_list_store_insert_with_values( store , &host->iter, G_MAXINT,
+ ADR_COLUMN, entries[0],
+ PORT_COLUMN, entries[1],
+ PACKETS_COLUMN, host->tx_frames+host->rx_frames,
+ BYTES_COLUMN, host->tx_bytes+host->rx_bytes,
+ PKT_AB_COLUMN, host->tx_frames,
+ BYTES_AB_COLUMN, host->tx_bytes,
+ PKT_BA_COLUMN, host->rx_frames,
+ BYTES_BA_COLUMN, host->rx_bytes,
+#ifdef HAVE_GEOIP
+ GEOIP1_COLUMN, geoip[0],
+ GEOIP2_COLUMN, geoip[1],
+ GEOIP3_COLUMN, geoip[2],
+ GEOIP4_COLUMN, geoip[3],
+ GEOIP5_COLUMN, geoip[4],
+ GEOIP6_COLUMN, geoip[5],
+ GEOIP7_COLUMN, geoip[6],
+ GEOIP8_COLUMN, geoip[7],
+ GEOIP9_COLUMN, geoip[8],
+ GEOIP10_COLUMN, geoip[9],
+ GEOIP11_COLUMN, geoip[10],
+ GEOIP12_COLUMN, geoip[11],
+ GEOIP13_COLUMN, geoip[12],
+#endif
+ INDEX_COLUMN, i,
+ -1);
+ }
+ else {
+ gtk_list_store_set (store, &host->iter,
+ PACKETS_COLUMN, host->tx_frames+host->rx_frames,
+ BYTES_COLUMN, host->tx_bytes+host->rx_bytes,
+ PKT_AB_COLUMN, host->tx_frames,
+ BYTES_AB_COLUMN, host->tx_bytes,
+ PKT_BA_COLUMN, host->rx_frames,
+ BYTES_BA_COLUMN, host->rx_bytes,
+ -1);
+ }
+ }
+ if (!first) {
+ if (!hl->fixed_col && hl->num_hosts >= 1000) {
+ /* finding the right size for a column isn't easy
+ * let it run in autosize a little (1000 is arbitrary)
+ * and then switch to fixed width.
+ */
+ hl->fixed_col = TRUE;
+ switch_to_fixed_col(hl->table);
+ }
+
+ gtk_tree_view_set_model(GTK_TREE_VIEW(hl->table), GTK_TREE_MODEL(store));
+ g_object_unref(store);
+ }
+}
+
+static void
+draw_hostlist_table_data_cb(void *arg)
+{
+ draw_hostlist_table_data(arg);
+}
+
+typedef struct {
+ int nb_cols;
+ gint columns_order[N_COLUMNS];
+ GString *CSV_str;
+ hostlist_table *talkers;
+} csv_t;
+
+/* output in C locale */
+static gboolean
+csv_handle(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
+ gpointer data)
+{
+ csv_t *csv = (csv_t *)data;
+ gchar *table_text;
+ int i;
+ unsigned idx;
+ guint64 value;
+
+ gtk_tree_model_get(model, iter, 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 ADR_COLUMN:
+ case PORT_COLUMN:
+ 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 PACKETS_COLUMN:
+ case BYTES_COLUMN:
+ case PKT_AB_COLUMN:
+ case BYTES_AB_COLUMN:
+ case PKT_BA_COLUMN:
+ case BYTES_BA_COLUMN:
+ 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;
+ default:
+ 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;
+ }
+ }
+ g_string_append(csv->CSV_str,"\n");
+
+ return FALSE;
+}
+
+static void
+copy_as_csv_cb(GtkWindow *copy_bt, gpointer data _U_)
+{
+ GtkClipboard *cb;
+ char *savelocale;
+ GList *columns, *list;
+ GtkTreeViewColumn *column;
+ GtkListStore *store;
+ csv_t csv;
+
+ csv.talkers=g_object_get_data(G_OBJECT(copy_bt), HOST_PTR_KEY);
+ if (!csv.talkers)
+ return;
+
+ savelocale = setlocale(LC_NUMERIC, NULL);
+ setlocale(LC_NUMERIC, "C");
+ csv.CSV_str = g_string_new("");
+
+ columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(csv.talkers->table));
+ list = columns;
+ csv.nb_cols = 0;
+ while(columns) {
+ column = columns->data;
+ if (gtk_tree_view_column_get_visible(column)) {
+ csv.columns_order[csv.nb_cols] = gtk_tree_view_column_get_sort_column_id(column);
+ if (csv.nb_cols)
+ g_string_append(csv.CSV_str, ",");
+ g_string_append_printf(csv.CSV_str, "\"%s\"", gtk_tree_view_column_get_title(column));
+ csv.nb_cols++;
+ }
+ columns = g_list_next(columns);
+ }
+ g_list_free(list);
+
+ g_string_append(csv.CSV_str,"\n");
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(csv.talkers->table)));
+ gtk_tree_model_foreach(GTK_TREE_MODEL(store), csv_handle, &csv);
+
+ /* Now that we have the CSV data, copy it into the default clipboard */
+ cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); /* Get the default clipboard */
+ gtk_clipboard_set_text(cb, csv.CSV_str->str, -1); /* Copy the CSV data into the clipboard */
+ setlocale(LC_NUMERIC, savelocale);
+ g_string_free(csv.CSV_str, TRUE); /* Free the memory */
+}
+
+#ifdef HAVE_GEOIP
+typedef struct {
+ int nb_cols;
+ gint32 col_lat, col_lon, col_country, col_city, col_as_num, col_ip, col_packets, col_bytes;
+ FILE *out_file;
+ gboolean hosts_written;
+ hostlist_table *talkers;
+} map_t;
+
+/* XXX output in C locale */
+static gboolean
+map_handle(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
+ gpointer data)
+{
+ map_t *map = (map_t *)data;
+ gchar *table_entry;
+ guint64 value;
+ /* Add the column values to the TSV data */
+
+ /* check, if we have a geolocation available for this host */
+ gtk_tree_model_get(model, iter, map->col_lat, &table_entry, -1);
+ if (strcmp(table_entry, "-") == 0) {
+ g_free(table_entry);
+ return FALSE;
+ }
+
+ gtk_tree_model_get(model, iter, map->col_lon, &table_entry, -1);
+ if (strcmp(table_entry, "-") == 0) {
+ g_free(table_entry);
+ return FALSE;
+ }
+
+ /* Latitude */
+ gtk_tree_model_get(model, iter, map->col_lat, &table_entry, -1);
+ fputs(table_entry, map->out_file);
+ g_free(table_entry);
+ fputs("\t", map->out_file);
+
+ /* Longitude */
+ gtk_tree_model_get(model, iter, map->col_lon, &table_entry, -1);
+ fputs(table_entry, map->out_file);
+ g_free(table_entry);
+ fputs("\t", map->out_file);
+
+ /* Title */
+ gtk_tree_model_get(model, iter, map->col_ip, &table_entry, -1);
+ fputs(table_entry, map->out_file);
+ g_free(table_entry);
+ fputs("\t", map->out_file);
+
+ /* Description */
+ if (map->col_as_num >= 0) {
+ gtk_tree_model_get(model, iter, map->col_as_num, &table_entry, -1);
+ fputs("AS: ", map->out_file);
+ fputs(table_entry, map->out_file);
+ g_free(table_entry);
+ fputs("<br/>", map->out_file);
+ }
+
+ if (map->col_country >= 0) {
+ gtk_tree_model_get(model, iter, map->col_country, &table_entry, -1);
+ fputs("Country: ", map->out_file);
+ fputs(table_entry, map->out_file);
+ g_free(table_entry);
+ fputs("<br/>", map->out_file);
+ }
+
+ if (map->col_country >= 0) {
+ gtk_tree_model_get(model, iter, map->col_city, &table_entry, -1);
+ fputs("City: ", map->out_file);
+ fputs(table_entry, map->out_file);
+ g_free(table_entry);
+ fputs("<br/>", map->out_file);
+ }
+
+ gtk_tree_model_get(model, iter, map->col_packets, &value, -1);
+ fprintf(map->out_file, "Packets: %" G_GINT64_MODIFIER "u<br/>", value);
+
+ gtk_tree_model_get(model, iter, map->col_bytes, &value, -1);
+ fprintf(map->out_file, "Bytes: %" G_GINT64_MODIFIER "u\t", value);
+
+ /* XXX - we could add specific icons, e.g. depending on the amount of packets or bytes */
+
+ fputs("\n", map->out_file); /* new row */
+ map->hosts_written = TRUE;
+
+ return FALSE;
+}
+
+static void
+open_as_map_cb(GtkWindow *copy_bt, gpointer data _U_)
+{
+ guint32 i;
+ gchar *file_uri;
+ gboolean uri_open;
+ char *map_path, *map_data_filename;
+ char *src_file_path;
+ char *dst_file_path;
+ GList *columns, *list;
+ GtkTreeViewColumn *column;
+ GtkListStore *store;
+ map_t map;
+
+ map.talkers =g_object_get_data(G_OBJECT(copy_bt), HOST_PTR_KEY);
+ if (!map.talkers)
+ return;
+
+ map.col_lat = map.col_lon = map.col_country = map.col_city = map.col_as_num = map.col_ip = map.col_packets = map.col_bytes = -1;
+ map.hosts_written = FALSE;
+ /* Find the interesting columns */
+ columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(map.talkers->table));
+ list = columns;
+ map.nb_cols = 0;
+ while(columns) {
+ column = columns->data;
+ i = gtk_tree_view_column_get_sort_column_id(column);
+ if(strcmp(map.talkers->default_titles[i], "Latitude") == 0) {
+ map.col_lat = i;
+ map.nb_cols++;
+ }
+ if(strcmp(map.talkers->default_titles[i], "Longitude") == 0) {
+ map.col_lon = i;
+ map.nb_cols++;
+ }
+ if(strcmp(map.talkers->default_titles[i], "Country") == 0) {
+ map.col_country = i;
+ map.nb_cols++;
+ }
+ if(strcmp(map.talkers->default_titles[i], "City") == 0) {
+ map.col_city = i;
+ map.nb_cols++;
+ }
+ if(strcmp(map.talkers->default_titles[i], "AS Number") == 0) {
+ map.col_as_num = i;
+ }
+ if(strcmp(map.talkers->default_titles[i], "Address") == 0) {
+ map.col_ip = i;
+ map.nb_cols++;
+ }
+ if(strcmp(map.talkers->default_titles[i], "Packets") == 0) {
+ map.col_packets = i;
+ map.nb_cols++;
+ }
+ if(strcmp(map.talkers->default_titles[i], "Bytes") == 0) {
+ map.col_bytes = i;
+ map.nb_cols++;
+ }
+ columns = g_list_next(columns);
+ }
+ g_list_free(list);
+
+ /* check for the minimum required data */
+ if(map.col_lat == -1 || map.col_lon == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Latitude/Longitude data not available (GeoIP installed?)");
+ return;
+ }
+
+ /* open the TSV output file */
+ /* XXX - add error handling */
+ if (! create_tempdir(&map_path, "Wireshark IP Map ")) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Could not create temporary directory\n%s",
+ map_path);
+ return;
+ }
+ map_data_filename = g_strdup_printf("%s%cipmap.txt", map_path, G_DIR_SEPARATOR);
+ map.out_file = ws_fopen(map_data_filename, "w");
+ if(map.out_file == NULL) {
+ open_failure_alert_box(map_data_filename, errno, TRUE);
+ return;
+ }
+
+ fputs("lat\tlon\ttitle\tdescription\t\n", map.out_file);
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(map.talkers->table)));
+ gtk_tree_model_foreach(GTK_TREE_MODEL(store), map_handle, &map);
+
+
+ fclose(map.out_file);
+
+ if(!map.hosts_written) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No latitude/longitude data found");
+ return;
+ }
+
+ /* copy ipmap.html to temp dir */
+ src_file_path = get_datafile_path("ipmap.html");
+ dst_file_path = g_strdup_printf("%s%cipmap.html", map_path, G_DIR_SEPARATOR);
+
+ if (!copy_file_binary_mode(src_file_path, dst_file_path)) {
+ g_free(src_file_path);
+ g_free(dst_file_path);
+ return;
+ }
+ g_free(src_file_path);
+
+ /* open the webbrowser */
+ file_uri = filename2uri(dst_file_path);
+ g_free(dst_file_path);
+ uri_open = browser_open_url (file_uri);
+ if(!uri_open) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Couldn't open the file: \"%s\" in your web browser", file_uri);
+ g_free(file_uri);
+ return;
+ }
+
+ g_free(file_uri);
+}
+#endif /* HAVE_GEOIP */
+
+static gint default_col_size[N_COLUMNS];
+
+static void
+init_default_col_size(GtkWidget *view)
+{
+
+ default_col_size[ADR_COLUMN] = get_default_col_size(view, "00000000.000000000000");
+ default_col_size[PORT_COLUMN] = get_default_col_size(view, "000000");
+ default_col_size[PACKETS_COLUMN] = get_default_col_size(view, "00 000 000");
+ default_col_size[BYTES_COLUMN] = get_default_col_size(view, "0 000 000 000");
+ default_col_size[PKT_AB_COLUMN] = default_col_size[PACKETS_COLUMN];
+ default_col_size[PKT_BA_COLUMN] = default_col_size[PACKETS_COLUMN];
+ default_col_size[BYTES_AB_COLUMN] = default_col_size[BYTES_COLUMN];
+ default_col_size[BYTES_BA_COLUMN] = default_col_size[BYTES_COLUMN];
+#ifdef HAVE_GEOIP
+ default_col_size[GEOIP1_COLUMN] = default_col_size[ADR_COLUMN];
+ default_col_size[GEOIP2_COLUMN] = default_col_size[GEOIP1_COLUMN];
+ default_col_size[GEOIP3_COLUMN] = default_col_size[GEOIP1_COLUMN];
+ default_col_size[GEOIP4_COLUMN] = default_col_size[GEOIP1_COLUMN];
+ default_col_size[GEOIP5_COLUMN] = default_col_size[GEOIP1_COLUMN];
+ default_col_size[GEOIP6_COLUMN] = default_col_size[GEOIP1_COLUMN];
+ default_col_size[GEOIP7_COLUMN] = default_col_size[GEOIP1_COLUMN];
+ default_col_size[GEOIP8_COLUMN] = default_col_size[GEOIP1_COLUMN];
+ default_col_size[GEOIP9_COLUMN] = default_col_size[GEOIP1_COLUMN];
+ default_col_size[GEOIP10_COLUMN] = default_col_size[GEOIP1_COLUMN];
+ default_col_size[GEOIP11_COLUMN] = default_col_size[GEOIP1_COLUMN];
+ default_col_size[GEOIP12_COLUMN] = default_col_size[GEOIP1_COLUMN];
+ default_col_size[GEOIP13_COLUMN] = default_col_size[GEOIP1_COLUMN];
+
+#endif
+}
+
+static gboolean
+init_hostlist_table_page(hostlist_table *hosttable, GtkWidget *vbox, gboolean hide_ports, const char *table_name, const char *tap_name,
+ const char *filter, tap_packet_cb packet_func)
+{
+ guint i;
+ GString *error_string;
+ char title[256];
+ GtkListStore *store;
+ GtkWidget *tree;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSortable *sortable;
+ GtkTreeSelection *sel;
+ static gboolean col_size = FALSE;
+
+ hosttable->default_titles[0] = "Address";
+ hosttable->default_titles[1] = "Port";
+ hosttable->default_titles[2] = "Packets";
+ hosttable->default_titles[3] = "Bytes";
+ hosttable->default_titles[4] = "Tx Packets";
+ hosttable->default_titles[5] = "Tx Bytes";
+ hosttable->default_titles[6] = "Rx Packets";
+ hosttable->default_titles[7] = "Rx Bytes";
+
+#ifdef HAVE_GEOIP
+ for (i = 0; i < NUM_GEOIP_COLS; i++) {
+ if (i < geoip_db_num_dbs()) {
+ hosttable->default_titles[NUM_BUILTIN_COLS + i] = geoip_db_name(i);
+ } else {
+ hosttable->default_titles[NUM_BUILTIN_COLS + i] = "";
+ }
+ }
+#endif /* HAVE_GEOIP */
+
+ if (strcmp(table_name, "NCP")==0) {
+ hosttable->default_titles[1] = "Connection";
+ }
+
+ hosttable->has_ports=!hide_ports;
+ hosttable->num_hosts = 0;
+ hosttable->resolve_names=TRUE;
+ hosttable->page_lb = NULL;
+ hosttable->fixed_col = FALSE;
+ hosttable->geoip_visible = FALSE;
+
+ g_snprintf(title, sizeof(title), "%s Endpoints", table_name);
+ hosttable->name_lb = gtk_label_new(title);
+ gtk_box_pack_start(GTK_BOX(vbox), hosttable->name_lb, FALSE, FALSE, 0);
+
+ /* Create the store */
+ store = gtk_list_store_new (N_COLUMNS, /* Total number of columns */
+ G_TYPE_STRING, /* Address */
+ G_TYPE_STRING, /* Port */
+ G_TYPE_UINT64, /* Packets */
+ G_TYPE_UINT64, /* Bytes */
+ G_TYPE_UINT64, /* Packets A->B */
+ G_TYPE_UINT64, /* Bytes A->B */
+ G_TYPE_UINT64, /* Packets A<-B */
+ G_TYPE_UINT64, /* Bytes A<-B */
+#ifdef HAVE_GEOIP
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+#endif
+ G_TYPE_UINT); /* Index */
+
+ hosttable->scrolled_window=scrolled_window_new(NULL, NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), hosttable->scrolled_window, TRUE, TRUE, 0);
+ tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
+ hosttable->table = GTK_TREE_VIEW(tree);
+ sortable = GTK_TREE_SORTABLE(store);
+ g_object_unref (G_OBJECT (store));
+
+ if (!col_size) {
+ col_size = TRUE;
+ init_default_col_size(GTK_WIDGET(hosttable->table));
+ }
+
+ 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 < N_COLUMNS -1; i++) {
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set(renderer, "ypad", 0, NULL);
+ switch(i) {
+ case 0: /* address and port */
+ case 1:
+ column = gtk_tree_view_column_new_with_attributes (hosttable->default_titles[i], renderer, "text",
+ i, NULL);
+ if(hide_ports && i == 1){
+ /* 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 2: /* counts */
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7: /* 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);
+ break;
+ default: /* GEOIP */
+ column = gtk_tree_view_column_new_with_attributes (hosttable->default_titles[i], renderer, "text",
+ 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 (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);
+ }
+ }
+#endif
+ break;
+ }
+ gtk_tree_view_column_set_sort_column_id(column, i);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_reorderable(column, TRUE);
+ gtk_tree_view_column_set_min_width(column, 40);
+ gtk_tree_view_column_set_fixed_width(column, default_col_size[i]);
+ gtk_tree_view_append_column (hosttable->table, column);
+
+#if 0
+ /* make total frames be the default sort order, too slow */
+ if (i == PACKETS_COLUMN) {
+ gtk_tree_view_column_clicked(column);
+ }
+#endif
+ }
+
+ gtk_container_add(GTK_CONTAINER(hosttable->scrolled_window), (GtkWidget *)hosttable->table);
+
+ hosttable->num_hosts=0;
+ hosttable->hosts=NULL;
+ hosttable->hashtable=NULL;
+
+ gtk_tree_view_set_rules_hint(hosttable->table, TRUE);
+ gtk_tree_view_set_headers_clickable(hosttable->table, TRUE);
+ gtk_tree_view_set_reorderable (hosttable->table, TRUE);
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hosttable->table));
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+
+ /* create popup menu for this table */
+ hostlist_create_popup_menu(hosttable);
+
+ /* register the tap and rerun the taps on the packet list */
+ error_string=register_tap_listener(tap_name, hosttable, filter, 0, reset_hostlist_table_data_cb, packet_func, draw_hostlist_table_data_cb);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(hosttable);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+void
+init_hostlist_table(gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, tap_packet_cb packet_func)
+{
+ hostlist_table *hosttable;
+ char title[256];
+ GtkWidget *vbox;
+ GtkWidget *bbox;
+ GtkWidget *close_bt, *help_bt;
+ gboolean ret;
+ GtkWidget *copy_bt;
+#ifdef HAVE_GEOIP
+ GtkWidget *map_bt;
+#endif
+
+ hosttable=g_malloc(sizeof(hostlist_table));
+
+ hosttable->name=table_name;
+ hosttable->filter=filter;
+ hosttable->use_dfilter=FALSE;
+ g_snprintf(title, sizeof(title), "%s Endpoints: %s", table_name, cf_get_display_name(&cfile));
+ hosttable->win = dlg_window_new(title); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(hosttable->win), TRUE);
+
+ gtk_window_set_default_size(GTK_WINDOW(hosttable->win), 750, 400);
+
+ vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(hosttable->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ ret = init_hostlist_table_page(hosttable, vbox, hide_ports, table_name, tap_name, filter, packet_func);
+ if(ret == FALSE) {
+ g_free(hosttable);
+ return;
+ }
+
+ /* Button row. */
+ /* XXX - maybe we want to have a "Copy as CSV" stock button here? */
+ /*copy_bt = gtk_button_new_with_label ("Copy content to clipboard as CSV");*/
+#ifdef HAVE_GEOIP
+ if( strstr(table_name, "IPv4") != NULL) {
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_COPY, WIRESHARK_STOCK_MAP, GTK_STOCK_HELP, NULL);
+ } else {
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_COPY, GTK_STOCK_HELP, NULL);
+ }
+#else
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_COPY, GTK_STOCK_HELP, NULL);
+#endif
+
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(hosttable->win, close_bt, window_cancel_button_cb);
+
+ copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY);
+ gtk_widget_set_tooltip_text(copy_bt, "Copy all statistical values of this page to the clipboard in CSV (Comma Separated Values) format.");
+ g_object_set_data(G_OBJECT(copy_bt), HOST_PTR_KEY, hosttable);
+ g_signal_connect(copy_bt, "clicked", G_CALLBACK(copy_as_csv_cb), NULL);
+
+#ifdef HAVE_GEOIP
+ map_bt = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_MAP);
+ if(map_bt != NULL) {
+ gtk_widget_set_tooltip_text(map_bt, "Show a map of the IP addresses (internet connection required).");
+ g_object_set_data(G_OBJECT(map_bt), HOST_PTR_KEY, hosttable);
+ g_signal_connect(map_bt, "clicked", G_CALLBACK(open_as_map_cb), NULL);
+ }
+#endif /* HAVE_GEOIP */
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_ENDPOINTS_DIALOG);
+
+ g_signal_connect(hosttable->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(hosttable->win, "destroy", G_CALLBACK(hostlist_win_destroy_cb), hosttable);
+
+ gtk_widget_show_all(hosttable->win);
+ window_present(hosttable->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(hosttable->win));
+}
+
+
+static void
+ct_nb_switch_page_cb(GtkNotebook *nb, gpointer *pg _U_, guint page, gpointer data)
+{
+ GtkWidget *copy_bt = (GtkWidget *) data;
+ void ** pages = g_object_get_data(G_OBJECT(nb), NB_PAGES_KEY);
+
+ page++;
+
+ if (pages && page > 0 && (int) page <= GPOINTER_TO_INT(pages[0]) && copy_bt) {
+ g_object_set_data(G_OBJECT(copy_bt), HOST_PTR_KEY, pages[page]);
+ }
+}
+
+#ifdef HAVE_GEOIP
+static void
+ct_nb_map_switch_page_cb(GtkNotebook *nb, gpointer *pg _U_, guint page, gpointer data)
+{
+ GtkWidget *map_bt = (GtkWidget *) data;
+ void ** pages = g_object_get_data(G_OBJECT(nb), NB_PAGES_KEY);
+
+ page++;
+
+ if (pages && page > 0 && (int) page <= GPOINTER_TO_INT(pages[0]) && map_bt) {
+ g_object_set_data(G_OBJECT(map_bt), HOST_PTR_KEY, pages[page]);
+ if(strstr( ((hostlist_table *)pages[page])->name, "IPv4") != NULL) {
+ gtk_widget_set_sensitive(map_bt, TRUE);
+ } else {
+ gtk_widget_set_sensitive(map_bt, FALSE);
+ }
+ }
+}
+#endif /* HAVE_GEOIP */
+
+
+static void
+hostlist_win_destroy_notebook_cb(GtkWindow *win _U_, gpointer data)
+{
+ void ** pages = data;
+ int page;
+
+ /* first "page" contains the number of pages */
+ for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) {
+ hostlist_win_destroy_cb(NULL, pages[page]);
+ }
+ g_free(pages);
+}
+
+
+
+
+static hostlist_table *
+init_hostlist_notebook_page_cb(gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter,
+ tap_packet_cb packet_func)
+{
+ gboolean ret;
+ GtkWidget *page_vbox;
+ hostlist_table *hosttable;
+
+ hosttable=g_malloc(sizeof(hostlist_table));
+ hosttable->name=table_name;
+ hosttable->filter=filter;
+ hosttable->use_dfilter=FALSE;
+
+ page_vbox=gtk_vbox_new(FALSE, 6);
+ hosttable->win = page_vbox;
+ gtk_container_set_border_width(GTK_CONTAINER(page_vbox), 6);
+
+ ret = init_hostlist_table_page(hosttable, page_vbox, hide_ports, table_name, tap_name, filter, packet_func);
+ if(ret == FALSE) {
+ g_free(hosttable);
+ return NULL;
+ }
+
+ return hosttable;
+}
+
+
+typedef struct {
+ gboolean hide_ports; /* hide TCP / UDP port columns */
+ const char *table_name; /* GUI output name */
+ const char *tap_name; /* internal name */
+ const char *filter; /* display filter string (unused) */
+ tap_packet_cb packet_func; /* function to be called for new incoming packets */
+} register_hostlist_t;
+
+
+static GSList *registered_hostlist_tables = NULL;
+
+void
+register_hostlist_table(gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, tap_packet_cb packet_func)
+{
+ register_hostlist_t *table;
+
+ table = g_malloc(sizeof(register_hostlist_t));
+
+ table->hide_ports = hide_ports;
+ table->table_name = table_name;
+ table->tap_name = tap_name;
+ table->filter = filter;
+ table->packet_func = packet_func;
+
+ registered_hostlist_tables = g_slist_append(registered_hostlist_tables, table);
+}
+
+
+static void
+hostlist_resolve_toggle_dest(GtkWidget *widget, gpointer data)
+{
+ int page;
+ void ** pages = data;
+ gboolean resolve_names;
+ hostlist_table *hosttable;
+
+
+ resolve_names = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
+
+ for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) {
+ hosttable = pages[page];
+ hosttable->resolve_names = resolve_names;
+ draw_hostlist_table_addresses(hosttable);
+ }
+}
+
+
+static void
+hostlist_filter_toggle_dest(GtkWidget *widget, gpointer data)
+{
+ int page;
+ void ** pages = data;
+ gboolean use_filter;
+ hostlist_table *hosttable = NULL;
+
+ use_filter = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
+
+ for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) {
+ hosttable = pages[page];
+ hosttable->use_dfilter = use_filter;
+ reset_hostlist_table_data(hosttable);
+ }
+
+ cf_retap_packets(&cfile);
+ if (hosttable) {
+ gdk_window_raise(gtk_widget_get_window(hosttable->win));
+ }
+}
+
+
+void
+init_hostlist_notebook_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ hostlist_table *hosttable;
+ char title[256];
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *bbox;
+ GtkWidget *close_bt, *help_bt;
+ GtkWidget *win;
+ GtkWidget *resolv_cb;
+ GtkWidget *filter_cb;
+ int page;
+ void ** pages;
+ GtkWidget *nb;
+ GtkWidget *page_lb;
+ GSList *current_table;
+ register_hostlist_t *registered;
+ GtkWidget *copy_bt;
+#ifdef HAVE_GEOIP
+ GtkWidget *map_bt;
+#endif
+
+
+ pages = g_malloc(sizeof(void *) * (g_slist_length(registered_hostlist_tables) + 1));
+
+ win = dlg_window_new("hostlist"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(win), TRUE);
+
+ g_snprintf(title, sizeof(title), "Endpoints: %s", cf_get_display_name(&cfile));
+ gtk_window_set_title(GTK_WINDOW(win), title);
+ gtk_window_set_default_size(GTK_WINDOW(win), 750, 400);
+
+ vbox=gtk_vbox_new(FALSE, 6);
+ gtk_container_add(GTK_CONTAINER(win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ nb = gtk_notebook_new();
+ gtk_container_add(GTK_CONTAINER(vbox), nb);
+ g_object_set_data(G_OBJECT(nb), NB_PAGES_KEY, pages);
+
+ page = 0;
+
+ current_table = registered_hostlist_tables;
+ while(current_table) {
+ registered = current_table->data;
+ page_lb = gtk_label_new("");
+ hosttable = init_hostlist_notebook_page_cb(registered->hide_ports, registered->table_name, registered->tap_name,
+ registered->filter, registered->packet_func);
+ g_object_set_data(G_OBJECT(hosttable->win), HOST_PTR_KEY, hosttable);
+ gtk_notebook_append_page(GTK_NOTEBOOK(nb), hosttable->win, page_lb);
+ hosttable->win = win;
+ hosttable->page_lb = page_lb;
+ pages[++page] = hosttable;
+
+ current_table = g_slist_next(current_table);
+ }
+
+ pages[0] = GINT_TO_POINTER(page);
+
+ hbox = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+ resolv_cb = gtk_check_button_new_with_mnemonic("Name resolution");
+ gtk_container_add(GTK_CONTAINER(hbox), resolv_cb);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolv_cb), TRUE);
+ gtk_widget_set_tooltip_text(resolv_cb,
+ "Show results of name resolutions rather than the \"raw\" values. Please note: The corresponding name resolution must be enabled.");
+
+ g_signal_connect(resolv_cb, "toggled", G_CALLBACK(hostlist_resolve_toggle_dest), pages);
+
+ filter_cb = gtk_check_button_new_with_mnemonic("Limit to display filter");
+ gtk_container_add(GTK_CONTAINER(hbox), filter_cb);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(filter_cb), FALSE);
+ gtk_widget_set_tooltip_text(filter_cb, "Limit the list to endpoints matching the current display filter.");
+
+ g_signal_connect(filter_cb, "toggled", G_CALLBACK(hostlist_filter_toggle_dest), pages);
+
+ /* Button row. */
+#ifdef HAVE_GEOIP
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_COPY, WIRESHARK_STOCK_MAP, GTK_STOCK_HELP, NULL);
+#else
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_COPY, GTK_STOCK_HELP, NULL);
+#endif
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(win, close_bt, window_cancel_button_cb);
+
+ copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY);
+ gtk_widget_set_tooltip_text(copy_bt, "Copy all statistical values of this page to the clipboard in CSV (Comma Separated Values) format.");
+ g_signal_connect(copy_bt, "clicked", G_CALLBACK(copy_as_csv_cb), NULL);
+ g_object_set_data(G_OBJECT(copy_bt), HOST_PTR_KEY, pages[page]);
+
+#ifdef HAVE_GEOIP
+ map_bt = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_MAP);
+ gtk_widget_set_tooltip_text(map_bt, "Show a map of the IP addresses (internet connection required).");
+ g_object_set_data(G_OBJECT(map_bt), HOST_PTR_KEY, pages[page]);
+ g_signal_connect(map_bt, "clicked", G_CALLBACK(open_as_map_cb), NULL);
+ g_signal_connect(nb, "switch-page", G_CALLBACK(ct_nb_map_switch_page_cb), map_bt);
+ gtk_widget_set_sensitive(map_bt, FALSE);
+#endif /* HAVE_GEOIP */
+
+ g_signal_connect(nb, "switch-page", G_CALLBACK(ct_nb_switch_page_cb), copy_bt);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_ENDPOINTS_DIALOG);
+
+ g_signal_connect(win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(win, "destroy", G_CALLBACK(hostlist_win_destroy_notebook_cb), pages);
+
+ gtk_widget_show_all(win);
+ window_present(win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(win));
+}
+
+/*
+ * Compute the hash value for a given address/port pairs if the match
+ * is to be exact.
+ */
+typedef struct {
+ address address;
+ guint32 port;
+} host_key_t;
+
+static guint
+host_hash(gconstpointer v)
+{
+ const host_key_t *key = (const host_key_t *)v;
+ guint hash_val;
+
+ hash_val = 0;
+ ADD_ADDRESS_TO_HASH(hash_val, &key->address);
+ hash_val += key->port;
+ return hash_val;
+}
+
+/*
+ * Compare two host keys for an exact match.
+ */
+static gint
+host_match(gconstpointer v, gconstpointer w)
+{
+ const host_key_t *v1 = (const host_key_t *)v;
+ const host_key_t *v2 = (const host_key_t *)w;
+
+ if (v1->port == v2->port &&
+ ADDRESSES_EQUAL(&v1->address, &v2->address)) {
+ return 1;
+ }
+ /*
+ * The addresses or the ports don't match.
+ */
+ return 0;
+}
+
+void
+add_hostlist_table_data(hostlist_table *hl, const address *addr, guint32 port, gboolean sender, int num_frames, int num_bytes, SAT_E sat, int port_type_val)
+{
+ hostlist_talker_t *talker=NULL;
+ int talker_idx=0;
+
+ /* XXX should be optimized to allocate n extra entries at a time
+ instead of just one */
+ /* if we dont have any entries at all yet */
+ if(hl->hosts==NULL){
+ hl->hosts=g_array_sized_new(FALSE, FALSE, sizeof(hostlist_talker_t), 10000);
+ hl->hashtable = g_hash_table_new_full(host_hash,
+ host_match, /* key_equal_func */
+ g_free, /* key_destroy_func */
+ NULL); /* value_destroy_func */
+ }
+ else {
+ /* try to find it among the existing known conversations */
+ host_key_t existing_key;
+
+ existing_key.address = *addr;
+ existing_key.port = port;
+ talker_idx = GPOINTER_TO_UINT(g_hash_table_lookup(hl->hashtable, &existing_key));
+ if (talker_idx) {
+ talker_idx--;
+ talker=&g_array_index(hl->hosts, hostlist_talker_t, talker_idx);
+ }
+ }
+
+ /* if we still dont know what talker this is it has to be a new one
+ and we have to allocate it and append it to the end of the list */
+ if(talker==NULL){
+ host_key_t *new_key;
+ hostlist_talker_t host;
+
+ COPY_ADDRESS(&host.address, addr);
+ host.sat=sat;
+ host.port_type=port_type_val;
+ host.port=port;
+ host.rx_frames=0;
+ host.tx_frames=0;
+ host.rx_bytes=0;
+ host.tx_bytes=0;
+ host.iter_valid = FALSE;
+ host.modified = TRUE;
+
+ g_array_append_val(hl->hosts, host);
+ talker_idx= hl->num_hosts;
+ talker=&g_array_index(hl->hosts, hostlist_talker_t, talker_idx);
+
+ /* hl->hosts address is not a constant but address.data is */
+ new_key = g_malloc(sizeof (host_key_t));
+ SET_ADDRESS(&new_key->address, talker->address.type, talker->address.len, talker->address.data);
+ new_key->port = port;
+ g_hash_table_insert(hl->hashtable, new_key, GUINT_TO_POINTER(talker_idx +1));
+ hl->num_hosts++;
+ }
+
+ /* if this is a new talker we need to initialize the struct */
+ talker->modified = TRUE;
+
+ /* update the talker struct */
+ if( sender ){
+ talker->tx_frames+=num_frames;
+ talker->tx_bytes+=num_bytes;
+ } else {
+ talker->rx_frames+=num_frames;
+ talker->rx_bytes+=num_bytes;
+ }
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/gtk/hostlist_table.h b/ui/gtk/hostlist_table.h
new file mode 100644
index 0000000000..a41119a90e
--- /dev/null
+++ b/ui/gtk/hostlist_table.h
@@ -0,0 +1,123 @@
+/* hostlist_table.h 2004 Ian Schorr
+ * modified from endpoint_talkers_table 2003 Ronnie Sahlberg
+ * Helper routines common to all host talkers taps.
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __HOSTLIST_TABLE_H__
+#define __HOSTLIST_TABLE_H__
+
+#include "sat.h"
+
+/** @file
+ * Hostlist definitions.
+ */
+
+/** Hostlist information */
+typedef struct _hostlist_talker_t {
+ address address; /**< address */
+ SAT_E sat; /**< address type */
+ guint32 port_type; /**< port_type (e.g. PT_TCP) */
+ guint32 port; /**< port */
+
+ guint64 rx_frames; /**< number of received packets */
+ guint64 tx_frames; /**< number of transmitted packets */
+ guint64 rx_bytes; /**< number of received bytes */
+ guint64 tx_bytes; /**< number of transmitted bytes */
+
+ gboolean modified; /**< new to redraw the row */
+ GtkTreeIter iter;
+ gboolean iter_valid; /**< not a new row */
+
+} hostlist_talker_t;
+
+#define NUM_BUILTIN_COLS 8
+#ifdef HAVE_GEOIP
+# define NUM_GEOIP_COLS 13
+#else
+# define NUM_GEOIP_COLS 0
+#endif
+#define NUM_HOSTLIST_COLS (NUM_BUILTIN_COLS + NUM_GEOIP_COLS)
+
+/** Hostlist widget */
+typedef struct _hostlist_table {
+ const char *name; /**< the name of the table */
+ const char *filter; /**< the filter used */
+ gboolean use_dfilter; /**< use display filter */
+ GtkWidget *win; /**< GTK window */
+ GtkWidget *page_lb; /**< page label */
+ 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 */
+ GtkWidget *menu; /**< context menu */
+ gboolean has_ports; /**< table has ports */
+ guint32 num_hosts; /**< number of hosts (0 or 1) */
+ GArray *hosts; /**< array of host values */
+ GHashTable *hashtable; /**< conversations hash table */
+ gboolean fixed_col; /**< if switched to fixed column */
+ gboolean resolve_names; /**< resolve address names? */
+ gboolean geoip_visible; /**< if geoip columns are visible */
+} hostlist_table;
+
+/** Register the hostlist table for the multiple hostlist window.
+ *
+ * @param hide_ports hide the port columns
+ * @param table_name the table name to be displayed
+ * @param tap_name the registered tap name
+ * @param filter the optional filter name or NULL
+ * @param packet_func the function to be called for each incoming packet
+ */
+extern void register_hostlist_table(gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, tap_packet_cb packet_func);
+
+/** Init the hostlist table for the single hostlist window.
+ *
+ * @param hide_ports hide the port columns
+ * @param table_name the table name to be displayed
+ * @param tap_name the registered tap name
+ * @param filter the optional filter name or NULL
+ * @param packet_func the function to be called for each incoming packet
+ */
+extern void init_hostlist_table(gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, tap_packet_cb packet_func);
+
+/** Callback for "Endpoints" statistics item.
+ *
+ * @param w unused
+ * @param d unused
+ */
+extern void init_hostlist_notebook_cb(GtkWidget *w, gpointer d);
+
+/** Add some data to the table.
+ *
+ * @param hl the table to add the data to
+ * @param addr address
+ * @param port port
+ * @param sender TRUE, if this is a sender
+ * @param num_frames number of packets
+ * @param num_bytes number of bytes
+ * @param sat address type
+ * @param port_type the port type (e.g. PT_TCP)
+ */
+void add_hostlist_table_data(hostlist_table *hl, const address *addr,
+ guint32 port, gboolean sender, int num_frames, int num_bytes, SAT_E sat, int port_type);
+
+#endif /* __HOSTLIST_TABLE_H__ */
diff --git a/ui/gtk/hostlist_tcpip.c b/ui/gtk/hostlist_tcpip.c
new file mode 100644
index 0000000000..e7aec10e9a
--- /dev/null
+++ b/ui/gtk/hostlist_tcpip.c
@@ -0,0 +1,89 @@
+/* hostlist_tcpip.c 2004 Ian Schorr
+ * modified from endpoint_talkers_tcpip.c 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/packet.h"
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-tcp.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/hostlist_table.h"
+
+static int
+tcpip_hostlist_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ hostlist_table *hosts=(hostlist_table *)pit;
+ const struct tcpheader *tcphdr=vip;
+
+ /* Take two "add" passes per packet, adding for each direction, ensures that all
+ packets are counted properly (even if address is sending to itself)
+ XXX - this could probably be done more efficiently inside hostlist_table */
+ add_hostlist_table_data(hosts, &tcphdr->ip_src, tcphdr->th_sport, TRUE, 1, pinfo->fd->pkt_len, SAT_NONE, PT_TCP);
+ add_hostlist_table_data(hosts, &tcphdr->ip_dst, tcphdr->th_dport, FALSE, 1, pinfo->fd->pkt_len, SAT_NONE, PT_TCP);
+
+ return 1;
+}
+
+
+
+static void
+gtk_tcpip_hostlist_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"endpoints,tcp,",14)){
+ filter=optarg+14;
+ } else {
+ filter=NULL;
+ }
+
+ init_hostlist_table(FALSE, "TCP", "tcp", filter, tcpip_hostlist_packet);
+
+}
+
+void
+gtk_tcpip_hostlist_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ gtk_tcpip_hostlist_init("endpoints,tcp",NULL);
+}
+
+void
+register_tap_listener_tcpip_hostlist(void)
+{
+ register_stat_cmd_arg("endpoints,tcp", gtk_tcpip_hostlist_init,NULL);
+ register_hostlist_table(FALSE, "TCP", "tcp", NULL /*filter*/, tcpip_hostlist_packet);
+}
diff --git a/ui/gtk/hostlist_tr.c b/ui/gtk/hostlist_tr.c
new file mode 100644
index 0000000000..a9ce34a13d
--- /dev/null
+++ b/ui/gtk/hostlist_tr.c
@@ -0,0 +1,89 @@
+/* hostlist_tr.c 2004 Ian Schorr
+ * modified from endpoint_talkers_tr.c 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/packet.h"
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-tr.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/hostlist_table.h"
+
+static int
+tr_hostlist_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ hostlist_table *hosts=(hostlist_table *)pit;
+ const tr_hdr *trhdr=vip;
+
+ /* Take two "add" passes per packet, adding for each direction, ensures that all
+ packets are counted properly (even if address is sending to itself)
+ XXX - this could probably be done more efficiently inside hostlist_table */
+ add_hostlist_table_data(hosts, &trhdr->src, 0, TRUE, 1, pinfo->fd->pkt_len, SAT_TOKENRING, PT_NONE);
+ add_hostlist_table_data(hosts, &trhdr->dst, 0, FALSE, 1, pinfo->fd->pkt_len, SAT_TOKENRING, PT_NONE);
+
+ return 1;
+}
+
+
+
+static void
+gtk_tr_hostlist_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"hosts,tr,",9)){
+ filter=optarg+9;
+ } else {
+ filter=NULL;
+ }
+
+ init_hostlist_table(TRUE, "Token Ring", "tr", filter, tr_hostlist_packet);
+
+}
+
+void
+gtk_tr_hostlist_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ gtk_tr_hostlist_init("hosts,tr",NULL);
+}
+
+void
+register_tap_listener_tr_hostlist(void)
+{
+ register_stat_cmd_arg("hosts,tr", gtk_tr_hostlist_init,NULL);
+ register_hostlist_table(TRUE, "Token Ring", "tr", NULL /*filter*/, tr_hostlist_packet);
+}
diff --git a/ui/gtk/hostlist_udpip.c b/ui/gtk/hostlist_udpip.c
new file mode 100644
index 0000000000..4d88452bf6
--- /dev/null
+++ b/ui/gtk/hostlist_udpip.c
@@ -0,0 +1,89 @@
+/* hostlist_udpip.c 2004 Ian Schorr
+ * modified from endpoint_talkers_udpip.c 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/packet.h"
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-udp.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/hostlist_table.h"
+
+static int
+udpip_hostlist_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ hostlist_table *hosts=(hostlist_table *)pit;
+ const e_udphdr *udphdr=vip;
+
+ /* Take two "add" passes per packet, adding for each direction, ensures that all
+ packets are counted properly (even if address is sending to itself)
+ XXX - this could probably be done more efficiently inside hostlist_table */
+ add_hostlist_table_data(hosts, &udphdr->ip_src, udphdr->uh_sport, TRUE, 1, pinfo->fd->pkt_len, SAT_NONE, PT_UDP);
+ add_hostlist_table_data(hosts, &udphdr->ip_dst, udphdr->uh_dport, FALSE, 1, pinfo->fd->pkt_len, SAT_NONE, PT_UDP);
+
+ return 1;
+}
+
+
+
+static void
+gtk_udpip_hostlist_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"endpoints,udp,",14)){
+ filter=optarg+14;
+ } else {
+ filter=NULL;
+ }
+
+ init_hostlist_table(FALSE, "UDP", "udp", filter, udpip_hostlist_packet);
+
+}
+
+void
+gtk_udpip_hostlist_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ gtk_udpip_hostlist_init("endpoints,udp",NULL);
+}
+
+void
+register_tap_listener_udpip_hostlist(void)
+{
+ register_stat_cmd_arg("endpoints,udp", gtk_udpip_hostlist_init,NULL);
+ register_hostlist_table(FALSE, "UDP", "udp", NULL /*filter*/, udpip_hostlist_packet);
+}
diff --git a/ui/gtk/hostlist_usb.c b/ui/gtk/hostlist_usb.c
new file mode 100644
index 0000000000..873cea941e
--- /dev/null
+++ b/ui/gtk/hostlist_usb.c
@@ -0,0 +1,86 @@
+/* hostlist_usb.c 2007 Jon Smirl
+ * modified from endpoint_talkers_eth.c 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/packet.h"
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-usb.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/hostlist_table.h"
+
+static int
+usb_hostlist_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip _U_)
+{
+ hostlist_table *hosts=(hostlist_table *)pit;
+
+ /* Take two "add" passes per packet, adding for each direction, ensures that all
+ packets are counted properly (even if address is sending to itself)
+ XXX - this could probably be done more efficiently inside hostlist_table */
+ add_hostlist_table_data(hosts, &pinfo->src, 0, TRUE, 1, pinfo->fd->pkt_len, SAT_NONE, PT_NONE);
+ add_hostlist_table_data(hosts, &pinfo->dst, 0, FALSE, 1, pinfo->fd->pkt_len, SAT_NONE, PT_NONE);
+
+ return 1;
+}
+
+static void
+gtk_usb_hostlist_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if (!strncmp(optarg, "hosts,usb," ,10)) {
+ filter = optarg + 10;
+ } else {
+ filter = NULL;
+ }
+
+ init_hostlist_table(TRUE, "USB", "usb", filter, usb_hostlist_packet);
+
+}
+
+void
+gtk_usb_hostlist_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ gtk_usb_hostlist_init("hosts,usb", NULL);
+}
+
+void
+register_tap_listener_usb_hostlist(void)
+{
+ register_stat_cmd_arg("hosts,usb", gtk_usb_hostlist_init, NULL);
+ register_hostlist_table(TRUE, "USB", "usb", NULL /*filter*/, usb_hostlist_packet);
+}
diff --git a/ui/gtk/hostlist_wlan.c b/ui/gtk/hostlist_wlan.c
new file mode 100644
index 0000000000..d3b5f99a81
--- /dev/null
+++ b/ui/gtk/hostlist_wlan.c
@@ -0,0 +1,87 @@
+/* hostlist_wlan.c 2004 Giles Scott
+ * modified from endpoint_talkers_eth.c 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/packet.h"
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-ieee80211.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/hostlist_table.h"
+
+static int
+wlan_hostlist_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ hostlist_table *hosts=(hostlist_table *)pit;
+ const wlan_hdr *whdr=vip;
+
+ /* Take two "add" passes per packet, adding for each direction, ensures that all
+ packets are counted properly (even if address is sending to itself)
+ XXX - this could probably be done more efficiently inside hostlist_table */
+ add_hostlist_table_data(hosts, &whdr->src, 0, TRUE, 1, pinfo->fd->pkt_len, SAT_WLAN, PT_NONE);
+ add_hostlist_table_data(hosts, &whdr->dst, 0, FALSE, 1, pinfo->fd->pkt_len, SAT_WLAN, PT_NONE);
+
+ return 1;
+}
+
+static void
+gtk_wlan_hostlist_init(const char *optarg, void* userdata _U_)
+{
+ const char *filter=NULL;
+
+ if(!strncmp(optarg,"hosts,wlan,",11)){
+ filter=optarg+11;
+ } else {
+ filter=NULL;
+ }
+
+ init_hostlist_table(TRUE, "WLAN", "wlan", filter, wlan_hostlist_packet);
+
+}
+
+void
+gtk_wlan_hostlist_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ gtk_wlan_hostlist_init("hosts,wlan",NULL);
+}
+
+void
+register_tap_listener_wlan_hostlist(void)
+{
+ register_stat_cmd_arg("hosts,wlan", gtk_wlan_hostlist_init,NULL);
+ register_hostlist_table(TRUE, "WLAN", "wlan", NULL /*filter*/, wlan_hostlist_packet);
+}
diff --git a/ui/gtk/iax2_analysis.c b/ui/gtk/iax2_analysis.c
new file mode 100644
index 0000000000..cbd48671ce
--- /dev/null
+++ b/ui/gtk/iax2_analysis.c
@@ -0,0 +1,3745 @@
+/* iax2_analysis.c
+ * IAX2 analysis addition for Wireshark
+ *
+ * $Id$
+ *
+ * based on rtp_analysis.c
+ * Copyright 2003, Alcatel Business Systems
+ * By Lars Ruoff <lars.ruoff@gmx.net>
+ *
+ * based on tap_rtp.c
+ * Copyright 2003, Iskratel, Ltd, Kranj
+ * By Miha Jemec <m.jemec@iskratel.si>
+ *
+ * Graph. Copyright 2004, Verso Technology
+ * By Alejandro Vaquero <alejandro.vaquero@verso.com>
+ * Based on io_stat.c by Ronnie Sahlberg
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <locale.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <gtk/gtk.h>
+
+#include <epan/epan_dissect.h>
+#include <epan/filesystem.h>
+#include <epan/pint.h>
+#include <epan/tap.h>
+#include <epan/tap-voip.h>
+#include <epan/dissectors/packet-iax2.h>
+#include <epan/iax2_codec_type.h>
+#include <epan/addr_resolv.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/strutil.h>
+
+#include "../util.h"
+#include "../g711.h"
+#include "../alert_box.h"
+#include "../simple_dialog.h"
+#include "../stat_menu.h"
+#include "../progress_dlg.h"
+#include "../tempfile.h"
+#include <wsutil/file_util.h>
+
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/file_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/rtp_analysis.h"
+#include "ui/gtk/iax2_analysis.h"
+#include "ui/gtk/rtp_stream.h"
+#include "ui/gtk/rtp_stream_dlg.h"
+#include "ui/gtk/utf8_entities.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+enum
+{
+ PACKET_COLUMN,
+ DELTA_COLUMN,
+ JITTER_COLUMN,
+ IPBW_COLUMN,
+ STATUS_COLUMN,
+ DATE_COLUMN,
+ LENGTH_COLUMN,
+ FOREGROUND_COLOR_COL,
+ BACKGROUND_COLOR_COL,
+ N_COLUMN /* The number of columns */
+};
+
+/****************************************************************************/
+#define NUM_COLS 7
+#define NUM_GRAPH_ITEMS 100000
+#define MAX_YSCALE 16
+#define AUTO_MAX_YSCALE_INDEX 0
+#define AUTO_MAX_YSCALE 0
+#define MAX_GRAPHS 4
+#define GRAPH_FWD_JITTER 0
+#define GRAPH_FWD_DIFF 1
+#define GRAPH_REV_JITTER 2
+#define GRAPH_REV_DIFF 3
+static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
+
+#define MAX_PIXELS_PER_TICK 4
+#define DEFAULT_PIXELS_PER_TICK_INDEX 1
+static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
+static const char *graph_descr[4] = {"Fwd Jitter", "Fwd Difference", "Rvr Jitter", "Rvr Difference"};
+/* unit is in ms */
+#define MAX_TICK_VALUES 5
+#define DEFAULT_TICK_INTERVAL_VALUES_INDEX 1
+static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
+typedef struct _dialog_graph_graph_item_t {
+ guint32 value;
+ guint32 flags;
+} dialog_graph_graph_item_t;
+
+typedef struct _dialog_graph_graph_t {
+ struct _user_data_t *ud;
+ dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
+ int plot_style;
+ gboolean display;
+ GtkWidget *display_button;
+ int hf_index;
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkRGBA rgba_color;
+#endif
+ GdkColor color;
+ gchar title[100];
+} dialog_graph_graph_t;
+
+
+typedef struct _dialog_graph_t {
+ gboolean needs_redraw;
+ gint32 interval_index; /* index into tick_interval_values array */
+ gint32 interval; /* measurement interval in ms */
+ guint32 last_interval;
+ guint32 max_interval; /* XXX max_interval and num_items are redundant */
+ guint32 num_items;
+ struct _dialog_graph_graph_t graph[MAX_GRAPHS];
+ GtkWidget *window;
+ GtkWidget *draw_area;
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_surface_t *surface;
+#else
+ GdkPixmap *pixmap;
+#endif
+ GtkAdjustment *scrollbar_adjustment;
+ GtkWidget *scrollbar;
+ int surface_width;
+ int surface_height;
+ int pixels_per_tick_index; /* index into pixels_per_tick array */
+ int pixels_per_tick;
+ int max_y_units_index; /* index into yscale_max array */
+ int max_y_units;
+ double start_time;
+} dialog_graph_t;
+
+typedef struct _dialog_data_t {
+ GtkWidget *window;
+ GtkWidget *list_fwd;
+ GtkTreeIter iter;
+ GtkWidget *list_rev;
+ GtkWidget *label_stats_fwd;
+ GtkWidget *label_stats_rev;
+ GtkWidget *selected_list;
+ guint number_of_nok;
+ GtkTreeSelection *selected_list_sel;
+ gint selected_list_row;
+ GtkWidget *notebook;
+ GtkWidget *save_voice_as_w;
+ GtkWidget *save_csv_as_w;
+ gint notebook_signal_id;
+ dialog_graph_t dialog_graph;
+} dialog_data_t;
+
+#define OK_TEXT "[ Ok ]"
+
+/* type of error when saving voice in a file didn't succeed */
+typedef enum {
+ TAP_RTP_WRONG_CODEC,
+ TAP_RTP_WRONG_LENGTH,
+ TAP_RTP_PADDING_ERROR,
+ TAP_RTP_SHORT_FRAME,
+ TAP_RTP_FILE_OPEN_ERROR,
+ TAP_RTP_FILE_WRITE_ERROR,
+ TAP_RTP_NO_DATA
+} error_type_t;
+
+typedef struct _tap_iax2_save_info_t {
+ FILE *fp;
+ guint32 count;
+ error_type_t error_type;
+ gboolean saved;
+} tap_iax2_save_info_t;
+
+
+/* structure that holds the information about the forward and reversed direction */
+struct _info_direction {
+ tap_iax2_stat_t statinfo;
+ tap_iax2_save_info_t saveinfo;
+};
+
+#define SILENCE_PCMU (guint8)0xFF
+#define SILENCE_PCMA (guint8)0x55
+
+/* structure that holds general information about the connection
+* and structures for both directions */
+typedef struct _user_data_t {
+ /* tap associated data*/
+ address ip_src_fwd;
+ guint16 port_src_fwd;
+ address ip_dst_fwd;
+ guint16 port_dst_fwd;
+ address ip_src_rev;
+ guint16 port_src_rev;
+ address ip_dst_rev;
+ guint16 port_dst_rev;
+
+ struct _info_direction forward;
+ struct _info_direction reversed;
+
+ char *f_tempname;
+ char *r_tempname;
+
+ /* dialog associated data */
+ dialog_data_t dlg;
+
+} user_data_t;
+
+
+/* Column titles. */
+static const gchar *titles[7] = {
+ "Packet",
+ "Delta (ms)",
+ "Jitter (ms)",
+ "IP BW (kbps)",
+ "Status",
+ "Date",
+ "Length"
+};
+
+#define SAVE_FORWARD_DIRECTION_MASK 0x01
+#define SAVE_REVERSE_DIRECTION_MASK 0x02
+#define SAVE_BOTH_DIRECTION_MASK (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
+
+#define SAVE_NONE_FORMAT 0
+#define SAVE_WAV_FORMAT 1
+#define SAVE_AU_FORMAT 2
+#define SAVE_SW_FORMAT 3
+#define SAVE_RAW_FORMAT 4
+
+
+static void on_refresh_bt_clicked(GtkWidget *bt _U_, gpointer data);
+/****************************************************************************/
+static void enable_graph(dialog_graph_graph_t *dgg)
+{
+
+ dgg->display=TRUE;
+
+}
+
+static void dialog_graph_reset(user_data_t* user_data);
+
+
+
+/****************************************************************************/
+/* TAP FUNCTIONS */
+
+/****************************************************************************/
+/* when there is a [re]reading of packet's */
+static void
+iax2_reset(void *user_data_arg)
+{
+ user_data_t *user_data = user_data_arg;
+ user_data->forward.statinfo.first_packet = TRUE;
+ user_data->reversed.statinfo.first_packet = TRUE;
+ user_data->forward.statinfo.max_delta = 0;
+ user_data->reversed.statinfo.max_delta = 0;
+ user_data->forward.statinfo.max_jitter = 0;
+ user_data->reversed.statinfo.max_jitter = 0;
+ user_data->forward.statinfo.mean_jitter = 0;
+ user_data->reversed.statinfo.mean_jitter = 0;
+ user_data->forward.statinfo.delta = 0;
+ user_data->reversed.statinfo.delta = 0;
+ user_data->forward.statinfo.diff = 0;
+ user_data->reversed.statinfo.diff = 0;
+ user_data->forward.statinfo.jitter = 0;
+ user_data->reversed.statinfo.jitter = 0;
+ user_data->forward.statinfo.bandwidth = 0;
+ user_data->reversed.statinfo.bandwidth = 0;
+ user_data->forward.statinfo.total_bytes = 0;
+ user_data->reversed.statinfo.total_bytes = 0;
+ user_data->forward.statinfo.bw_start_index = 0;
+ user_data->reversed.statinfo.bw_start_index = 0;
+ user_data->forward.statinfo.bw_index = 0;
+ user_data->reversed.statinfo.bw_index = 0;
+ user_data->forward.statinfo.timestamp = 0;
+ user_data->reversed.statinfo.timestamp = 0;
+ user_data->forward.statinfo.max_nr = 0;
+ user_data->reversed.statinfo.max_nr = 0;
+ user_data->forward.statinfo.total_nr = 0;
+ user_data->reversed.statinfo.total_nr = 0;
+ user_data->forward.statinfo.sequence = 0;
+ user_data->reversed.statinfo.sequence = 0;
+ user_data->forward.statinfo.start_seq_nr = 0;
+ user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
+ user_data->forward.statinfo.stop_seq_nr = 0;
+ user_data->reversed.statinfo.stop_seq_nr = 0;
+ user_data->forward.statinfo.cycles = 0;
+ user_data->reversed.statinfo.cycles = 0;
+ user_data->forward.statinfo.under = FALSE;
+ user_data->reversed.statinfo.under = FALSE;
+ user_data->forward.statinfo.start_time = 0;
+ user_data->reversed.statinfo.start_time = 0;
+ user_data->forward.statinfo.time = 0;
+ user_data->reversed.statinfo.time = 0;
+ user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
+ user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
+
+ user_data->forward.saveinfo.count = 0;
+ user_data->reversed.saveinfo.count = 0;
+ user_data->forward.saveinfo.saved = FALSE;
+ user_data->reversed.saveinfo.saved = FALSE;
+
+ /* clear the dialog box lists */
+ gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd))));
+ gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev))));
+
+ /* reset graph info */
+ dialog_graph_reset(user_data);
+
+
+ /* XXX check for error at fclose? */
+ if (user_data->forward.saveinfo.fp != NULL)
+ fclose(user_data->forward.saveinfo.fp);
+ if (user_data->reversed.saveinfo.fp != NULL)
+ fclose(user_data->reversed.saveinfo.fp);
+ user_data->forward.saveinfo.fp = ws_fopen(user_data->f_tempname, "wb");
+ if (user_data->forward.saveinfo.fp == NULL)
+ user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
+ user_data->reversed.saveinfo.fp = ws_fopen(user_data->r_tempname, "wb");
+ if (user_data->reversed.saveinfo.fp == NULL)
+ user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
+ return;
+}
+
+/****************************************************************************/
+static gboolean iax2_packet_add_graph(dialog_graph_graph_t *dgg, tap_iax2_stat_t *statinfo, packet_info *pinfo, guint32 value)
+{
+ dialog_graph_graph_item_t *it;
+ guint32 idx;
+ double rtp_time;
+
+ /* we sometimes get called when dgg is disabled.
+ this is a bug since the tap listener should be removed first */
+ if(!dgg->display){
+ return FALSE;
+ }
+
+ dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
+
+ /*
+ * Find which interval this is supposed to go in and store the
+ * interval index as idx
+ */
+ if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
+ dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
+ }
+ rtp_time = nstime_to_msec(&pinfo->fd->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
+ if(rtp_time<0){
+ return FALSE;
+ }
+ idx = (guint32)(rtp_time)/dgg->ud->dlg.dialog_graph.interval;
+
+ /* some sanity checks */
+ if(idx>=NUM_GRAPH_ITEMS){
+ return FALSE;
+ }
+
+ /* update num_items */
+ if(idx > dgg->ud->dlg.dialog_graph.num_items){
+ dgg->ud->dlg.dialog_graph.num_items=idx;
+ dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
+ }
+
+ /*
+ * Find the appropriate dialog_graph_graph_item_t structure
+ */
+ it=&dgg->items[idx];
+
+ /*
+ * Use the max value to highlight RTP problems
+ */
+ if (value > it->value) {
+ it->value=value;
+ }
+ it->flags = it->flags | statinfo->flags;
+
+ return TRUE;
+}
+
+/****************************************************************************/
+/* here we can redraw the output */
+/* not used yet */
+static void iax2_draw(void *prs _U_)
+{
+ return;
+}
+
+/* forward declarations */
+static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number,
+ double delta, double jitter, double bandwidth, gchar *status,
+ gchar *timeStr, guint32 pkt_len,gchar *color_str, guint32 flags);
+
+static int iax2_packet_add_info(GtkWidget *list,user_data_t * user_data,
+ tap_iax2_stat_t *statinfo, packet_info *pinfo,
+ const struct _iax2_info_t *iax2info);
+
+static void iax2_packet_save_payload(tap_iax2_save_info_t *saveinfo,
+ tap_iax2_stat_t *statinfo,
+ packet_info *pinfo,
+ const struct _iax2_info_t *iax2info);
+
+
+/****************************************************************************/
+/* whenever a IAX2 packet is seen by the tap listener */
+static gboolean iax2_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *iax2info_arg)
+{
+ user_data_t *user_data = user_data_arg;
+ const struct _iax2_info_t *iax2info = iax2info_arg;
+
+ /* we ignore packets that are not displayed */
+ if (pinfo->fd->flags.passed_dfilter == 0)
+ return FALSE;
+
+ /* we ignore packets that carry no data */
+ if (iax2info->payload_len == 0)
+ return FALSE;
+
+ /* is it the forward direction? */
+ else if (CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
+ && user_data->port_src_fwd == pinfo->srcport
+ && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
+ && user_data->port_dst_fwd == pinfo->destport) {
+ iax2_packet_analyse(&(user_data->forward.statinfo), pinfo, iax2info);
+ iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_JITTER]),
+ &(user_data->forward.statinfo),
+ pinfo,
+ (guint32)(user_data->forward.statinfo.jitter*1000000));
+ iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DIFF]),
+ &(user_data->forward.statinfo),
+ pinfo,
+ (guint32)(user_data->forward.statinfo.diff*1000000));
+ iax2_packet_add_info(user_data->dlg.list_fwd, user_data,
+ &(user_data->forward.statinfo), pinfo, iax2info);
+ iax2_packet_save_payload(&(user_data->forward.saveinfo),
+ &(user_data->forward.statinfo), pinfo, iax2info);
+ }
+ /* is it the reversed direction? */
+ else if (CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
+ && user_data->port_src_rev == pinfo->srcport
+ && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
+ && user_data->port_dst_rev == pinfo->destport) {
+ iax2_packet_analyse(&(user_data->reversed.statinfo), pinfo, iax2info);
+ iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_JITTER]),
+ &(user_data->reversed.statinfo),
+ pinfo,
+ (guint32)(user_data->reversed.statinfo.jitter*1000000));
+ iax2_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DIFF]),
+ &(user_data->reversed.statinfo),
+ pinfo,
+ (guint32)(user_data->reversed.statinfo.diff*1000000));
+ iax2_packet_add_info(user_data->dlg.list_rev, user_data,
+ &(user_data->reversed.statinfo), pinfo, iax2info);
+ iax2_packet_save_payload(&(user_data->reversed.saveinfo),
+ &(user_data->reversed.statinfo), pinfo, iax2info);
+ }
+
+ return FALSE;
+}
+
+/****************************************************************************/
+/* This comes from tap-rtp-common.c */
+/****************************************************************************/
+
+int iax2_packet_analyse(tap_iax2_stat_t *statinfo,
+ packet_info *pinfo,
+ const struct _iax2_info_t *iax2info)
+{
+ double current_time;
+ double current_jitter;
+ double current_diff;
+
+ statinfo->flags = 0;
+ /* check payload type */
+ if (iax2info->ftype == AST_FRAME_VOICE) {
+ if (iax2info->csub != statinfo->pt)
+ statinfo->flags |= STAT_FLAG_PT_CHANGE;
+ statinfo->pt = iax2info->csub;
+ }
+
+ /* store the current time and calculate the current jitter */
+ current_time = nstime_to_sec(&pinfo->fd->rel_ts);
+ current_diff = fabs (current_time - statinfo->time - (((double)iax2info->timestamp - (double)statinfo->timestamp)/1000));
+ current_jitter = statinfo->jitter + ( current_diff - statinfo->jitter)/16;
+ statinfo->delta = current_time-(statinfo->time);
+ statinfo->jitter = current_jitter;
+ statinfo->diff = current_diff;
+
+ /* calculate the BW in Kbps adding the IP+IAX2 header to the RTP -> 20bytes(IP)+ 4bytes(Mini) = 24bytes */
+ statinfo->bw_history[statinfo->bw_index].bytes = iax2info->payload_len + 24;
+ statinfo->bw_history[statinfo->bw_index].time = current_time;
+ /* check if there are more than 1sec in the history buffer to calculate BW in bps. If so, remove those for the calculation */
+ while ((statinfo->bw_history[statinfo->bw_start_index].time+1)<current_time){
+ statinfo->total_bytes -= statinfo->bw_history[statinfo->bw_start_index].bytes;
+ statinfo->bw_start_index++;
+ if (statinfo->bw_start_index == BUFF_BW) statinfo->bw_start_index=0;
+ };
+ statinfo->total_bytes += iax2info->payload_len + 24;
+ statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000;
+ statinfo->bw_index++;
+ if (statinfo->bw_index == BUFF_BW) statinfo->bw_index = 0;
+
+
+ /* is this the first packet we got in this direction? */
+ if (statinfo->first_packet) {
+ statinfo->start_seq_nr = 0;
+ statinfo->start_time = current_time;
+ statinfo->delta = 0;
+ statinfo->jitter = 0;
+ statinfo->diff = 0;
+ statinfo->flags |= STAT_FLAG_FIRST;
+ statinfo->first_packet = FALSE;
+ }
+ /* is it a regular packet? */
+ if (!(statinfo->flags & STAT_FLAG_FIRST)
+ && !(statinfo->flags & STAT_FLAG_MARKER)
+ && !(statinfo->flags & STAT_FLAG_PT_CN)
+ && !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP)
+ && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
+ /* include it in maximum delta calculation */
+ if (statinfo->delta > statinfo->max_delta) {
+ statinfo->max_delta = statinfo->delta;
+ statinfo->max_nr = pinfo->fd->num;
+ }
+ /* maximum and mean jitter calculation */
+ if (statinfo->jitter > statinfo->max_jitter) {
+ statinfo->max_jitter = statinfo->jitter;
+ }
+ statinfo->mean_jitter = (statinfo->mean_jitter*statinfo->total_nr + current_diff) / (statinfo->total_nr+1);
+ }
+ /* regular payload change? (CN ignored) */
+ if (!(statinfo->flags & STAT_FLAG_FIRST)
+ && !(statinfo->flags & STAT_FLAG_PT_CN)) {
+ if ((statinfo->pt != statinfo->reg_pt)
+ && (statinfo->reg_pt != PT_UNDEFINED)) {
+ statinfo->flags |= STAT_FLAG_REG_PT_CHANGE;
+ }
+ }
+
+ /* set regular payload*/
+ if (!(statinfo->flags & STAT_FLAG_PT_CN)) {
+ statinfo->reg_pt = statinfo->pt;
+ }
+
+ /* TODO: lost packets / duplicated: we should infer this from timestamp... */
+ statinfo->time = current_time;
+ statinfo->timestamp = iax2info->timestamp; /* millisecs */
+ statinfo->stop_seq_nr = 0;
+ statinfo->total_nr++;
+
+ return 0;
+}
+
+
+#if 0
+static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
+static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
+static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
+static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
+static const GdkColor COLOR_FOREGROUND = {0, 0x0000, 0x0000, 0x0000};
+#endif
+
+/****************************************************************************/
+/* adds statistics information from the packet to the list */
+static int iax2_packet_add_info(GtkWidget *list, user_data_t * user_data,
+ tap_iax2_stat_t *statinfo, packet_info *pinfo,
+ const struct _iax2_info_t *iax2info _U_)
+{
+ guint16 msecs;
+ gchar timeStr[32];
+ struct tm *tm_tmp;
+ time_t then;
+ gchar status[40];
+ /* GdkColor color = COLOR_DEFAULT; */
+ gchar color_str[14];
+ then = pinfo->fd->abs_ts.secs;
+ msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
+ tm_tmp = localtime(&then);
+ g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
+ tm_tmp->tm_mon + 1,
+ tm_tmp->tm_mday,
+ tm_tmp->tm_year + 1900,
+ tm_tmp->tm_hour,
+ tm_tmp->tm_min,
+ tm_tmp->tm_sec,
+ msecs);
+
+ /* Default to using black on white text if nothing below overrides it */
+ g_snprintf(color_str,sizeof(color_str),"#ffffffffffff");
+
+ if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
+ g_snprintf(status,sizeof(status),"Wrong sequence nr.");
+ /* color = COLOR_ERROR; */
+ g_snprintf(color_str,sizeof(color_str),"#ffffbfffbfff");
+ }
+ else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
+ g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
+ /* color = COLOR_WARNING; */
+ g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
+ }
+ else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
+ g_snprintf(status,sizeof(status),"Incorrect timestamp");
+ /* color = COLOR_WARNING; */
+ g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
+ }
+ else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
+ && !(statinfo->flags & STAT_FLAG_FIRST)
+ && !(statinfo->flags & STAT_FLAG_PT_CN)
+ && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
+ && !(statinfo->flags & STAT_FLAG_MARKER)) {
+ g_snprintf(status,sizeof(status),"Marker missing?");
+ /* color = COLOR_WARNING; */
+ g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
+ }
+ else {
+ if (statinfo->flags & STAT_FLAG_MARKER) {
+ /* color = COLOR_WARNING; */
+ g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
+ }
+ g_snprintf(status,sizeof(status),OK_TEXT);
+ }
+ /* is this the first packet we got in this direction? */
+ if (statinfo->flags & STAT_FLAG_FIRST) {
+ add_to_list(list, user_data,
+ pinfo->fd->num,
+ 0,
+ 0,
+ statinfo->bandwidth,
+ status,
+ timeStr, pinfo->fd->pkt_len,
+ color_str,
+ statinfo->flags);
+ }
+ else {
+ add_to_list(list, user_data,
+ pinfo->fd->num,
+ statinfo->delta*1000,
+ statinfo->jitter*1000,
+ statinfo->bandwidth,
+ status,
+ timeStr, pinfo->fd->pkt_len,
+ color_str,
+ statinfo->flags);
+ }
+ return 0;
+}
+
+#define MAX_SILENCE_TICKS 1000000
+/****************************************************************************/
+static void iax2_packet_save_payload(tap_iax2_save_info_t *saveinfo,
+ tap_iax2_stat_t *statinfo,
+ packet_info *pinfo,
+ const struct _iax2_info_t *iax2info)
+{
+ const guint8 *data;
+ size_t nchars;
+
+ /* is this the first packet we got in this direction? */
+ if (statinfo->flags & STAT_FLAG_FIRST) {
+ if (saveinfo->fp == NULL) {
+ saveinfo->saved = FALSE;
+ saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
+ }
+ else
+ saveinfo->saved = TRUE;
+ }
+
+ /* save the voice information */
+ /* if there was already an error, we quit */
+ if (saveinfo->saved == FALSE)
+ return;
+
+ /* if the captured length and packet length aren't equal, we quit */
+ if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
+ saveinfo->saved = FALSE;
+ saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
+ return;
+ }
+
+ if (iax2info->payload_len > 0) {
+ data = iax2info->payload_data;
+ nchars = fwrite(data, sizeof(unsigned char), iax2info->payload_len, saveinfo->fp);
+ if (nchars != iax2info->payload_len) {
+ /* Write error or short write */
+ saveinfo->saved = FALSE;
+ saveinfo->error_type = TAP_RTP_FILE_WRITE_ERROR;
+ return;
+ }
+ saveinfo->count+=iax2info->payload_len;
+
+ if (fflush(saveinfo->fp) == EOF) {
+ /* Write error */
+ saveinfo->saved = FALSE;
+ saveinfo->error_type = TAP_RTP_FILE_WRITE_ERROR;
+ return;
+ }
+ saveinfo->saved = TRUE;
+ return;
+ }
+
+ return;
+}
+
+
+/****************************************************************************/
+/* CALLBACKS */
+
+/****************************************************************************/
+
+/****************************************************************************/
+/* close the dialog window and remove the tap listener */
+static void on_iax2_window_destroy(GtkWidget *win _U_, gpointer data)
+{
+ user_data_t *user_data = data;
+
+ /* remove tap listener */
+ protect_thread_critical_region();
+ remove_tap_listener(user_data);
+ unprotect_thread_critical_region();
+
+ /* close and remove temporary files */
+ if (user_data->forward.saveinfo.fp != NULL)
+ fclose(user_data->forward.saveinfo.fp);
+ if (user_data->reversed.saveinfo.fp != NULL)
+ fclose(user_data->reversed.saveinfo.fp);
+ /*XXX: test for error **/
+ ws_remove(user_data->f_tempname);
+ ws_remove(user_data->r_tempname);
+
+#if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
+ /* destroy save_csv_as window if open */
+ if (user_data->dlg.save_csv_as_w != NULL)
+ window_destroy(user_data->dlg.save_csv_as_w);
+
+ /* destroy save_voice_as window if open */
+ if (user_data->dlg.save_voice_as_w != NULL)
+ window_destroy(user_data->dlg.save_voice_as_w);
+#endif
+ /* destroy graph window if open */
+ if (user_data->dlg.dialog_graph.window != NULL)
+ window_destroy(user_data->dlg.dialog_graph.window);
+
+
+ /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exception using GTK1*/
+ /* XXX: Is this still true for GTK2 ??? */
+ g_signal_handler_disconnect(user_data->dlg.notebook, user_data->dlg.notebook_signal_id);
+
+ g_free(user_data->f_tempname);
+ g_free(user_data->r_tempname);
+ g_free(user_data);
+}
+
+
+/****************************************************************************/
+static void on_notebook_switch_page(GtkNotebook *notebook _U_,
+ gpointer *page _U_,
+ gint page_num,
+ gpointer data)
+{
+ user_data_t *user_data = data;
+
+ user_data->dlg.selected_list =
+ (page_num==0) ? user_data->dlg.list_fwd : user_data->dlg.list_rev ;
+
+ user_data->dlg.selected_list_row = 0;
+}
+
+/****************************************************************************/
+static void on_list_select_row(GtkTreeSelection *selection,
+ gpointer data)
+{
+ user_data_t *user_data = data;
+
+ user_data->dlg.selected_list_sel = selection;
+}
+
+
+/****************************************************************************/
+static void dialog_graph_set_title(user_data_t* user_data)
+{
+ char *title;
+
+ if (!user_data->dlg.dialog_graph.window){
+ return;
+ }
+ title = g_strdup_printf("IAX2 Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
+ get_addr_name(&(user_data->ip_src_fwd)),
+ user_data->port_src_fwd,
+ get_addr_name(&(user_data->ip_dst_fwd)),
+ user_data->port_dst_fwd,
+ get_addr_name(&(user_data->ip_src_rev)),
+ user_data->port_src_rev,
+ get_addr_name(&(user_data->ip_dst_rev)),
+ user_data->port_dst_rev);
+
+ gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
+ g_free(title);
+
+}
+
+
+/****************************************************************************/
+static void dialog_graph_reset(user_data_t* user_data)
+{
+ int i, j;
+
+ user_data->dlg.dialog_graph.needs_redraw=TRUE;
+ for(i=0;i<MAX_GRAPHS;i++){
+ for(j=0;j<NUM_GRAPH_ITEMS;j++){
+ dialog_graph_graph_item_t *dggi;
+ dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
+ dggi->value=0;
+ dggi->flags=0;
+ }
+ }
+ user_data->dlg.dialog_graph.last_interval=0xffffffff;
+ user_data->dlg.dialog_graph.max_interval=0;
+ user_data->dlg.dialog_graph.num_items=0;
+
+ /* create the color titles near the filter buttons */
+ for(i=0;i<MAX_GRAPHS;i++){
+ /* it is forward */
+ if (i<2){
+ g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
+ sizeof (user_data->dlg.dialog_graph.graph[0].title),
+ "%s: %s:%u to %s:%u",
+ graph_descr[i],
+ get_addr_name(&(user_data->ip_src_fwd)),
+ user_data->port_src_fwd,
+ get_addr_name(&(user_data->ip_dst_fwd)),
+ user_data->port_dst_fwd);
+ /* it is reverse */
+ } else {
+ g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
+ sizeof(user_data->dlg.dialog_graph.graph[0].title),
+ "%s: %s:%u to %s:%u",
+ graph_descr[i],
+ get_addr_name(&(user_data->ip_src_rev)),
+ user_data->port_src_rev,
+ get_addr_name(&(user_data->ip_dst_rev)),
+ user_data->port_dst_rev);
+ }
+ }
+
+ dialog_graph_set_title(user_data);
+}
+
+/****************************************************************************/
+static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
+{
+ dialog_graph_graph_item_t *it;
+
+ it=&dgg->items[idx];
+
+ return it->value;
+}
+
+/****************************************************************************/
+static void print_time_scale_string(char *buf, int buf_len, guint32 t)
+{
+ if(t>=10000000){
+ g_snprintf(buf, buf_len, "%ds",t/1000000);
+ } else if(t>=1000000){
+ g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
+ } else if(t>=10000){
+ g_snprintf(buf, buf_len, "%dms",t/1000);
+ } else if(t>=1000){
+ g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
+ } else {
+ g_snprintf(buf, buf_len, "%dus",t);
+ }
+}
+
+/****************************************************************************/
+static void dialog_graph_draw(user_data_t* user_data)
+{
+ int i, lwidth;
+ guint32 last_interval, first_interval, interval_delta, delta_multiplier;
+ gint32 current_interval;
+ guint32 left_x_border;
+ guint32 right_x_border;
+ guint32 top_y_border;
+ guint32 bottom_y_border;
+ PangoLayout *layout;
+ int label_width, label_height;
+ int label_width_mid, label_height_mid;
+ guint32 draw_width, draw_height;
+ char label_string[15];
+ GtkAllocation widget_alloc;
+ cairo_t *cr;
+
+ /* new variables */
+ guint32 num_time_intervals;
+ guint32 max_value; /* max value of seen data */
+ guint32 max_y; /* max value of the Y scale */
+
+ if(!user_data->dlg.dialog_graph.needs_redraw){
+ return;
+ }
+ user_data->dlg.dialog_graph.needs_redraw=FALSE;
+
+ /*
+ * Find the length of the intervals we have data for
+ * so we know how large arrays we need to malloc()
+ */
+ num_time_intervals=user_data->dlg.dialog_graph.num_items;
+ /* if there isnt anything to do, just return */
+ if(num_time_intervals==0){
+ return;
+ }
+ num_time_intervals+=1;
+ /* XXX move this check to _packet() */
+ if(num_time_intervals>NUM_GRAPH_ITEMS){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "IAX2 Graph error. There are too many entries, bailing out");
+ return;
+ }
+
+ /*
+ * find the max value so we can autoscale the y axis
+ */
+ max_value=0;
+ for(i=0;i<MAX_GRAPHS;i++){
+ int idx;
+
+ if(!user_data->dlg.dialog_graph.graph[i].display){
+ continue;
+ }
+ for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
+ guint32 val;
+
+ val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
+
+ /* keep track of the max value we have encountered */
+ if(val>max_value){
+ max_value=val;
+ }
+ }
+ }
+
+ /*
+ * Clear out old plot
+ */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ gtk_widget_get_allocation(user_data->dlg.dialog_graph.draw_area, &widget_alloc);
+ cairo_rectangle (cr,
+ 0,
+ 0,
+ widget_alloc.width,
+ widget_alloc.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+
+ /*
+ * Calculate the y scale we should use
+ */
+ if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
+ max_y=yscale_max[MAX_YSCALE-1];
+ for(i=MAX_YSCALE-1;i>0;i--){
+ if(max_value<yscale_max[i]){
+ max_y=yscale_max[i];
+ }
+ }
+ } else {
+ /* the user had specified an explicit y scale to use */
+ max_y=user_data->dlg.dialog_graph.max_y_units;
+ }
+
+ /*
+ * Calculate size of borders surrounding the plot
+ * The border on the right side needs to be adjusted depending
+ * on the width of the text labels.
+ */
+ print_time_scale_string(label_string, sizeof(label_string), max_y);
+ layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
+ pango_layout_get_pixel_size(layout, &label_width, &label_height);
+ print_time_scale_string(label_string, sizeof(label_string), max_y*5/10);
+ layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
+ pango_layout_get_pixel_size(layout, &label_width_mid, &label_height_mid);
+ if (label_width_mid > label_width) {
+ label_width = label_width_mid;
+ label_height = label_height_mid;
+ }
+
+ left_x_border=10;
+ right_x_border=label_width+20;
+ top_y_border=10;
+ bottom_y_border=label_height+20;
+
+
+ /*
+ * Calculate the size of the drawing area for the actual plot
+ */
+ draw_width=user_data->dlg.dialog_graph.surface_width-right_x_border-left_x_border;
+ draw_height=user_data->dlg.dialog_graph.surface_height-top_y_border-bottom_y_border;
+
+
+ /*
+ * Draw the y axis and labels
+ * (we always draw the y scale with 11 ticks along the axis)
+ */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, user_data->dlg.dialog_graph.surface_width-right_x_border+1.5, top_y_border+0.5);
+ cairo_line_to(cr, user_data->dlg.dialog_graph.surface_width-right_x_border+1.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+ for(i=0;i<=10;i++){
+ int xwidth;
+
+ xwidth=5;
+ if(!(i%5)){
+ /* first, middle and last tick are slightly longer */
+ xwidth=10;
+ }
+ /* draw the tick */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr,
+ user_data->dlg.dialog_graph.surface_width-right_x_border+1.5,
+ user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10+0.5);
+
+ cairo_line_to(cr,
+ user_data->dlg.dialog_graph.surface_width-right_x_border+1.5+xwidth,
+ user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ /* draw the labels */
+ if(i==0){
+ print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_move_to (cr,
+ user_data->dlg.dialog_graph.surface_width-right_x_border+15+label_width-lwidth,
+ user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10-label_height/2);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+ if(i==5){
+ print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_move_to (cr,
+ user_data->dlg.dialog_graph.surface_width-right_x_border+15+label_width-lwidth,
+ user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10-label_height/2);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+ if(i==10){
+ print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_move_to (cr,
+ user_data->dlg.dialog_graph.surface_width-right_x_border+15+label_width-lwidth,
+ user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10-label_height/2);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+ }
+
+
+
+ /*
+ * if we have not specified the last_interval via the gui,
+ * then just pick the current end of the capture so that is scrolls
+ * nicely when doing live captures
+ */
+ if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
+ last_interval=user_data->dlg.dialog_graph.max_interval;
+ } else {
+ last_interval=user_data->dlg.dialog_graph.last_interval;
+ }
+
+
+
+
+/*XXX*/
+ /* plot the x-scale */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, left_x_border+0.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+1.5);
+ cairo_line_to(cr, user_data->dlg.dialog_graph.surface_width-right_x_border+1.5,user_data->dlg.dialog_graph.surface_height-bottom_y_border+1.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+ if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
+ first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
+ first_interval*=user_data->dlg.dialog_graph.interval;
+ } else {
+ first_interval=0;
+ }
+
+ interval_delta=1;
+ delta_multiplier=5;
+ while(interval_delta<((last_interval-first_interval)/10)){
+ interval_delta*=delta_multiplier;
+ if(delta_multiplier==5){
+ delta_multiplier=2;
+ } else {
+ delta_multiplier=5;
+ }
+ }
+
+ for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
+ int x, xlen;
+
+ /* if pixels_per_tick is <5, only draw every 10 ticks */
+ if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
+ continue;
+ }
+
+ if(current_interval%interval_delta){
+ xlen=5;
+ } else {
+ xlen=17;
+ }
+
+ x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, x-1-user_data->dlg.dialog_graph.pixels_per_tick/2+0.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+1.5);
+ cairo_line_to(cr, x-1-user_data->dlg.dialog_graph.pixels_per_tick/2+0.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+xlen+1.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+ if(xlen==17){
+ if(user_data->dlg.dialog_graph.interval>=1000){
+ g_snprintf(label_string, sizeof(label_string), "%ds", current_interval/1000);
+ } else if(user_data->dlg.dialog_graph.interval>=100){
+ g_snprintf(label_string, sizeof(label_string),
+ "%d.%1ds", current_interval/1000,(current_interval/100)%10);
+ } else if(user_data->dlg.dialog_graph.interval>=10){
+ g_snprintf(label_string, sizeof(label_string),
+ "%d.%2ds", current_interval/1000,(current_interval/10)%100);
+ } else {
+ g_snprintf(label_string, sizeof(label_string),
+ "%d.%3ds", current_interval/1000,current_interval%1000);
+ }
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_move_to (cr,
+ x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
+ user_data->dlg.dialog_graph.surface_height-bottom_y_border+20);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+
+ }
+
+
+
+
+
+
+ /*
+ * Draw "x" for Sequence Errors and "m" for Marks
+ */
+ /* Draw the labels Fwd and Rev */
+ g_strlcpy(label_string, UTF8_LEFTWARDS_ARROW "Fwd",sizeof(label_string));
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_move_to (cr,
+ user_data->dlg.dialog_graph.surface_width-right_x_border+33-lwidth,
+ user_data->dlg.dialog_graph.surface_height-bottom_y_border+3);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+
+ g_strlcpy(label_string, UTF8_LEFTWARDS_ARROW "Rev",sizeof(label_string));
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_move_to (cr,
+ user_data->dlg.dialog_graph.surface_width-right_x_border+33-lwidth,
+ user_data->dlg.dialog_graph.surface_height-bottom_y_border+3+9);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+
+ /* Draw the marks */
+ for(i=MAX_GRAPHS-1;i>=0;i--){
+ guint32 interval;
+ guint32 x_pos/*, prev_x_pos*/;
+
+ /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
+ if (!user_data->dlg.dialog_graph.graph[i].display){
+ continue;
+ }
+ /* initialize prev x/y to the low left corner of the graph */
+ /* prev_x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-first_interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border; */
+
+ for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
+ x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
+
+ if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
+ if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
+ g_strlcpy(label_string, "x", sizeof(label_string));
+ } else {
+ g_strlcpy(label_string, "m", sizeof(label_string));
+ }
+
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_move_to (cr,
+ x_pos-1-lwidth/2,
+ user_data->dlg.dialog_graph.surface_height-bottom_y_border+3+7*(i/2));
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+
+ /* prev_x_pos=x_pos; */
+ }
+ }
+
+ g_object_unref(G_OBJECT(layout));
+
+ /*
+ * Loop over all graphs and draw them
+ */
+ for(i=MAX_GRAPHS-1;i>=0;i--){
+ guint32 interval;
+ guint32 x_pos, y_pos, /*prev_x_pos,*/ prev_y_pos;
+ if (!user_data->dlg.dialog_graph.graph[i].display){
+ continue;
+ }
+ /* initialize prev x/y to the low left corner of the graph */
+ /* prev_x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-first_interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border; */
+ prev_y_pos=draw_height-1+top_y_border;
+
+ for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
+ guint32 val;
+ x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
+ val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
+ if(val>max_y){
+ y_pos=0;
+ } else {
+ y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
+ }
+
+ /* dont need to draw anything if the segment
+ * is entirely above the top of the graph
+ */
+ if( (prev_y_pos==0) && (y_pos==0) ){
+ prev_y_pos=y_pos;
+ /* prev_x_pos=x_pos; */
+ continue;
+ }
+
+ if(val){
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ gdk_cairo_set_source_color (cr, &user_data->dlg.dialog_graph.graph[i].color);
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, x_pos+0.5, draw_height-1+top_y_border+0.5);
+ cairo_line_to(cr, x_pos+0.5, y_pos+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ }
+
+ prev_y_pos=y_pos;
+ /* prev_x_pos=x_pos; */
+ }
+ }
+
+
+ cr = gdk_cairo_create (gtk_widget_get_window(user_data->dlg.dialog_graph.draw_area));
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, user_data->dlg.dialog_graph.pixmap, 0, 0);
+#endif
+ cairo_rectangle (cr, 0, 0, user_data->dlg.dialog_graph.surface_width, user_data->dlg.dialog_graph.surface_height);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+
+
+ /* update the scrollbar */
+ gtk_adjustment_set_upper(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) user_data->dlg.dialog_graph.max_interval);
+ gtk_adjustment_set_step_increment(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) ((last_interval-first_interval)/10));
+ gtk_adjustment_set_page_increment(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) (last_interval-first_interval));
+ if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
+ gtk_adjustment_set_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) (user_data->dlg.dialog_graph.max_interval/100));
+ } else {
+ gtk_adjustment_set_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) (last_interval-first_interval));
+ }
+ gtk_adjustment_set_value(user_data->dlg.dialog_graph.scrollbar_adjustment, last_interval - gtk_adjustment_get_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment));
+ gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
+ gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
+
+}
+
+/****************************************************************************/
+static void dialog_graph_redraw(user_data_t* user_data)
+{
+ user_data->dlg.dialog_graph.needs_redraw=TRUE;
+ dialog_graph_draw(user_data);
+}
+
+/****************************************************************************/
+
+static void draw_area_destroy_cb(GtkWidget *widget _U_, gpointer data)
+{
+ user_data_t *user_data = data;
+
+ user_data->dlg.dialog_graph.window = NULL;
+}
+
+/****************************************************************************/
+#if GTK_CHECK_VERSION(3,0,0)
+static gboolean draw_area_draw(GtkWidget *widget, cairo_t *cr, gpointer data)
+{
+ user_data_t *user_data = data;
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
+ cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
+ cairo_fill (cr);
+
+ return FALSE;
+}
+#else
+static gboolean draw_area_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
+{
+ user_data_t *user_data = data;
+ cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(widget));
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, user_data->dlg.dialog_graph.pixmap, 0, 0);
+#endif
+ cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+#endif
+/****************************************************************************/
+static gboolean draw_area_configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer data)
+{
+ user_data_t *user_data = data;
+ GtkAllocation widget_alloc;
+ cairo_t *cr;
+
+#if GTK_CHECK_VERSION(2,22,0)
+ if(user_data->dlg.dialog_graph.surface){
+ g_object_unref(user_data->dlg.dialog_graph.surface);
+ user_data->dlg.dialog_graph.surface=NULL;
+ }
+ gtk_widget_get_allocation(widget, &widget_alloc);
+ user_data->dlg.dialog_graph.surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
+ CAIRO_CONTENT_COLOR,
+ widget_alloc.width,
+ widget_alloc.height);
+#else
+ if(user_data->dlg.dialog_graph.pixmap){
+ g_object_unref(user_data->dlg.dialog_graph.pixmap);
+ user_data->dlg.dialog_graph.pixmap=NULL;
+ }
+
+ gtk_widget_get_allocation(widget, &widget_alloc);
+ user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(gtk_widget_get_window(widget),
+ widget_alloc.width,
+ widget_alloc.height,
+ -1);
+#endif
+ user_data->dlg.dialog_graph.surface_width=widget_alloc.width;
+ user_data->dlg.dialog_graph.surface_height=widget_alloc.height;
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+ dialog_graph_redraw(user_data);
+ return TRUE;
+}
+
+/****************************************************************************/
+static void scrollbar_changed(GtkWidget *widget _U_, gpointer data)
+{
+ user_data_t *user_data = data;
+ guint32 mi;
+
+ mi=(guint32) (gtk_adjustment_get_value(user_data->dlg.dialog_graph.scrollbar_adjustment) + gtk_adjustment_get_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment));
+ if(user_data->dlg.dialog_graph.last_interval==mi){
+ return;
+ }
+ if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
+ && (mi==user_data->dlg.dialog_graph.max_interval) ){
+ return;
+ }
+
+ user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
+
+ dialog_graph_redraw(user_data);
+ return;
+}
+
+/****************************************************************************/
+static void create_draw_area(user_data_t* user_data, GtkWidget *box)
+{
+ user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
+ g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(draw_area_destroy_cb), user_data);
+
+ gtk_widget_set_size_request(user_data->dlg.dialog_graph.draw_area, user_data->dlg.dialog_graph.surface_width, user_data->dlg.dialog_graph.surface_height);
+
+ /* signals needed to handle backing pixmap */
+#if GTK_CHECK_VERSION(3,0,0)
+ g_signal_connect(user_data->dlg.dialog_graph.draw_area, "draw", G_CALLBACK(draw_area_draw), user_data);
+#else
+ g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(draw_area_expose_event), user_data);
+#endif
+ g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(draw_area_configure_event), user_data);
+
+ gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
+ gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
+
+ /* create the associated scrollbar */
+ user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
+ user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
+ gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
+ gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
+ g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), user_data);
+}
+
+/****************************************************************************/
+static void disable_graph(dialog_graph_graph_t *dgg)
+{
+ if (dgg->display) {
+ dgg->display=FALSE;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
+ FALSE);
+ }
+}
+
+/****************************************************************************/
+static void filter_box_display_button_cb(GtkWidget *widget _U_, gpointer data)
+{
+ dialog_graph_graph_t *dgg = data;
+
+ /* this graph is not active, just update display and redraw */
+ if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
+ disable_graph(dgg);
+ dialog_graph_redraw(dgg->ud);
+ return;
+ }
+
+ enable_graph(dgg);
+ cf_retap_packets(&cfile);
+ dialog_graph_redraw(dgg->ud);
+
+ return;
+}
+
+/****************************************************************************/
+static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
+{
+ GtkWidget *hbox;
+ GtkWidget *label;
+ char str[256];
+
+ hbox=gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(box), hbox);
+ gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
+ gtk_widget_show(hbox);
+
+ g_snprintf(str, sizeof(str), "Graph %d", num);
+ dgg->display_button=gtk_toggle_button_new_with_label(str);
+ gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
+ gtk_widget_show(dgg->display_button);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
+ g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_box_display_button_cb), dgg);
+
+ label=gtk_label_new(dgg->title);
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_color(label, GTK_STATE_NORMAL, &dgg->rgba_color);
+ gtk_widget_override_color(label, GTK_STATE_ACTIVE, &dgg->rgba_color);
+ gtk_widget_override_color(label, GTK_STATE_PRELIGHT, &dgg->rgba_color);
+ gtk_widget_override_color(label, GTK_STATE_SELECTED, &dgg->rgba_color);
+ gtk_widget_override_color(label, GTK_STATE_INSENSITIVE, &dgg->rgba_color);
+#else
+ gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
+ gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
+ gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
+ gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
+ gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
+#endif
+ return;
+}
+
+/****************************************************************************/
+static void create_filter_area(user_data_t* user_data, GtkWidget *box)
+{
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ int i;
+ GtkWidget *label;
+
+ frame=gtk_frame_new("Graphs");
+ gtk_container_add(GTK_CONTAINER(box), frame);
+ gtk_widget_show(frame);
+
+ vbox=gtk_vbox_new(FALSE, 1);
+ gtk_container_add(GTK_CONTAINER(frame), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
+ gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
+ gtk_widget_show(vbox);
+
+ for(i=0;i<MAX_GRAPHS;i++){
+ create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
+ }
+
+ label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ return;
+}
+
+/****************************************************************************/
+static void yscale_select(GtkWidget *item, gpointer data)
+{
+ user_data_t *user_data = data;
+ int i;
+
+ i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
+
+ user_data->dlg.dialog_graph.max_y_units_index=i;
+ user_data->dlg.dialog_graph.max_y_units=yscale_max[i];
+ dialog_graph_redraw(user_data);
+}
+
+/****************************************************************************/
+static void pixels_per_tick_select(GtkWidget *item, gpointer data)
+{
+ user_data_t *user_data = data;
+ int i;
+
+ i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
+
+ user_data->dlg.dialog_graph.pixels_per_tick_index=i;
+ user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[i];
+ dialog_graph_redraw(user_data);
+}
+
+/****************************************************************************/
+static void tick_interval_select(GtkWidget *item, gpointer data)
+{
+ user_data_t *user_data = data;
+ int i;
+
+ i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
+
+ user_data->dlg.dialog_graph.interval_index=i;
+ user_data->dlg.dialog_graph.interval=tick_interval_values[i];
+ cf_retap_packets(&cfile);
+ dialog_graph_redraw(user_data);
+}
+
+/****************************************************************************/
+static GtkWidget *
+create_yscale_max_menu_items(user_data_t* user_data)
+{
+ char str[15];
+ GtkWidget *combo_box;
+ int i;
+
+ combo_box = gtk_combo_box_text_new();
+
+ for(i=0;i<MAX_YSCALE;i++){
+ if(yscale_max[i]==AUTO_MAX_YSCALE){
+ g_strlcpy(str,"Auto",sizeof(str));
+ } else if (yscale_max[i] < 1000000) {
+ g_snprintf(str, sizeof(str), "%u ms", yscale_max[i]/1000);
+ } else {
+ g_snprintf(str, sizeof(str), "%u s", yscale_max[i]/1000000);
+ }
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
+ }
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.max_y_units_index);
+ g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), user_data);
+
+ return combo_box;
+}
+
+/****************************************************************************/
+static GtkWidget *
+create_pixels_per_tick_menu_items(user_data_t *user_data)
+{
+ char str[5];
+ GtkWidget *combo_box;
+ int i;
+
+ combo_box = gtk_combo_box_text_new();
+
+ for(i=0;i<MAX_PIXELS_PER_TICK;i++){
+ g_snprintf(str, sizeof(str), "%u", pixels_per_tick[i]);
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
+ }
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.pixels_per_tick_index);
+
+ g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), user_data);
+
+ return combo_box;
+}
+
+/****************************************************************************/
+static GtkWidget *
+create_tick_interval_menu_items(user_data_t *user_data)
+{
+ GtkWidget *combo_box;
+ char str[15];
+ int i;
+
+ combo_box = gtk_combo_box_text_new();
+
+ for(i=0;i<MAX_TICK_VALUES;i++){
+ if(tick_interval_values[i]>=1000){
+ g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
+ } else if(tick_interval_values[i]>=100){
+ g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
+ } else if(tick_interval_values[i]>=10){
+ g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
+ } else {
+ g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
+ }
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
+ }
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.interval_index);
+ g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), user_data);
+
+ return combo_box;
+}
+
+/****************************************************************************/
+static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, GtkWidget *(*func)(user_data_t* user_data))
+{
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *combo_box;
+
+ hbox=gtk_hbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(box), hbox);
+ gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
+ gtk_widget_show(hbox);
+
+ label=gtk_label_new(name);
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+ combo_box = (*func)(user_data);
+ gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
+ gtk_widget_show(combo_box);
+}
+
+/****************************************************************************/
+static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
+{
+ GtkWidget *frame_vbox;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+
+ frame_vbox=gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(box), frame_vbox);
+ gtk_widget_show(frame_vbox);
+
+ frame = gtk_frame_new("X Axis");
+ gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
+ gtk_widget_show(frame);
+
+ vbox=gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(frame), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
+ gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
+ gtk_widget_show(vbox);
+
+ create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
+ create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
+
+ frame = gtk_frame_new("Y Axis");
+ gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
+ gtk_widget_show(frame);
+
+ vbox=gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(frame), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
+ gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
+ gtk_widget_show(vbox);
+
+ create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
+
+ return;
+}
+
+/****************************************************************************/
+static void dialog_graph_init_window(user_data_t* user_data)
+{
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *bt_close;
+
+ /* create the main window */
+ user_data->dlg.dialog_graph.window=dlg_window_new("I/O Graphs"); /* transient_for top_level */
+
+ vbox=gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
+ gtk_widget_show(vbox);
+
+ create_draw_area(user_data, vbox);
+
+ hbox=gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
+ gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
+ gtk_widget_show(hbox);
+
+ create_filter_area(user_data, hbox);
+ create_ctrl_area(user_data, hbox);
+
+ dialog_graph_set_title(user_data);
+
+ hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show(hbox);
+
+ bt_close = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
+
+ g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+
+ gtk_widget_show(user_data->dlg.dialog_graph.window);
+ window_present(user_data->dlg.dialog_graph.window);
+
+}
+
+
+/****************************************************************************/
+static void on_graph_bt_clicked(GtkWidget *bt _U_, gpointer data)
+{
+ user_data_t *user_data = data;
+
+ if (user_data->dlg.dialog_graph.window != NULL) {
+ /* There's already a graph window; reactivate it. */
+ reactivate_window(user_data->dlg.dialog_graph.window);
+ return;
+ }
+
+ dialog_graph_init_window(user_data);
+
+}
+
+/****************************************************************************/
+static void on_goto_bt_clicked(GtkWidget *bt _U_, gpointer data)
+{
+ user_data_t *user_data = data;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ guint fnumber;
+
+ selection = user_data->dlg.selected_list_sel;
+
+ if (selection==NULL)
+ return;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)){
+ gtk_tree_model_get (model, &iter, PACKET_COLUMN, &fnumber, -1);
+ cf_goto_frame(&cfile, fnumber);
+ }
+}
+
+
+static void draw_stat(user_data_t *user_data);
+
+/****************************************************************************/
+/* re-dissects all packets */
+static void on_refresh_bt_clicked(GtkWidget *bt _U_, gpointer data)
+{
+ user_data_t *user_data = data;
+ GString *error_string;
+
+ /* remove tap listener */
+ protect_thread_critical_region();
+ remove_tap_listener(user_data);
+ unprotect_thread_critical_region();
+
+ /* register tap listener */
+ error_string = register_tap_listener("IAX2", user_data, NULL, 0,
+ iax2_reset, iax2_packet, iax2_draw);
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ return;
+ }
+
+ /* retap all packets */
+ cf_retap_packets(&cfile);
+
+ /* draw statistics info */
+ draw_stat(user_data);
+
+}
+
+/****************************************************************************/
+static void on_next_bt_clicked(GtkWidget *bt _U_, gpointer data)
+{
+ user_data_t *user_data = data;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ gchar *text;
+ GtkTreeSelection *selection;
+ GtkTreePath *path;
+
+ selection = user_data->dlg.selected_list_sel;
+
+ if (selection==NULL)
+ return;
+
+try_again:
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)){
+ while (gtk_tree_model_iter_next (model,&iter)) {
+ gtk_tree_model_get (model, &iter, STATUS_COLUMN, &text, -1);
+ if (strcmp(text, OK_TEXT) != 0) {
+ gtk_tree_selection_select_iter (selection, &iter);
+ path = gtk_tree_model_get_path(model, &iter);
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data->dlg.selected_list),
+ path,
+ NULL, FALSE, 0, 0);
+ gtk_tree_path_free(path);
+ g_free (text);
+ return;
+ }
+ g_free (text);
+ }
+ /* wrap around */
+ if (user_data->dlg.number_of_nok>1){
+ /* Get the first iter and select it before starting over */
+ gtk_tree_model_get_iter_first(model, &iter);
+ gtk_tree_selection_select_iter (selection, &iter);
+ goto try_again;
+ }
+ }
+}
+
+/****************************************************************************/
+/* when we want to save the information */
+static gboolean save_csv_as_ok_cb(GtkWidget *w _U_, gpointer fc /*user_data_t *user_data*/)
+{
+ gchar *g_dest;
+ GtkWidget *rev, *forw, *both;
+ user_data_t *user_data;
+
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ gboolean more_items = TRUE;
+
+ /* To Hold data from the list row */
+ guint packet; /* Packet */
+ gfloat delta; /* Delta(ms) */
+ gfloat jitter; /* Jitter(ms) */
+ gfloat ipbw; /* IP BW(kbps) */
+ char *status_str; /* Status */
+ char *date_str; /* Date */
+ guint length; /* Length */
+
+ FILE *fp;
+ int j;
+
+ g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
+
+ /* Perhaps the user specified a directory instead of a file.
+ * Check whether they did.
+ */
+ if (test_for_directory(g_dest) == EISDIR) {
+ /* It's a directory - set the file selection box to display it. */
+ set_last_open_dir(g_dest);
+ file_selection_set_current_folder(fc, get_last_open_dir());
+ gtk_file_chooser_set_current_name(fc, "");
+ g_free(g_dest);
+ return FALSE; /* run the dialog again */
+ }
+ rev = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "reversed_rb");
+ forw = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "forward_rb");
+ both = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "both_rb");
+ user_data = (user_data_t*)g_object_get_data(G_OBJECT(fc), "user_data");
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(forw)) || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
+ fp = ws_fopen(g_dest, "w");
+ if (fp == NULL) {
+ open_failure_alert_box(g_dest, errno, TRUE);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
+ fprintf(fp, "Forward\n");
+ if (ferror(fp)) {
+ write_failure_alert_box(g_dest, errno);
+ fclose(fp);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ }
+
+ for(j = 0; j < NUM_COLS; j++) {
+ if (j == 0) {
+ fprintf(fp,"\"%s\"",titles[j]);
+ } else {
+ fprintf(fp,",\"%s\"",titles[j]);
+ }
+ }
+ fprintf(fp,"\n");
+ if (ferror(fp)) {
+ write_failure_alert_box(g_dest, errno);
+ fclose(fp);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd));
+ store = GTK_LIST_STORE(model);
+ if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
+
+ while (more_items) {
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
+ 0, &packet,
+ 1, &delta,
+ 2, &jitter,
+ 3, &ipbw,
+ 4, &status_str,
+ 5, &date_str,
+ 6, &length,
+ -1);
+ fprintf(fp, "\"%u\"",packet);
+ fprintf(fp, ",\"%.2f\"", delta);
+ fprintf(fp, ",\"%.2f\"", jitter);
+ fprintf(fp, ",\"%.2f\"", ipbw);
+ fprintf(fp, ",\"%s\"", status_str);
+ fprintf(fp, ",\"%s\"", date_str);
+ fprintf(fp, ",\"%u\"", length);
+ fprintf(fp,"\n");
+ g_free(status_str);
+ g_free(date_str);
+ if (ferror(fp)) {
+ write_failure_alert_box(g_dest, errno);
+ fclose(fp);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+
+ more_items = gtk_tree_model_iter_next (model,&iter);
+ }
+ }
+
+ if (fclose(fp) == EOF) {
+ write_failure_alert_box(g_dest, errno);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ }
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rev)) || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
+ fp = ws_fopen(g_dest, "a");
+ if (fp == NULL) {
+ open_failure_alert_box(g_dest, errno, TRUE);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ fprintf(fp, "\nReverse\n");
+ if (ferror(fp)) {
+ write_failure_alert_box(g_dest, errno);
+ fclose(fp);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ } else {
+ fp = ws_fopen(g_dest, "w");
+ if (fp == NULL) {
+ open_failure_alert_box(g_dest, errno, TRUE);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ }
+ for(j = 0; j < NUM_COLS; j++) {
+ if (j == 0) {
+ fprintf(fp,"\"%s\"",titles[j]);
+ } else {
+ fprintf(fp,",\"%s\"",titles[j]);
+ }
+ }
+ fprintf(fp,"\n");
+ if (ferror(fp)) {
+ write_failure_alert_box(g_dest, errno);
+ fclose(fp);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev));
+ store = GTK_LIST_STORE(model);
+ if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
+
+ more_items = TRUE;
+
+ while (more_items) {
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
+ 0, &packet,
+ 1, &delta,
+ 2, &jitter,
+ 3, &ipbw,
+ 4, &status_str,
+ 5, &date_str,
+ 6, &length,
+ -1);
+ fprintf(fp, "\"%u\"",packet);
+ fprintf(fp, ",\"%.2f\"", delta);
+ fprintf(fp, ",\"%.2f\"", jitter);
+ fprintf(fp, ",\"%.2f\"", ipbw);
+ fprintf(fp, ",\"%s\"", status_str);
+ fprintf(fp, ",\"%s\"", date_str);
+ fprintf(fp, ",\"%u\"", length);
+ fprintf(fp,"\n");
+ g_free(status_str);
+ g_free(date_str);
+ if (ferror(fp)) {
+ write_failure_alert_box(g_dest, errno);
+ fclose(fp);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+
+ more_items = gtk_tree_model_iter_next (model,&iter);
+ }
+ }
+ if (fclose(fp) == EOF) {
+ write_failure_alert_box(g_dest, errno);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ }
+ g_free(g_dest);
+ return TRUE; /* we're done */
+}
+
+static void save_csv_as_destroy_cb(GtkWidget *win _U_, gpointer data)
+{
+ user_data_t *user_data = data;
+
+ user_data->dlg.save_csv_as_w = NULL;
+}
+
+/* when the user wants to save the csv information in a file */
+static void save_csv_as_cb(GtkWidget *bt _U_, gpointer data)
+{
+ user_data_t *user_data = data;
+ GtkWidget *vertb;
+ GtkWidget *table1;
+ GtkWidget *label_format;
+ GtkWidget *channels_label;
+ GtkWidget *forward_rb;
+ GtkWidget *reversed_rb;
+ GtkWidget *both_rb;
+
+#if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
+ if (user_data->dlg.save_csv_as_w != NULL) {
+ /* There's already a Save CSV info dialog box; reactivate it. */
+ reactivate_window(user_data->dlg.save_csv_as_w);
+ return;
+ }
+#endif
+ user_data->dlg.save_csv_as_w =
+ gtk_file_chooser_dialog_new("Wireshark: Save Data As CSV",
+ GTK_WINDOW(user_data->dlg.notebook),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NULL);
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), TRUE);
+ gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_csv_as_w),GTK_WINDOW(user_data->dlg.window));
+
+ /* Build our "extra widget" to be added to the file chooser widget */
+ /* Container for each row of widgets */
+ vertb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
+ gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), vertb);
+ gtk_widget_show (vertb);
+
+ table1 = gtk_table_new (2, 4, FALSE);
+ gtk_widget_show (table1);
+ gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
+ gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
+
+ label_format = gtk_label_new ("Format: Comma Separated Values");
+ gtk_widget_show (label_format);
+ gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
+
+ channels_label = gtk_label_new ("Channels: ");
+ gtk_widget_show (channels_label);
+ gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
+
+ forward_rb = gtk_radio_button_new_with_label (NULL, "forward ");
+ gtk_widget_show (forward_rb);
+ gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ reversed_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "reversed ");
+ gtk_widget_show (reversed_rb);
+ gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ both_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "both");
+ gtk_widget_show (both_rb);
+ gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
+
+ g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "forward_rb", forward_rb);
+ g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "reversed_rb", reversed_rb);
+ g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "both_rb", both_rb);
+ g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "user_data", user_data);
+
+ g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event",
+ G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
+ G_CALLBACK(save_csv_as_destroy_cb), user_data);
+
+ gtk_widget_show(user_data->dlg.save_csv_as_w);
+ window_present(user_data->dlg.save_csv_as_w);
+
+ /* "Run" the GtkFileChooserDialog. */
+ /* Upon exit: If "Accept" run the OK callback. */
+ /* If the OK callback returns with a FALSE status, re-run the dialog.*/
+ /* Destroy the window. */
+ /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
+ /* return with a TRUE status so that the dialog window will be destroyed. */
+ /* Trying to re-run the dialog after popping up an alert box will not work */
+ /* since the user will not be able to dismiss the alert box. */
+ /* The (somewhat unfriendly) effect: the user must re-invoke the */
+ /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
+ /* */
+ /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
+ /* GtkFileChooserDialog. */
+ while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_csv_as_w)) == GTK_RESPONSE_ACCEPT) {
+ if (save_csv_as_ok_cb(NULL, user_data->dlg.save_csv_as_w)) {
+ break; /* we're done */
+ }
+ }
+ window_destroy(user_data->dlg.save_csv_as_w);
+}
+
+/****************************************************************************/
+static void save_voice_as_destroy_cb(GtkWidget *win _U_, gpointer data)
+{
+ user_data_t *user_data = data;
+
+ /* Note that we no longer have a Save voice info dialog box. */
+ user_data->dlg.save_voice_as_w = NULL;
+}
+
+/****************************************************************************/
+/* here we save it into a file that user specified */
+/* XXX what about endians here? could go something wrong? */
+static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
+{
+ int to_fd, forw_fd, rev_fd, fread_cnt = 0, rread = 0, fwritten, rwritten;
+ gchar f_pd[1] = {0};
+ gchar r_pd[1] = {0};
+ gint16 sample;
+ gchar pd[4];
+ guint32 f_write_silence = 0;
+ guint32 r_write_silence = 0;
+ progdlg_t *progbar;
+ guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
+ gboolean stop_flag = FALSE;
+ /*size_t nchars;*/
+
+ forw_fd = ws_open(user_data->f_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
+ if (forw_fd < 0)
+ return FALSE;
+ rev_fd = ws_open(user_data->r_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
+ if (rev_fd < 0) {
+ ws_close(forw_fd);
+ return FALSE;
+ }
+
+ /* open file for saving */
+ to_fd = ws_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+ if (to_fd < 0) {
+ ws_close(forw_fd);
+ ws_close(rev_fd);
+ return FALSE;
+ }
+
+ progbar = create_progress_dlg("Saving voice in a file", dest, TRUE, &stop_flag);
+
+ if (format == SAVE_AU_FORMAT) /* au format */
+ {
+ /* First we write the .au header. XXX Hope this is endian independant */
+ /* the magic word 0x2e736e64 == .snd */
+ /* XXX: Should we be checking for write errors below ? */
+ phtonl(pd, 0x2e736e64);
+ fwritten = ws_write(to_fd, pd, 4);
+ if ((fwritten < 4) || (fwritten < 0) || (fread_cnt < 0)) {
+ ws_close(forw_fd);
+ ws_close(rev_fd);
+ ws_close(to_fd);
+ destroy_progress_dlg(progbar);
+ return FALSE;
+ }
+ /* header offset == 24 bytes */
+ phtonl(pd, 24);
+ fwritten = ws_write(to_fd, pd, 4);
+ if ((fwritten < 4) || (fwritten < 0) || (fread_cnt < 0)) {
+ ws_close(forw_fd);
+ ws_close(rev_fd);
+ ws_close(to_fd);
+ destroy_progress_dlg(progbar);
+ return FALSE;
+ }
+ /* total length, it is permited to set this to 0xffffffff */
+ phtonl(pd, -1);
+ fwritten = ws_write(to_fd, pd, 4);
+ if ((fwritten < 4) || (fwritten < 0) || (fread_cnt < 0)) {
+ ws_close(forw_fd);
+ ws_close(rev_fd);
+ ws_close(to_fd);
+ destroy_progress_dlg(progbar);
+ return FALSE;
+ }
+ /* encoding format == 16-bit linear PCM */
+ phtonl(pd, 3);
+ fwritten = ws_write(to_fd, pd, 4);
+ if ((fwritten < 4) || (fwritten < 0) || (fread_cnt < 0)) {
+ ws_close(forw_fd);
+ ws_close(rev_fd);
+ ws_close(to_fd);
+ destroy_progress_dlg(progbar);
+ return FALSE;
+ }
+ /* sample rate == 8000 Hz */
+ phtonl(pd, 8000);
+ fwritten = ws_write(to_fd, pd, 4);
+ if ((fwritten < 4) || (fwritten < 0) || (fread_cnt < 0)) {
+ ws_close(forw_fd);
+ ws_close(rev_fd);
+ ws_close(to_fd);
+ destroy_progress_dlg(progbar);
+ return FALSE;
+ }
+ /* channels == 1 */
+ phtonl(pd, 1);
+ fwritten = ws_write(to_fd, pd, 4);
+ if ((fwritten < 4) || (fwritten < 0) || (fread_cnt < 0)) {
+ ws_close(forw_fd);
+ ws_close(rev_fd);
+ ws_close(to_fd);
+ destroy_progress_dlg(progbar);
+ return FALSE;
+ }
+
+ switch (channels) {
+ /* only forward direction */
+ case SAVE_FORWARD_DIRECTION_MASK: {
+ progbar_count = user_data->forward.saveinfo.count;
+ progbar_quantum = user_data->forward.saveinfo.count/100;
+ while ((fread_cnt = read(forw_fd, f_pd, 1)) > 0) {
+ if(stop_flag)
+ break;
+ if((count > progbar_nextstep) && (count <= progbar_count)) {
+ update_progress_dlg(progbar,
+ (gfloat) count/progbar_count, "Saving");
+ progbar_nextstep = progbar_nextstep + progbar_quantum;
+ }
+ count++;
+
+ if (user_data->forward.statinfo.pt == AST_FORMAT_ULAW){
+ sample = ulaw2linear(*f_pd);
+ phtons(pd, sample);
+ }
+ else if(user_data->forward.statinfo.pt == AST_FORMAT_ALAW){
+ sample = alaw2linear(*f_pd);
+ phtons(pd, sample);
+ }
+ else{
+ ws_close(forw_fd);
+ ws_close(rev_fd);
+ ws_close(to_fd);
+ destroy_progress_dlg(progbar);
+ return FALSE;
+ }
+
+ fwritten = ws_write(to_fd, pd, 2);
+ if ((fwritten < 2) || (fwritten < 0) || (fread_cnt < 0)) {
+ ws_close(forw_fd);
+ ws_close(rev_fd);
+ ws_close(to_fd);
+ destroy_progress_dlg(progbar);
+ return FALSE;
+ }
+ }
+ break;
+ }
+ /* only reversed direction */
+ case SAVE_REVERSE_DIRECTION_MASK: {
+ progbar_count = user_data->reversed.saveinfo.count;
+ progbar_quantum = user_data->reversed.saveinfo.count/100;
+ while ((rread = read(rev_fd, r_pd, 1)) > 0) {
+ if(stop_flag)
+ break;
+ if((count > progbar_nextstep) && (count <= progbar_count)) {
+ update_progress_dlg(progbar,
+ (gfloat) count/progbar_count, "Saving");
+ progbar_nextstep = progbar_nextstep + progbar_quantum;
+ }
+ count++;
+
+ if (user_data->reversed.statinfo.pt == AST_FORMAT_ULAW){
+ sample = ulaw2linear(*r_pd);
+ phtons(pd, sample);
+ }
+ else if(user_data->reversed.statinfo.pt == AST_FORMAT_ALAW){
+ sample = alaw2linear(*r_pd);
+ phtons(pd, sample);
+ }
+ else{
+ ws_close(forw_fd);
+ ws_close(rev_fd);
+ ws_close(to_fd);
+ destroy_progress_dlg(progbar);
+ return FALSE;
+ }
+
+ rwritten = ws_write(to_fd, pd, 2);
+ if ((rwritten < 2) || (rwritten < 0) || (rread < 0)) {
+ ws_close(forw_fd);
+ ws_close(rev_fd);
+ ws_close(to_fd);
+ destroy_progress_dlg(progbar);
+ return FALSE;
+ }
+ }
+ break;
+ }
+ /* both directions */
+ case SAVE_BOTH_DIRECTION_MASK: {
+ (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
+ (progbar_count = user_data->forward.saveinfo.count) :
+ (progbar_count = user_data->reversed.saveinfo.count);
+ progbar_quantum = progbar_count/100;
+ /* since conversation in one way can start later than in the other one,
+ * we have to write some silence information for one channel */
+ if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
+ f_write_silence = (guint32)
+ ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
+ }
+ else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
+ r_write_silence = (guint32)
+ ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
+ }
+ for(;;) {
+ if(stop_flag)
+ break;
+ if((count > progbar_nextstep) && (count <= progbar_count)) {
+ update_progress_dlg(progbar,
+ (gfloat) count/progbar_count, "Saving");
+ progbar_nextstep = progbar_nextstep + progbar_quantum;
+ }
+ count++;
+ if(f_write_silence > 0) {
+ rread = read(rev_fd, r_pd, 1);
+ switch (user_data->forward.statinfo.reg_pt) {
+ case AST_FORMAT_ULAW:
+ *f_pd = SILENCE_PCMU;
+ break;
+ case AST_FORMAT_ALAW:
+ *f_pd = SILENCE_PCMA;
+ break;
+ }
+ fread_cnt = 1;
+ f_write_silence--;
+ }
+ else if(r_write_silence > 0) {
+ fread_cnt = read(forw_fd, f_pd, 1);
+ switch (user_data->reversed.statinfo.reg_pt) {
+ case AST_FORMAT_ULAW:
+ *r_pd = SILENCE_PCMU;
+ break;
+ case AST_FORMAT_ALAW:
+ *r_pd = SILENCE_PCMA;
+ break;
+ }
+ rread = 1;
+ r_write_silence--;
+ }
+ else {
+ fread_cnt = read(forw_fd, f_pd, 1);
+ rread = read(rev_fd, r_pd, 1);
+ }
+ if ((rread == 0) && (fread_cnt == 0))
+ break;
+ if ((user_data->forward.statinfo.pt == AST_FORMAT_ULAW) && (user_data->reversed.statinfo.pt == AST_FORMAT_ULAW)){
+ sample = (ulaw2linear(*r_pd) + ulaw2linear(*f_pd)) / 2;
+ phtons(pd, sample);
+ }
+ else if((user_data->forward.statinfo.pt == AST_FORMAT_ALAW) && (user_data->reversed.statinfo.pt == AST_FORMAT_ALAW)){
+ sample = (alaw2linear(*r_pd) + alaw2linear(*f_pd)) / 2;
+ phtons(pd, sample);
+ }
+ else
+ {
+ ws_close(forw_fd);
+ ws_close(rev_fd);
+ ws_close(to_fd);
+ destroy_progress_dlg(progbar);
+ return FALSE;
+ }
+
+
+ rwritten = ws_write(to_fd, pd, 2);
+ if ((rwritten < 2) || (rread < 0) || (fread_cnt < 0)) {
+ ws_close(forw_fd);
+ ws_close(rev_fd);
+ ws_close(to_fd);
+ destroy_progress_dlg(progbar);
+ return FALSE;
+ }
+ }
+ }
+ }
+ }
+ else if (format == SAVE_RAW_FORMAT) /* raw format */
+ {
+ int fd;
+ switch (channels) {
+ /* only forward direction */
+ case SAVE_FORWARD_DIRECTION_MASK: {
+ progbar_count = user_data->forward.saveinfo.count;
+ progbar_quantum = user_data->forward.saveinfo.count/100;
+ fd = forw_fd;
+ break;
+ }
+ /* only reversed direction */
+ case SAVE_REVERSE_DIRECTION_MASK: {
+ progbar_count = user_data->reversed.saveinfo.count;
+ progbar_quantum = user_data->reversed.saveinfo.count/100;
+ fd = rev_fd;
+ break;
+ }
+ default: {
+ ws_close(forw_fd);
+ ws_close(rev_fd);
+ ws_close(to_fd);
+ destroy_progress_dlg(progbar);
+ return FALSE;
+ }
+ }
+
+
+
+ /* XXX how do you just copy the file? */
+ while ((rread = read(fd, pd, 1)) > 0) {
+ if(stop_flag)
+ break;
+ if((count > progbar_nextstep) && (count <= progbar_count)) {
+ update_progress_dlg(progbar,
+ (gfloat) count/progbar_count, "Saving");
+ progbar_nextstep = progbar_nextstep + progbar_quantum;
+ }
+ count++;
+
+ rwritten = ws_write(to_fd, pd, 1);
+
+ if ((rwritten < rread) || (rwritten < 0) || (rread < 0)) {
+ ws_close(forw_fd);
+ ws_close(rev_fd);
+ ws_close(to_fd);
+ destroy_progress_dlg(progbar);
+ return FALSE;
+ }
+ }
+ }
+
+ destroy_progress_dlg(progbar);
+ ws_close(forw_fd);
+ ws_close(rev_fd);
+ ws_close(to_fd);
+ return TRUE;
+}
+
+
+/****************************************************************************/
+/* the user wants to save in a file */
+/* XXX support for different formats is currently commented out */
+static gboolean save_voice_as_ok_cb(GtkWidget *w _U_, gpointer fc)
+{
+ gchar *g_dest;
+ /*GtkWidget *wav, *sw;*/
+ GtkWidget *au, *raw;
+ GtkWidget *rev, *forw, *both;
+ user_data_t *user_data;
+ gint channels , format;
+
+ g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
+
+ /* Perhaps the user specified a directory instead of a file.
+ Check whether they did. */
+ if (test_for_directory(g_dest) == EISDIR) {
+ /* It's a directory - set the file selection box to display it. */
+ set_last_open_dir(g_dest);
+ file_selection_set_current_folder(fc, get_last_open_dir());
+ gtk_file_chooser_set_current_name(fc, "");
+ g_free(g_dest);
+ return FALSE; /* run the dialog again */
+ }
+
+#if 0
+ wav = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "wav_rb");
+ sw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "sw_rb");
+#endif
+ au = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "au_rb");
+ raw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "raw_rb");
+ rev = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "reversed_rb");
+ forw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "forward_rb");
+ both = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "both_rb");
+ user_data = (user_data_t *)g_object_get_data(G_OBJECT(fc), "user_data");
+
+ /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
+ * we don't support that codec. So we pop up a warning. Maybe it would be better to
+ * disable the ok button or disable the buttons for direction if only one is not ok. The
+ * problem is if we open the save voice dialog and then click the refresh button and maybe
+ * the state changes, so we can't save anymore. In this case we should be able to update
+ * the buttons. For now it is easier if we put the warning when the ok button is pressed.
+ */
+
+ /* we can not save in both directions */
+ if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both)))) {
+ /* there are many combinations here, we just exit when first matches */
+ if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
+ (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save in a file: Unsupported codec!");
+ else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
+ (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save in a file: Wrong length of captured packets!");
+ else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
+ (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save in a file: Not all data in all packets was captured!");
+ else
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save in a file: File I/O problem!");
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ /* we can not save forward direction */
+ else if ((user_data->forward.saveinfo.saved == FALSE) && ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (forw))) ||
+ (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both))))) {
+ if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save forward direction in a file: Unsupported codec!");
+ else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save forward direction in a file: Wrong length of captured packets!");
+ else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save forward direction in a file: Not all data in all packets was captured!");
+ else
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save forward direction in a file: File I/O problem!");
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ /* we can not save reversed direction */
+ else if ((user_data->reversed.saveinfo.saved == FALSE) && ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rev))) ||
+ (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both))))) {
+ if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save reversed direction in a file: Unsupported codec!");
+ else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save reversed direction in a file: Wrong length of captured packets!");
+ else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save reversed direction in a file: Not all data in all packets was captured!");
+ else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save reversed direction in a file: No IAX2 data!");
+ else
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save reversed direction in a file: File I/O problem!");
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+
+#if 0
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (wav)))
+ format = SAVE_WAV_FORMAT;
+ else
+#endif
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (au)))
+ format = SAVE_AU_FORMAT;
+#if 0
+ else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (sw)))
+ format = SAVE_SW_FORMAT;
+#endif
+ else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (raw)))
+ format = SAVE_RAW_FORMAT;
+ else
+ format = SAVE_NONE_FORMAT;
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rev)))
+ channels = SAVE_REVERSE_DIRECTION_MASK;
+ else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both)))
+ channels = SAVE_BOTH_DIRECTION_MASK;
+ else
+ channels = SAVE_FORWARD_DIRECTION_MASK;
+
+ /* direction/format validity*/
+ if (format == SAVE_AU_FORMAT)
+ {
+ /* make sure streams are alaw/ulaw */
+ if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != AST_FORMAT_ALAW) && (user_data->forward.statinfo.pt != AST_FORMAT_ULAW)){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != AST_FORMAT_ALAW) && (user_data->reversed.statinfo.pt != AST_FORMAT_ULAW)){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ /* make sure pt's don't differ */
+ if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save in a file: Forward and reverse direction differ in type");
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ }
+ else if (format == SAVE_RAW_FORMAT)
+ {
+ /* can't save raw in both directions */
+ if (channels == SAVE_BOTH_DIRECTION_MASK){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save in a file: Unable to save raw data in both directions");
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ }
+ else
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save in a file: Invalid save format");
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+
+ if(!copy_file(g_dest, channels, format, user_data)) {
+ /* XXX - report the error type! */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "An error occurred while saving voice in a file!");
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ g_free(g_dest);
+ return TRUE; /* we're done */
+}
+
+/****************************************************************************/
+/* when the user wants to save the voice information in a file */
+/* XXX support for different formats is currently commented out */
+static void save_voice_as_cb(GtkWidget *bt _U_, gpointer data)
+{
+ user_data_t *user_data = data;
+ GtkWidget *vertb;
+ GtkWidget *table1;
+ GtkWidget *label_format;
+ GtkWidget *channels_label;
+ GtkWidget *forward_rb;
+ GtkWidget *reversed_rb;
+ GtkWidget *both_rb;
+ /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
+ GtkWidget *au_rb;
+ GtkWidget *raw_rb;
+
+ /* if we can't save in a file: wrong codec, cut packets or other errors */
+ /* should the error arise here or later when you click ok button ?
+ * if we do it here, then we must disable the refresh button, so we don't do it here */
+
+#if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
+ if (user_data->dlg.save_voice_as_w != NULL) {
+ /* There's already a Save voice info dialog box; reactivate it. */
+ reactivate_window(user_data->dlg.save_voice_as_w);
+ return;
+ }
+#endif
+ /* XXX - use file_selection from dlg_utils instead! */
+ user_data->dlg.save_voice_as_w =
+ gtk_file_chooser_dialog_new("Wireshark: Save Payload As ...",
+ GTK_WINDOW(user_data->dlg.notebook),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NULL);
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), TRUE);
+ gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_voice_as_w),GTK_WINDOW(user_data->dlg.window));
+
+ /* Container for each row of widgets */
+ vertb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
+ gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), vertb);
+ gtk_widget_show (vertb);
+
+ table1 = gtk_table_new (2, 4, FALSE);
+ gtk_widget_show (table1);
+ gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
+ gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
+
+#if 0
+ label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
+ gtk_widget_show (label_format);
+ gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+#endif
+
+ label_format = gtk_label_new ("Format: ");
+ gtk_widget_show (label_format);
+ gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
+
+ raw_rb = gtk_radio_button_new_with_label (NULL, ".raw");
+ gtk_widget_show (raw_rb);
+ gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+
+ au_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".au");
+ gtk_widget_show (au_rb);
+ gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+#if 0
+ /* we support .au - ulaw*/
+ wav_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".wav");
+ gtk_widget_show (wav_rb);
+ gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ sw_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), "8 kHz, 16 bit ");
+ gtk_widget_show (sw_rb);
+ gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ au_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".au");
+ gtk_widget_show (au_rb);
+ gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+#endif
+
+ channels_label = gtk_label_new ("Channels: ");
+ gtk_widget_show (channels_label);
+ gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
+
+ forward_rb = gtk_radio_button_new_with_label (NULL, "forward ");
+ gtk_widget_show (forward_rb);
+ gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ reversed_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "reversed ");
+ gtk_widget_show (reversed_rb);
+ gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ both_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "both");
+ gtk_widget_show (both_rb);
+ gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(forward_rb), TRUE);
+
+#if 0
+ /* if one direction is nok we don't allow saving
+ XXX this is not ok since the user can click the refresh button and cause changes
+ but we can not update this window. So we move all the decision on the time the ok
+ button is clicked */
+ if (user_data->forward.saved == FALSE) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
+ gtk_widget_set_sensitive(forward_rb, FALSE);
+ gtk_widget_set_sensitive(both_rb, FALSE);
+ }
+ else if (user_data->reversed.saved == FALSE) {
+ gtk_widget_set_sensitive(reversed_rb, FALSE);
+ gtk_widget_set_sensitive(both_rb, FALSE);
+ }
+#endif
+
+ /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "wav_rb", wav_rb);*/
+ g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "au_rb", au_rb);
+ /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "sw_rb", sw_rb);*/
+ g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "raw_rb", raw_rb);
+ g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "forward_rb", forward_rb);
+ g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "reversed_rb", reversed_rb);
+ g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "both_rb", both_rb);
+ g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "user_data", user_data);
+
+ g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
+ G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
+ G_CALLBACK(save_voice_as_destroy_cb), user_data);
+
+ gtk_widget_show(user_data->dlg.save_voice_as_w);
+ window_present(user_data->dlg.save_voice_as_w);
+
+ /* "Run" the GtkFileChooserDialog. */
+ /* Upon exit: If "Accept" run the OK callback. */
+ /* If the OK callback returns with a FALSE status, re-run the dialog.*/
+ /* Destroy the window. */
+ /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
+ /* return with a TRUE status so that the dialog window will be destroyed. */
+ /* Trying to re-run the dialog after popping up an alert box will not work */
+ /* since the user will not be able to dismiss the alert box. */
+ /* The (somewhat unfriendly) effect: the user must re-invoke the */
+ /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
+ /* */
+ /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
+ /* GtkFileChooserDialog. */
+ while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_voice_as_w)) == GTK_RESPONSE_ACCEPT) {
+ if (save_voice_as_ok_cb(NULL, user_data->dlg.save_voice_as_w)) {
+ break; /* we're done */
+ }
+ }
+ window_destroy(user_data->dlg.save_voice_as_w);
+}
+
+
+/****************************************************************************/
+/* when we are finished with redisection, we add the label for the statistic */
+static void draw_stat(user_data_t *user_data)
+{
+ gchar label_max[200];
+
+ g_snprintf(label_max, sizeof(label_max), "Total IAX2 packets = %u Max delta = %f sec at packet no. %u",
+ user_data->forward.statinfo.total_nr,
+ user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr);
+
+ gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
+
+ g_snprintf(label_max, sizeof(label_max), "Total IAX2 packets = %u Max delta = %f sec at packet no. %u",
+ user_data->reversed.statinfo.total_nr,
+ user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr);
+
+ gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
+
+ return ;
+}
+
+
+
+/****************************************************************************/
+/* append a line to list */
+static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number,
+ double delta, double jitter, double bandwidth, gchar *status,
+ gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags)
+{
+ GtkListStore *list_store;
+
+ if (strcmp(status, OK_TEXT) != 0) {
+ user_data->dlg.number_of_nok++;
+ }
+
+ list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (list))); /* Get store */
+
+ /* Creates a new row at position. iter will be changed to point to this new row.
+ * If position is larger than the number of rows on the list, then the new row will be appended to the list.
+ * The row will be filled with the values given to this function.
+ * :
+ * should generally be preferred when inserting rows in a sorted list store.
+ */
+ gtk_list_store_insert_with_values( list_store , &user_data->dlg.iter, G_MAXINT,
+ PACKET_COLUMN, number,
+ DELTA_COLUMN, delta,
+ JITTER_COLUMN, jitter,
+ IPBW_COLUMN, bandwidth,
+ STATUS_COLUMN, (char *)status,
+ DATE_COLUMN, (char *)timeStr,
+ LENGTH_COLUMN, pkt_len,
+ FOREGROUND_COLOR_COL, NULL,
+ BACKGROUND_COLOR_COL, (char *)color_str,
+ -1);
+
+ if(flags & STAT_FLAG_FIRST){
+ /* Set first row as active */
+ gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), &user_data->dlg.iter);
+ }
+}
+
+/****************************************************************************
+ * Functions needed to present values from the list
+ */
+
+/* Present floats with two decimals */
+static void
+iax2_float_data_func (GtkTreeViewColumn *column _U_,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gfloat float_val;
+ gchar buf[20];
+ char *savelocale;
+
+ /* the col to get data from is in userdata */
+ gint float_col = GPOINTER_TO_INT(user_data);
+
+ gtk_tree_model_get(model, iter, float_col, &float_val, -1);
+
+ /* save the current locale */
+ savelocale = setlocale(LC_NUMERIC, NULL);
+ /* switch to "C" locale to avoid problems with localized decimal separators
+ * in g_snprintf("%f") functions
+ */
+ setlocale(LC_NUMERIC, "C");
+
+ g_snprintf(buf, sizeof(buf), "%.2f", float_val);
+ /* restore previous locale setting */
+ setlocale(LC_NUMERIC, savelocale);
+
+ g_object_set(renderer, "text", buf, NULL);
+}
+
+
+/* Create list */
+static
+GtkWidget* create_list(user_data_t* user_data)
+{
+
+ GtkListStore *list_store;
+ GtkWidget *list;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSortable *sortable;
+ GtkTreeView *list_view;
+ GtkTreeSelection *selection;
+
+ /* Create the store */
+ list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX */
+ G_TYPE_UINT, /* Packet */
+ G_TYPE_FLOAT, /* Delta(ms) */
+ G_TYPE_FLOAT, /* Jitter(ms) */
+ G_TYPE_FLOAT, /* IP BW(kbps) */
+ G_TYPE_STRING, /* Status */
+ G_TYPE_STRING, /* Date */
+ G_TYPE_UINT, /* Length */
+ G_TYPE_STRING, /* Foreground color */
+ G_TYPE_STRING); /* Background color */
+
+ /* Create a view */
+ list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
+
+ list_view = GTK_TREE_VIEW(list);
+ sortable = GTK_TREE_SORTABLE(list_store);
+
+ /* Speed up the list display */
+ gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
+
+ /* Setup the sortable columns */
+ gtk_tree_sortable_set_sort_column_id(sortable, PACKET_COLUMN, GTK_SORT_ASCENDING);
+ gtk_tree_view_set_headers_clickable(list_view, FALSE);
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref (G_OBJECT (list_store));
+
+ /*
+ * Create the first column packet, associating the "text" attribute of the
+ * cell_renderer to the first column of the model
+ */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Packet", renderer,
+ "text", PACKET_COLUMN,
+ "foreground", FOREGROUND_COLOR_COL,
+ "background", BACKGROUND_COLOR_COL,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, PACKET_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Second column.. Delta(ms). */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Delta(ms)", renderer,
+ "text", DELTA_COLUMN,
+ "foreground", FOREGROUND_COLOR_COL,
+ "background", BACKGROUND_COLOR_COL,
+ NULL);
+
+ gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
+ GINT_TO_POINTER(DELTA_COLUMN), NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, DELTA_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Third column.. Jitter(ms). */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Jitter(ms)", renderer,
+ "text", JITTER_COLUMN,
+ "foreground", FOREGROUND_COLOR_COL,
+ "background", BACKGROUND_COLOR_COL,
+ NULL);
+
+ gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
+ GINT_TO_POINTER(JITTER_COLUMN), NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, JITTER_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Fourth column.. IP BW(kbps). */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer,
+ "text", IPBW_COLUMN,
+ "foreground", FOREGROUND_COLOR_COL,
+ "background", BACKGROUND_COLOR_COL,
+ NULL);
+
+ gtk_tree_view_column_set_cell_data_func(column, renderer, iax2_float_data_func,
+ GINT_TO_POINTER(IPBW_COLUMN), NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, IPBW_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Fifth column.. Status. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ( "Status", renderer,
+ "text", STATUS_COLUMN,
+ "foreground", FOREGROUND_COLOR_COL,
+ "background", BACKGROUND_COLOR_COL,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, STATUS_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Sixth column.. Length. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Length", renderer,
+ "text", LENGTH_COLUMN,
+ "foreground", FOREGROUND_COLOR_COL,
+ "background", BACKGROUND_COLOR_COL,
+ NULL);
+
+
+ gtk_tree_view_column_set_sort_column_id(column, LENGTH_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Now enable the sorting of each column */
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
+
+ /* Setup the selection handler */
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+
+ g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
+ G_CALLBACK (on_list_select_row),
+ user_data);
+ return list;
+}
+
+
+
+/****************************************************************************/
+/* Create the dialog box with all widgets */
+static void create_iax2_dialog(user_data_t* user_data)
+{
+ GtkWidget *window = NULL;
+ GtkWidget *list_fwd;
+ GtkWidget *list_rev;
+ GtkWidget *label_stats_fwd;
+ GtkWidget *label_stats_rev;
+ GtkWidget *notebook;
+
+ GtkWidget *main_vb, *page, *page_r;
+ GtkWidget *label;
+ GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
+ GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
+ GtkWidget *graph_bt;
+ gchar label_forward[150];
+ gchar label_reverse[150];
+
+ gchar str_ip_src[16];
+ gchar str_ip_dst[16];
+
+ window = dlg_window_new("Wireshark: IAX2 Stream Analysis"); /* transient_for top_level */
+ gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
+ gtk_container_add(GTK_CONTAINER(window), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* Notebooks... */
+ g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)), 16);
+ g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)), 16);
+
+ g_snprintf(label_forward, sizeof(label_forward),
+ "Analysing stream from %s port %u to %s port %u ",
+ str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd);
+
+
+ g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)), 16);
+ g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)), 16);
+
+ g_snprintf(label_reverse, sizeof(label_reverse),
+ "Analysing stream from %s port %u to %s port %u ",
+ str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev);
+
+ /* Start a notebook for flipping between sets of changes */
+ notebook = gtk_notebook_new();
+ gtk_container_add(GTK_CONTAINER(main_vb), notebook);
+ g_object_set_data(G_OBJECT(window), "notebook", notebook);
+
+ user_data->dlg.notebook_signal_id =
+ g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
+
+ /* page for forward connection */
+ page = gtk_vbox_new(FALSE, 8);
+ gtk_container_set_border_width(GTK_CONTAINER(page), 8);
+
+ /* direction label */
+ label = gtk_label_new(label_forward);
+ gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
+
+ /* place for some statistics */
+ label_stats_fwd = gtk_label_new("\n");
+ gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
+
+ /* scrolled window */
+ scrolled_window = scrolled_window_new(NULL, NULL);
+
+ /* packet list */
+ list_fwd = create_list(user_data);
+ gtk_widget_show(list_fwd);
+ gtk_container_add(GTK_CONTAINER(scrolled_window), list_fwd);
+ gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
+ gtk_widget_show(scrolled_window);
+
+ /* tab */
+ label = gtk_label_new(" Forward Direction ");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
+
+ /* same page for reversed connection */
+ page_r = gtk_vbox_new(FALSE, 8);
+ gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
+ label = gtk_label_new(label_reverse);
+ gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
+ label_stats_rev = gtk_label_new("\n");
+ gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
+
+ scrolled_window_r = scrolled_window_new(NULL, NULL);
+
+ list_rev = create_list(user_data);
+ gtk_widget_show(list_rev);
+ gtk_container_add(GTK_CONTAINER(scrolled_window_r), list_rev);
+ gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
+ gtk_widget_show(scrolled_window_r);
+
+ label = gtk_label_new(" Reversed Direction ");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
+
+#if 0
+ /* page for help&about or future */
+ page_help = gtk_hbox_new(FALSE, 5);
+ label = gtk_label_new(" Future ");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
+ frame = gtk_frame_new("");
+ text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
+ gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
+ gtk_container_add(GTK_CONTAINER(frame), text);
+ gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
+ gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
+#endif
+
+ /* show all notebooks */
+ gtk_widget_show_all(notebook);
+
+ /* buttons */
+ box4 = gtk_hbutton_box_new();
+ gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
+ gtk_box_set_spacing(GTK_BOX (box4), 0);
+ gtk_widget_show(box4);
+
+ voice_bt = gtk_button_new_with_label("Save payload...");
+ gtk_container_add(GTK_CONTAINER(box4), voice_bt);
+ gtk_widget_show(voice_bt);
+ g_signal_connect(voice_bt, "clicked", G_CALLBACK(save_voice_as_cb), user_data);
+
+ csv_bt = gtk_button_new_with_label("Save as CSV...");
+ gtk_container_add(GTK_CONTAINER(box4), csv_bt);
+ gtk_widget_show(csv_bt);
+ g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
+
+ refresh_bt = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
+ gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
+ gtk_widget_show(refresh_bt);
+ g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
+
+ goto_bt = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
+ gtk_container_add(GTK_CONTAINER(box4), goto_bt);
+ gtk_widget_show(goto_bt);
+ g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked), user_data);
+
+ graph_bt = gtk_button_new_with_label("Graph");
+ gtk_container_add(GTK_CONTAINER(box4), graph_bt);
+ gtk_widget_show(graph_bt);
+ g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
+
+ next_bt = gtk_button_new_with_label("Next non-Ok");
+ gtk_container_add(GTK_CONTAINER(box4), next_bt);
+ gtk_widget_show(next_bt);
+ g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked), user_data);
+
+ close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_container_add(GTK_CONTAINER(box4), close_bt);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(close_bt, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
+#endif
+ gtk_widget_show(close_bt);
+ window_set_cancel_button(window, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(window, "destroy", G_CALLBACK(on_iax2_window_destroy), user_data);
+
+ gtk_widget_show(window);
+ window_present(window);
+
+ /* some widget references need to be saved for outside use */
+ user_data->dlg.window = window;
+ user_data->dlg.list_fwd = list_fwd;
+ user_data->dlg.list_rev = list_rev;
+ user_data->dlg.label_stats_fwd = label_stats_fwd;
+ user_data->dlg.label_stats_rev = label_stats_rev;
+ user_data->dlg.notebook = notebook;
+ user_data->dlg.selected_list = list_fwd;
+ user_data->dlg.number_of_nok = 0;
+
+ /*
+ * select the initial row
+ */
+ gtk_widget_grab_focus(list_fwd);
+}
+
+#if 0
+/****************************************************************************/
+static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
+ const gchar* proto_field, guint32* p_result)
+{
+ field_info *finfo;
+ proto_node *proto_sibling_node;
+ header_field_info *hfssrc;
+ ipv4_addr *ipv4;
+
+ finfo = PNODE_FINFO(ptree_node);
+
+ if (hfinformation==(finfo->hfinfo)) {
+ hfssrc = proto_registrar_get_byname(proto_field);
+ if (hfssrc == NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Bad field name!");
+ return FALSE;
+ }
+ for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
+ ptree_node=ptree_node->next) {
+ finfo=PNODE_FINFO(ptree_node);
+ if (hfssrc==finfo->hfinfo) {
+ if (hfinformation->type==FT_IPv4) {
+ ipv4 = fvalue_get(&finfo->value);
+ *p_result = ipv4_get_net_order_addr(ipv4);
+ }
+ else {
+ *p_result = fvalue_get_uinteger(&finfo->value);
+ }
+ return TRUE;
+ }
+ }
+ if(!ptree_node)
+ return FALSE;
+ }
+
+ proto_sibling_node = ptree_node->next;
+
+ if (proto_sibling_node) {
+ return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
+ }
+ else
+ return FALSE;
+}
+
+/****************************************************************************/
+static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
+ const gchar* proto_name,
+ const gchar* proto_field,
+ guint32* p_result)
+{
+ proto_node *ptree_node;
+ header_field_info *hfinformation;
+
+ hfinformation = proto_registrar_get_byname(proto_name);
+ if (hfinformation == NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Bad proto!");
+ return FALSE;
+ }
+
+ ptree_node = ((proto_node *)protocol_tree)->first_child;
+ if (!ptree_node) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "No info!");
+ return FALSE;
+ }
+ return process_node(ptree_node, hfinformation, proto_field, p_result);
+}
+#endif
+
+/****************************************************************************/
+void iax2_analysis(
+ address *ip_src_fwd,
+ guint16 port_src_fwd,
+ address *ip_dst_fwd,
+ guint16 port_dst_fwd,
+ address *ip_src_rev,
+ guint16 port_src_rev,
+ address *ip_dst_rev,
+ guint16 port_dst_rev
+ )
+{
+ user_data_t *user_data;
+ int fd;
+ int i;
+ static GdkColor col[MAX_GRAPHS] = {
+ {0, 0x0000, 0x0000, 0x0000},
+ {0, 0xffff, 0x0000, 0x0000},
+ {0, 0x0000, 0xffff, 0x0000},
+ {0, 0x0000, 0x0000, 0xffff}
+ };
+#if GTK_CHECK_VERSION(3,0,0)
+ static GdkRGBA rgba_col[MAX_GRAPHS] = {
+ {0.0, 0.0, 0.0, 1.0}, /* Black */
+ {1.0, 0.0, 0.1, 1.0}, /* Red */
+ {0.0, 1.0, 0.0, 1.0}, /* Green */
+ {0.0, 0.0, 1.0, 1.0}, /* Blue */
+ };
+#endif
+
+ char *tempname;
+
+ /* init */
+ user_data = g_malloc(sizeof(user_data_t));
+
+ COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
+ user_data->port_src_fwd = port_src_fwd;
+ COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
+ user_data->port_dst_fwd = port_dst_fwd;
+ COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
+ user_data->port_src_rev = port_src_rev;
+ COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
+ user_data->port_dst_rev = port_dst_rev;
+
+
+ /* file names for storing sound data */
+ /*XXX: check for errors*/
+ fd = create_tempfile(&tempname, "wireshark_iax2_f");
+ user_data->f_tempname = g_strdup(tempname);
+ ws_close(fd);
+ fd = create_tempfile(&tempname, "wireshark_iax2_r");
+ user_data->r_tempname = g_strdup(tempname);
+ ws_close(fd);
+ user_data->forward.saveinfo.fp = NULL;
+ user_data->reversed.saveinfo.fp = NULL;
+ user_data->dlg.save_voice_as_w = NULL;
+ user_data->dlg.save_csv_as_w = NULL;
+ user_data->dlg.dialog_graph.window = NULL;
+
+ /* init dialog_graph */
+ user_data->dlg.dialog_graph.needs_redraw=TRUE;
+ user_data->dlg.dialog_graph.interval_index=DEFAULT_TICK_INTERVAL_VALUES_INDEX;
+ user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_INTERVAL_VALUES_INDEX];
+ user_data->dlg.dialog_graph.draw_area=NULL;
+#if GTK_CHECK_VERSION(2,22,0)
+ user_data->dlg.dialog_graph.surface=NULL;
+#else
+ user_data->dlg.dialog_graph.pixmap=NULL;
+#endif
+ user_data->dlg.dialog_graph.scrollbar=NULL;
+ user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
+ user_data->dlg.dialog_graph.surface_width=500;
+ user_data->dlg.dialog_graph.surface_height=200;
+ user_data->dlg.dialog_graph.pixels_per_tick_index=DEFAULT_PIXELS_PER_TICK_INDEX;
+ user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK_INDEX];
+ user_data->dlg.dialog_graph.max_y_units_index=AUTO_MAX_YSCALE_INDEX;
+ user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
+ user_data->dlg.dialog_graph.last_interval=0xffffffff;
+ user_data->dlg.dialog_graph.max_interval=0;
+ user_data->dlg.dialog_graph.num_items=0;
+ user_data->dlg.dialog_graph.start_time = -1;
+
+ for(i=0;i<MAX_GRAPHS;i++){
+ user_data->dlg.dialog_graph.graph[i].color.pixel=0;
+ user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
+ user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
+ user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
+#if GTK_CHECK_VERSION(3,0,0)
+ user_data->dlg.dialog_graph.graph[i].rgba_color.red=rgba_col[i].red;
+ user_data->dlg.dialog_graph.graph[i].rgba_color.green=rgba_col[i].green;
+ user_data->dlg.dialog_graph.graph[i].rgba_color.blue=rgba_col[i].blue;
+ user_data->dlg.dialog_graph.graph[i].rgba_color.alpha=rgba_col[i].alpha;
+#endif
+ user_data->dlg.dialog_graph.graph[i].display=TRUE;
+ user_data->dlg.dialog_graph.graph[i].display_button=NULL;
+ user_data->dlg.dialog_graph.graph[i].ud=user_data;
+ }
+
+ /* create the dialog box */
+ create_iax2_dialog(user_data);
+
+ /* proceed as if the Refresh button would have been pressed */
+ on_refresh_bt_clicked(NULL, user_data);
+}
+
+/****************************************************************************/
+/* entry point from main menu */
+void iax2_analysis_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ address ip_src_fwd;
+ guint16 port_src_fwd;
+ address ip_dst_fwd;
+ guint16 port_dst_fwd;
+ address ip_src_rev;
+ guint16 port_src_rev;
+ address ip_dst_rev;
+ guint16 port_dst_rev;
+ /* unsigned int ptype; */
+
+ gchar filter_text[256];
+ dfilter_t *sfcode;
+ capture_file *cf;
+ epan_dissect_t edt;
+ gboolean frame_matched;
+ frame_data *fdata;
+ GList *strinfo_list;
+ GList *filtered_list = NULL;
+ rtp_stream_info_t *strinfo;
+ guint nfound;
+
+ /* Try to compile the filter. */
+ g_strlcpy(filter_text,"iax2 && (ip || ipv6)",256);
+ if (!dfilter_compile(filter_text, &sfcode)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
+ return;
+ }
+ /* we load the current file into cf variable */
+ cf = &cfile;
+ fdata = cf->current_frame;
+
+ /* we are on the selected frame now */
+ if (fdata == NULL)
+ return; /* if we exit here it's an error */
+
+ /* dissect the current frame */
+ if (!cf_read_frame(cf, fdata))
+ return; /* error reading the frame */
+ epan_dissect_init(&edt, TRUE, FALSE);
+ epan_dissect_prime_dfilter(&edt, sfcode);
+ epan_dissect_run(&edt, &cf->pseudo_header, cf->pd, fdata, NULL);
+
+ /* if it is not an iax2 frame, show an error dialog */
+ frame_matched = dfilter_apply_edt(sfcode, &edt);
+ if (frame_matched != 1) {
+ epan_dissect_cleanup(&edt);
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "You didn't choose a IAX2 packet!");
+ return;
+ }
+ /* check if it is Voice or MiniPacket
+ if (!get_int_value_from_proto_tree(edt->tree, "iax2", "iax2.call", &ptype)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Please select a Voice packet!");
+ return;
+ } */
+
+ /* check if it is part of a Call */
+ if (edt.pi.circuit_id == 0) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Please select a Call packet!");
+ return;
+ }
+
+ /* ok, it is a IAX2 frame, so let's get the ip and port values */
+ COPY_ADDRESS(&(ip_src_fwd), &(edt.pi.src))
+ COPY_ADDRESS(&(ip_dst_fwd), &(edt.pi.dst))
+ port_src_fwd = edt.pi.srcport;
+ port_dst_fwd = edt.pi.destport;
+
+ /* assume the inverse ip/port combination for the reverse direction */
+ COPY_ADDRESS(&(ip_src_rev), &(edt.pi.dst))
+ COPY_ADDRESS(&(ip_dst_rev), &(edt.pi.src))
+ port_src_rev = edt.pi.destport;
+ port_dst_rev = edt.pi.srcport;
+
+ /* Scan for rtpstream */
+ rtpstream_scan();
+ /* search for reversed direction in the global rtp streams list */
+ nfound = 0;
+ strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
+ while (strinfo_list)
+ {
+ strinfo = (rtp_stream_info_t*)(strinfo_list->data);
+ if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
+ && strinfo->src_port==port_src_fwd
+ && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
+ && strinfo->dest_port==port_dst_fwd)
+ {
+ filtered_list = g_list_prepend(filtered_list, strinfo);
+ }
+
+ if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
+ && strinfo->src_port==port_src_rev
+ && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
+ && strinfo->dest_port==port_dst_rev)
+ {
+ ++nfound;
+ filtered_list = g_list_append(filtered_list, strinfo);
+ }
+
+ strinfo_list = g_list_next(strinfo_list);
+ }
+
+ /* if more than one reverse streams found, we let the user choose the right one */
+ if (nfound>1) {
+ rtpstream_dlg_show(filtered_list);
+ return;
+ }
+ else {
+ iax2_analysis(
+ &ip_src_fwd,
+ port_src_fwd,
+ &ip_dst_fwd,
+ port_dst_fwd,
+ &ip_src_rev,
+ port_src_rev,
+ &ip_dst_rev,
+ port_dst_rev
+ );
+ }
+}
+
+/****************************************************************************/
+static void
+iax2_analysis_init(const char *dummy _U_,void* userdata _U_)
+{
+ iax2_analysis_cb(NULL, NULL);
+}
+
+/****************************************************************************/
+void
+register_tap_listener_iax2_analysis(void)
+{
+ register_stat_cmd_arg("IAX2", iax2_analysis_init,NULL);
+}
+
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 8
+ * tab-width: 8
+ * indent-tabs-mode: t
+ * End:
+ *
+ * ex: set shiftwidth=8 tabstop=8 noexpandtab:
+ * :indentSize=8:tabSize=8:noTabs=false:
+ */
diff --git a/ui/gtk/iax2_analysis.h b/ui/gtk/iax2_analysis.h
new file mode 100644
index 0000000000..cdc1a510e3
--- /dev/null
+++ b/ui/gtk/iax2_analysis.h
@@ -0,0 +1,119 @@
+/* iax2_analysis.h
+ * IAX2 analysis addition for Wireshark
+ *
+ * $Id$
+ *
+ * based on rtp_analysis.c
+ * Copyright 2003, Alcatel Business Systems
+ * By Lars Ruoff <lars.ruoff@gmx.net>
+ *
+ * based on tap_rtp.c
+ * Copyright 2003, Iskratel, Ltd, Kranj
+ * By Miha Jemec <m.jemec@iskratel.si>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __IAX2_ANALYSIS_H__
+#define __IAX2_ANALYSIS_H__
+
+#include <glib.h>
+#include <epan/address.h>
+#include <epan/packet_info.h>
+
+/** @file
+ * ???
+ * @todo what's this?
+ */
+
+void iax2_analysis(
+ address *ip_src_fwd,
+ guint16 port_src_fwd,
+ address *ip_dst_fwd,
+ guint16 port_dst_fwd,
+ address *ip_src_rev,
+ guint16 port_src_rev,
+ address *ip_dst_rev,
+ guint16 port_dst_rev
+ );
+
+/****************************************************************************/
+/* structure that holds the information about the forward and reversed direction */
+typedef struct _iax2_bw_history_item {
+ double time;
+ guint32 bytes;
+} iax2_bw_history_item;
+
+#define BUFF_BW 300
+
+typedef struct _tap_iax2_stat_t {
+ gboolean first_packet; /* do not use in code that is called after rtp_packet_analyse */
+ /* use (flags & STAT_FLAG_FIRST) instead */
+ /* all of the following fields will be initialized after
+ rtp_packet_analyse has been called */
+ guint32 flags; /* see STAT_FLAG-defines below */
+ guint16 seq_num;
+ guint32 timestamp;
+ guint32 delta_timestamp;
+ double bandwidth;
+ iax2_bw_history_item bw_history[BUFF_BW];
+ guint16 bw_start_index;
+ guint16 bw_index;
+ guint32 total_bytes;
+ double delta;
+ double jitter;
+ double diff;
+ double time;
+ double start_time;
+ double max_delta;
+ double max_jitter;
+ double mean_jitter;
+ guint32 max_nr;
+ guint16 start_seq_nr;
+ guint16 stop_seq_nr;
+ guint32 total_nr;
+ guint32 sequence;
+ gboolean under;
+ gint cycles;
+ guint16 pt;
+ int reg_pt;
+} tap_iax2_stat_t;
+
+#define PT_UNDEFINED -1
+
+/* status flags for the flags parameter in tap_iax2_stat_t */
+#define STAT_FLAG_FIRST 0x001
+#define STAT_FLAG_MARKER 0x002
+#define STAT_FLAG_WRONG_SEQ 0x004
+#define STAT_FLAG_PT_CHANGE 0x008
+#define STAT_FLAG_PT_CN 0x010
+#define STAT_FLAG_FOLLOW_PT_CN 0x020
+#define STAT_FLAG_REG_PT_CHANGE 0x040
+#define STAT_FLAG_WRONG_TIMESTAMP 0x080
+
+/* forward */
+struct _rtp_info;
+
+/* function for analysing an RTP packet. Called from rtp_analysis and rtp_streams */
+extern int iax2_packet_analyse(tap_iax2_stat_t *statinfo,
+ packet_info *pinfo,
+ const struct _iax2_info_t *iax2info);
+
+
+#endif /* __IAX2_ANALYSIS_H__ */
diff --git a/ui/gtk/io_stat.c b/ui/gtk/io_stat.c
new file mode 100644
index 0000000000..a19e1f75d6
--- /dev/null
+++ b/ui/gtk/io_stat.c
@@ -0,0 +1,2431 @@
+/* io_stat.c
+ * io_stat 2002 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <ctype.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/epan_dissect.h>
+#include <epan/packet_info.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/strutil.h>
+
+#include "../stat_menu.h"
+#include "../alert_box.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/pixmap_save.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/filter_autocomplete.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+#define MAX_GRAPHS 5
+
+#define MAX_YSCALE 28
+#define LOGARITHMIC_YSCALE 0
+#define AUTO_MAX_YSCALE 1
+#define DEFAULT_YSCALE_INDEX 1
+static guint32 yscale_max[MAX_YSCALE] = {LOGARITHMIC_YSCALE, AUTO_MAX_YSCALE, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000, 100000000, 200000000, 500000000, 1000000000, 2000000000};
+
+#define NO_FILTER_ORDER 0
+#define MAX_MOVING_AVERAGE_ORDER 10
+static guint32 moving_average_orders[MAX_MOVING_AVERAGE_ORDER] = {NO_FILTER_ORDER, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
+#define NO_FILTER 0
+#define MOVING_AVERAGE_FILTER 1
+
+#define MAX_PIXELS_PER_TICK 4
+#define DEFAULT_PIXELS_PER_TICK_INDEX 2
+static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
+
+
+#define DEFAULT_PLOT_STYLE 0
+#define PLOT_STYLE_LINE 0
+#define PLOT_STYLE_IMPULSE 1
+#define PLOT_STYLE_FILLED_BAR 2
+#define PLOT_STYLE_DOT 3
+#define MAX_PLOT_STYLES 4
+static const char *plot_style_name[MAX_PLOT_STYLES] = {
+ "Line",
+ "Impulse",
+ "FBar",
+ "Dot",
+};
+
+#define DEFAULT_COUNT_TYPE 0
+#define COUNT_TYPE_FRAMES 0
+#define COUNT_TYPE_BYTES 1
+#define COUNT_TYPE_BITS 2
+#define COUNT_TYPE_ADVANCED 3
+#define MAX_COUNT_TYPES 4
+static const char *count_type_names[MAX_COUNT_TYPES] = {"Packets/Tick", "Bytes/Tick", "Bits/Tick", "Advanced..."};
+
+/* unit is in ms */
+#define MAX_TICK_VALUES 7
+#define DEFAULT_TICK_VALUE_INDEX 3
+static const guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000, 60000, 600000 };
+
+#define CALC_TYPE_SUM 0
+#define CALC_TYPE_COUNT 1
+#define CALC_TYPE_MAX 2
+#define CALC_TYPE_MIN 3
+#define CALC_TYPE_AVG 4
+#define CALC_TYPE_LOAD 5
+#define MAX_CALC_TYPES 6
+#define DEFAULT_CALC_TYPE 0
+static const char *calc_type_names[MAX_CALC_TYPES] = {"SUM(*)", "COUNT(*)", "MAX(*)", "MIN(*)", "AVG(*)", "LOAD(*)"};
+
+
+typedef struct _io_stat_calc_type_t {
+ struct _io_stat_graph_t *gio;
+ int calc_type;
+} io_stat_calc_type_t;
+
+#define NUM_IO_ITEMS 100000
+typedef struct _io_item_t {
+ guint32 frames; /* always calculated, will hold number of frames*/
+ guint32 bytes; /* always calculated, will hold number of bytes*/
+ guint32 fields;
+ gint32 int_max;
+ gint32 int_min;
+ gint32 int_tot;
+ gfloat float_max;
+ gfloat float_min;
+ gfloat float_tot;
+ gdouble double_max;
+ gdouble double_min;
+ gdouble double_tot;
+ nstime_t time_max;
+ nstime_t time_min;
+ nstime_t time_tot;
+} io_item_t;
+
+typedef struct _io_stat_graph_t {
+ struct _io_stat_t *io;
+ io_item_t items[NUM_IO_ITEMS];
+ int plot_style;
+ gboolean display;
+ GtkWidget *display_button;
+ GtkWidget *filter_field;
+ GtkWidget *advanced_buttons;
+ int calc_type;
+ int hf_index;
+ GtkWidget *calc_field;
+ GdkColor color;
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkRGBA rgba_color;
+#endif
+ construct_args_t *args;
+ GtkWidget *filter_bt;
+} io_stat_graph_t;
+
+
+typedef struct _io_stat_t {
+ gboolean needs_redraw;
+ gint32 interval; /* measurement interval in ms */
+ guint32 last_interval;
+ guint32 max_interval; /* XXX max_interval and num_items are redundant */
+ guint32 num_items;
+ guint32 left_x_border;
+ guint32 right_x_border;
+ gboolean view_as_time;
+ nstime_t start_time;
+
+ struct _io_stat_graph_t graphs[MAX_GRAPHS];
+ GtkWidget *window;
+ GtkWidget *draw_area;
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_surface_t *surface;
+#else
+ GdkPixmap *pixmap;
+#endif
+ GtkAdjustment *scrollbar_adjustment;
+ GtkWidget *scrollbar;
+ guint first_frame_num[NUM_IO_ITEMS];
+ guint last_frame_num;
+ int pixmap_width;
+ int pixmap_height;
+ int pixels_per_tick;
+ int max_y_units;
+ int count_type;
+
+ guint32 filter_order;
+ int filter_type;
+} io_stat_t;
+
+
+static void init_io_stat_window(io_stat_t *io);
+static void filter_callback(GtkWidget *widget _U_, gpointer user_data);
+
+static void
+io_stat_set_title(io_stat_t *io)
+{
+ char *title;
+
+ if(!io->window){
+ return;
+ }
+ title = g_strdup_printf("Wireshark IO Graphs: %s", cf_get_display_name(&cfile));
+ gtk_window_set_title(GTK_WINDOW(io->window), title);
+ g_free(title);
+}
+
+static void
+io_stat_reset(io_stat_t *io)
+{
+ int i, j;
+
+ io->needs_redraw=TRUE;
+ for(i=0;i<MAX_GRAPHS;i++){
+ for(j=0;j<NUM_IO_ITEMS;j++){
+ io_item_t *ioi;
+ ioi=&io->graphs[i].items[j];
+
+ ioi->frames=0;
+ ioi->bytes=0;
+ ioi->fields=0;
+ ioi->int_max=0;
+ ioi->int_min=0;
+ ioi->int_tot=0;
+ ioi->float_max=0;
+ ioi->float_min=0;
+ ioi->float_tot=0;
+ ioi->double_max=0;
+ ioi->double_min=0;
+ ioi->double_tot=0;
+ nstime_set_zero(&ioi->time_max);
+ nstime_set_zero(&ioi->time_min);
+ nstime_set_zero(&ioi->time_tot);
+ }
+ }
+ io->last_interval=0xffffffff;
+ io->max_interval=0;
+ io->num_items=0;
+ io->start_time.secs=0;
+ io->start_time.nsecs=0;
+ for(j=0;j<NUM_IO_ITEMS;j++) {
+ io->first_frame_num[j]=0;
+ }
+ io->last_frame_num=0;
+
+ io_stat_set_title(io);
+}
+
+static void
+tap_iostat_reset(void *g)
+{
+ io_stat_graph_t *gio=g;
+
+ io_stat_reset(gio->io);
+}
+
+static gboolean
+tap_iostat_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_)
+{
+ io_stat_graph_t *git=g;
+ io_item_t *it;
+ nstime_t time_delta;
+ int idx;
+
+ /* we sometimes get called when git is disabled.
+ this is a bug since the tap listener should be removed first */
+ if(!git->display){
+ return FALSE;
+ }
+
+ git->io->needs_redraw=TRUE;
+
+ /*
+ * Find which interval this is supposed to go in and store the
+ * interval index as idx
+ */
+ time_delta=pinfo->fd->rel_ts;
+ if(time_delta.nsecs<0){
+ time_delta.secs--;
+ time_delta.nsecs+=1000000000;
+ }
+ if(time_delta.secs<0){
+ return FALSE;
+ }
+ idx=(int) ((time_delta.secs*1000+time_delta.nsecs/1000000)/git->io->interval);
+
+ /* some sanity checks */
+ if((idx<0)||(idx>=NUM_IO_ITEMS)){
+ git->io->num_items = NUM_IO_ITEMS-1;
+ return FALSE;
+ }
+
+ /* update num_items */
+ if((guint32)idx > git->io->num_items){
+ git->io->num_items=idx;
+ git->io->max_interval=(idx+1)*git->io->interval;
+ }
+
+ /* set start time */
+ if(git->io->start_time.secs == 0 && git->io->start_time.nsecs == 0) {
+ nstime_delta (&git->io->start_time, &pinfo->fd->abs_ts, &pinfo->fd->rel_ts);
+ }
+
+ /* set first and last frame num in current interval */
+ if (git->io->first_frame_num[idx] == 0) {
+ git->io->first_frame_num[idx]=pinfo->fd->num;
+ }
+ git->io->last_frame_num=pinfo->fd->num;
+
+ /*
+ * Find the appropriate io_item_t structure
+ */
+ it=&git->items[idx];
+
+
+ /*
+ * For ADVANCED mode we need to keep track of some more stuff
+ * than just frame and byte counts
+ */
+ if(git->io->count_type==COUNT_TYPE_ADVANCED){
+ GPtrArray *gp;
+ guint i;
+
+ gp=proto_get_finfo_ptr_array(edt->tree, git->hf_index);
+ if(!gp){
+ return FALSE;
+ }
+
+ /* update the appropriate counters, make sure that if
+ * fields==0 then this is the first seen value so
+ * set any min/max values accordingly
+ */
+ for(i=0;i<gp->len;i++){
+ int new_int;
+ float new_float;
+ double new_double;
+ nstime_t *new_time;
+
+ switch(proto_registrar_get_ftype(git->hf_index)){
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ new_int=fvalue_get_uinteger(&((field_info *)gp->pdata[i])->value);
+
+ if((new_int>it->int_max)||(it->fields==0)){
+ it->int_max=new_int;
+ }
+ if((new_int<it->int_min)||(it->fields==0)){
+ it->int_min=new_int;
+ }
+ it->int_tot+=new_int;
+ it->fields++;
+ break;
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ new_int=fvalue_get_sinteger(&((field_info *)gp->pdata[i])->value);
+ if((new_int>it->int_max)||(it->fields==0)){
+ it->int_max=new_int;
+ }
+ if((new_int<it->int_min)||(it->fields==0)){
+ it->int_min=new_int;
+ }
+ it->int_tot+=new_int;
+ it->fields++;
+ break;
+ case FT_FLOAT:
+ new_float=(gfloat)fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
+ if((new_float>it->float_max)||(it->fields==0)){
+ it->float_max=new_float;
+ }
+ if((new_float<it->float_min)||(it->fields==0)){
+ it->float_min=new_float;
+ }
+ it->float_tot+=new_float;
+ it->fields++;
+ break;
+ case FT_DOUBLE:
+ new_double=fvalue_get_floating(&((field_info *)gp->pdata[i])->value);
+ if((new_double>it->double_max)||(it->fields==0)){
+ it->double_max=new_double;
+ }
+ if((new_double<it->double_min)||(it->fields==0)){
+ it->double_min=new_double;
+ }
+ it->double_tot+=new_double;
+ it->fields++;
+ break;
+ case FT_RELATIVE_TIME:
+ new_time=fvalue_get(&((field_info *)gp->pdata[0])->value);
+
+ switch(git->calc_type){
+ guint64 t, pt; /* time in us */
+ int j;
+ case CALC_TYPE_LOAD:
+ /* it is a LOAD calculation of a relative time field.
+ * add the time this call spanned to each
+ * interval it spanned according to its contribution
+ * to that interval.
+ */
+ t=new_time->secs;
+ t=t*1000000+new_time->nsecs/1000;
+ j=idx;
+ /* handle current interval */
+ pt=pinfo->fd->rel_ts.secs*1000000+pinfo->fd->rel_ts.nsecs/1000;
+ pt=pt%(git->io->interval*1000);
+ if(pt>t){
+ pt=t;
+ }
+ while(t){
+ git->items[j].time_tot.nsecs+=(int) (pt*1000);
+ if(git->items[j].time_tot.nsecs>1000000000){
+ git->items[j].time_tot.secs++;
+ git->items[j].time_tot.nsecs-=1000000000;
+ }
+
+ if(j==0){
+ break;
+ }
+ j--;
+ t-=pt;
+ if(t > (guint32) (git->io->interval*1000)){
+ pt=git->io->interval*1000;
+ } else {
+ pt=t;
+ }
+ }
+ break;
+ default:
+ if( (new_time->secs>it->time_max.secs)
+ ||( (new_time->secs==it->time_max.secs)
+ &&(new_time->nsecs>it->time_max.nsecs))
+ ||(it->fields==0)){
+ it->time_max=*new_time;
+ }
+ if( (new_time->secs<it->time_min.secs)
+ ||( (new_time->secs==it->time_min.secs)
+ &&(new_time->nsecs<it->time_min.nsecs))
+ ||(it->fields==0)){
+ it->time_min=*new_time;
+ }
+ nstime_add(&it->time_tot, new_time);
+ it->fields++;
+ }
+ }
+ }
+ }
+
+ it->frames++;
+ it->bytes+=pinfo->fd->pkt_len;
+
+ return TRUE;
+}
+
+static guint
+get_frame_num(io_stat_t *io, guint32 idx, gboolean first)
+{
+ guint i, frame_num=0;
+
+ if (idx>io->num_items) {
+ return 0;
+ }
+
+ if (first) {
+ frame_num=io->first_frame_num[idx];
+ }
+
+ if (frame_num==0) {
+ /*
+ * If first frame not found we select the last
+ * frame in the previous interval
+ *
+ * If selecting the last frame we select the frame
+ * before the first frame in the next interval
+ */
+ for(i=idx+1;i<=io->num_items;i++) {
+ frame_num=io->first_frame_num[i];
+ if (frame_num != 0) {
+ return frame_num-1;
+ }
+ }
+
+ /*
+ * If not found we select the last frame
+ */
+ frame_num=io->last_frame_num;
+ }
+
+ return frame_num;
+}
+
+static guint32
+get_it_value(io_stat_t *io, int graph_id, int idx)
+{
+ double value=0;
+ int adv_type;
+ io_item_t *it;
+
+ it=&io->graphs[graph_id].items[idx];
+
+ switch(io->count_type){
+ case COUNT_TYPE_FRAMES:
+ return it->frames;
+ case COUNT_TYPE_BYTES:
+ return it->bytes;
+ case COUNT_TYPE_BITS:
+ return (it->bytes * 8);
+ }
+
+
+ adv_type=proto_registrar_get_ftype(io->graphs[graph_id].hf_index);
+ switch(adv_type){
+ case FT_NONE:
+ switch(io->graphs[graph_id].calc_type){
+ case CALC_TYPE_COUNT:
+ value=it->frames;
+ break;
+ default:
+ break;
+ }
+ break;
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ switch(io->graphs[graph_id].calc_type){
+ case CALC_TYPE_SUM:
+ value=it->int_tot;
+ break;
+ case CALC_TYPE_COUNT:
+ value=it->frames;
+ break;
+ case CALC_TYPE_MAX:
+ value=it->int_max;
+ break;
+ case CALC_TYPE_MIN:
+ value=it->int_min;
+ break;
+ case CALC_TYPE_AVG:
+ if(it->fields){
+ value=it->int_tot/it->fields;
+ } else {
+ value=0;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case FT_FLOAT:
+ switch(io->graphs[graph_id].calc_type){
+ case CALC_TYPE_SUM:
+ value=it->float_tot;
+ break;
+ case CALC_TYPE_COUNT:
+ value=it->frames;
+ break;
+ case CALC_TYPE_MAX:
+ value=it->float_max;
+ break;
+ case CALC_TYPE_MIN:
+ value=it->float_min;
+ break;
+ case CALC_TYPE_AVG:
+ if(it->fields){
+ value=it->float_tot/it->fields;
+ } else {
+ value=0;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case FT_DOUBLE:
+ switch(io->graphs[graph_id].calc_type){
+ case CALC_TYPE_SUM:
+ value=it->double_tot;
+ break;
+ case CALC_TYPE_COUNT:
+ value=it->frames;
+ break;
+ case CALC_TYPE_MAX:
+ value=it->double_max;
+ break;
+ case CALC_TYPE_MIN:
+ value=it->double_min;
+ break;
+ case CALC_TYPE_AVG:
+ if(it->fields){
+ value=it->double_tot/it->fields;
+ } else {
+ value=0;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case FT_RELATIVE_TIME:
+ switch(io->graphs[graph_id].calc_type){
+ case CALC_TYPE_COUNT:
+ value=it->frames;
+ break;
+ case CALC_TYPE_MAX:
+ value=(guint32) (it->time_max.secs*1000000+it->time_max.nsecs/1000);
+ break;
+ case CALC_TYPE_MIN:
+ value=(guint32) (it->time_min.secs*1000000+it->time_min.nsecs/1000);
+ break;
+ case CALC_TYPE_SUM:
+ value=(guint32) (it->time_tot.secs*1000000+it->time_tot.nsecs/1000);
+ break;
+ case CALC_TYPE_AVG:
+ if(it->fields){
+ guint64 t; /* time in us */
+
+ t=it->time_tot.secs;
+ t=t*1000000+it->time_tot.nsecs/1000;
+ value=(guint32) (t/it->fields);
+ } else {
+ value=0;
+ }
+ break;
+ case CALC_TYPE_LOAD:
+ value=(guint32) ((it->time_tot.secs*1000000+it->time_tot.nsecs/1000)/io->interval);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return (guint32)value; /* FIXME: loss of precision, visible on the graph for small values */
+}
+
+static void
+print_time_scale_string(char *buf, int buf_len, guint32 t, guint32 t_max, gboolean log_flag)
+{
+ if(t_max>=10000000 || (log_flag && t_max>=1000000)){
+ g_snprintf(buf, buf_len, "%ds",t/1000000);
+ } else if(t_max>=1000000){
+ g_snprintf(buf, buf_len, "%d.%1ds",t/1000000,(t%1000000)/100000);
+ } else if(t_max>=10000 || (log_flag && t_max>=1000)){
+ g_snprintf(buf, buf_len, "%dms",t/1000);
+ } else if(t_max>=1000){
+ g_snprintf(buf, buf_len, "%d.%1dms",t/1000,(t%1000)/100);
+ } else {
+ g_snprintf(buf, buf_len, "%dus",t);
+ }
+}
+
+static void
+print_interval_string(char *buf, int buf_len, guint32 interval, io_stat_t *io,
+ gboolean ext)
+{
+ if (io->view_as_time) {
+ struct tm *tmp;
+ time_t sec_val = interval/1000 + io->start_time.secs;
+ gint32 nsec_val = interval%1000 + io->start_time.nsecs/1000000;
+
+ if(nsec_val >= 1000) {
+ sec_val++;
+ nsec_val -= 1000;
+ }
+ tmp = localtime (&sec_val);
+ if(io->interval>=1000){
+ g_snprintf(buf, buf_len, "%02d:%02d:%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+ } else if(io->interval>=100){
+ g_snprintf(buf, buf_len, "%02d:%02d:%02d.%1d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val/100);
+ } else if(io->interval>=10){
+ g_snprintf(buf, buf_len, "%02d:%02d:%02d.%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val/10);
+ } else {
+ g_snprintf(buf, buf_len, "%02d:%02d:%02d.%03d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec, nsec_val);
+ }
+ } else {
+ if (!ext) {
+ g_snprintf(buf, buf_len, "%d.%03d", interval/1000,interval%1000);
+ } else if(io->interval>=60000){
+ g_snprintf(buf, buf_len, "%dm", interval/60000);
+ } else if(io->interval>=1000){
+ g_snprintf(buf, buf_len, "%ds", interval/1000);
+ } else if(io->interval>=100){
+ g_snprintf(buf, buf_len, "%d.%1ds", interval/1000,(interval/100)%10);
+ } else if(io->interval>=10){
+ g_snprintf(buf, buf_len, "%d.%02ds", interval/1000,(interval/10)%100);
+ } else {
+ g_snprintf(buf, buf_len, "%d.%03ds", interval/1000,interval%1000);
+ }
+ }
+}
+
+static void
+io_stat_draw(io_stat_t *io)
+{
+ int i, tics, ystart, ys;
+ guint32 last_interval, first_interval, interval_delta;
+ gint32 current_interval;
+ guint32 top_y_border;
+ guint32 bottom_y_border;
+ PangoLayout *layout;
+ int label_width, label_height;
+ guint32 draw_width, draw_height;
+ char label_string[45];
+ GtkAllocation widget_alloc;
+
+ /* new variables */
+ guint32 num_time_intervals;
+ guint32 max_value; /* max value of seen data */
+ guint32 max_y; /* max value of the Y scale */
+ gboolean draw_y_as_time;
+ cairo_t *cr;
+ if(!io->needs_redraw){
+ return;
+ }
+ io->needs_redraw=FALSE;
+
+ /*
+ * Find the length of the intervals we have data for
+ * so we know how large arrays we need to malloc()
+ */
+ num_time_intervals=io->num_items+1;
+
+ /* XXX move this check to _packet() */
+ if(num_time_intervals>NUM_IO_ITEMS){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "IO-Stat error. There are too many entries, bailing out");
+ return;
+ }
+
+
+ /*
+ * find the max value so we can autoscale the y axis
+ */
+ max_value=0;
+ for(i=0;i<MAX_GRAPHS;i++){
+ int idx;
+
+ if(!io->graphs[i].display){
+ continue;
+ }
+ for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
+ guint32 val;
+
+ val=get_it_value(io, i, idx);
+
+ /* keep track of the max value we have encountered */
+ if(val>max_value){
+ max_value=val;
+ }
+ }
+ }
+
+
+
+ /*
+ * Clear out old plot
+ */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (io->surface);
+#else
+ cr = gdk_cairo_create (io->pixmap);
+#endif
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ gtk_widget_get_allocation(io->draw_area, &widget_alloc);
+ cairo_rectangle (cr, 0, 0, widget_alloc.width,widget_alloc.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ /*
+ * Calculate the y scale we should use
+ */
+ if(io->max_y_units==AUTO_MAX_YSCALE){
+ max_y=yscale_max[MAX_YSCALE-1];
+ for(i=MAX_YSCALE-1;i>1;i--){
+ if(max_value<yscale_max[i]){
+ max_y=yscale_max[i];
+ }
+ }
+ } else if(io->max_y_units==LOGARITHMIC_YSCALE){
+ max_y=1000000000;
+ for(i=1000000000;i>1;i/=10){
+ if(max_value<(guint32)i){
+ max_y=i;
+ }
+ }
+ } else {
+ /* the user had specified an explicit y scale to use */
+ max_y=io->max_y_units;
+ }
+
+
+ /*
+ * If we use ADVANCED and all the graphs are plotting
+ * either MIN/MAX/AVG of an FT_RELATIVE_TIME field
+ * then we will do some some special processing for the
+ * labels for the Y axis below:
+ * we will append the time unit " s" " ms" or " us"
+ * and we will present the unit in decimal
+ */
+ draw_y_as_time=FALSE;
+ if(io->count_type==COUNT_TYPE_ADVANCED){
+ draw_y_as_time=TRUE;
+ for(i=0;i<MAX_GRAPHS;i++){
+ int adv_type;
+
+ if(!io->graphs[i].display){
+ continue;
+ }
+ adv_type=proto_registrar_get_ftype(io->graphs[i].hf_index);
+ switch(adv_type){
+ case FT_RELATIVE_TIME:
+ switch(io->graphs[i].calc_type){
+ case CALC_TYPE_SUM:
+ case CALC_TYPE_MAX:
+ case CALC_TYPE_MIN:
+ case CALC_TYPE_AVG:
+ break;
+ default:
+ draw_y_as_time=FALSE;
+ }
+ break;
+ default:
+ draw_y_as_time=FALSE;
+ }
+ }
+ }
+
+
+
+ /*
+ * Calculate size of borders surrounding the plot
+ * The border on the right side needs to be adjusted depending
+ * on the width of the text labels. For simplicity we assume that the
+ * top y scale label will be the widest one
+ */
+ if(draw_y_as_time){
+ if(io->max_y_units==LOGARITHMIC_YSCALE){
+ print_time_scale_string(label_string, 15, 100000, 100000, TRUE); /* 100 ms */
+ } else {
+ print_time_scale_string(label_string, 15, max_y, max_y, FALSE);
+ }
+ } else {
+ g_snprintf(label_string, 15, "%d", max_y);
+ }
+ layout = gtk_widget_create_pango_layout(io->draw_area, label_string);
+ pango_layout_get_pixel_size(layout, &label_width, &label_height);
+
+ io->left_x_border=10;
+ io->right_x_border=label_width+20;
+ top_y_border=10;
+ bottom_y_border=label_height+20;
+
+
+ /*
+ * Calculate the size of the drawing area for the actual plot
+ */
+ draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
+ draw_height=io->pixmap_height-top_y_border-bottom_y_border;
+
+
+ /*
+ * Add a warning if too many entries
+ */
+ if (num_time_intervals == NUM_IO_ITEMS) {
+ g_snprintf (label_string, 45, "Warning: Graph limited to %d entries", NUM_IO_ITEMS);
+ pango_layout_set_text(layout, label_string, -1);
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (io->surface);
+#else
+ cr = gdk_cairo_create (io->pixmap);
+#endif
+ cairo_move_to (cr, 5, io->pixmap_height-bottom_y_border-draw_height-label_height/2);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+
+ /*
+ * Draw the y axis and labels
+ * (we always draw the y scale with 11 ticks along the axis)
+ */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (io->surface);
+#else
+ cr = gdk_cairo_create (io->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, io->pixmap_width-io->right_x_border+1.5, top_y_border+0.5);
+ cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5, io->pixmap_height-bottom_y_border+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ if(io->max_y_units==LOGARITHMIC_YSCALE){
+ tics=(int)log10((double)max_y);
+ ystart=draw_height/10;
+ ys=-1;
+ } else {
+ tics=10;
+ ystart=ys=0;
+ }
+
+ for(i=ys;i<=tics;i++){
+ int xwidth, lwidth, ypos;
+
+ xwidth=5;
+ if(io->max_y_units==LOGARITHMIC_YSCALE){
+ if(i==ys) {
+ /* position for the 0 value */
+ ypos=io->pixmap_height-bottom_y_border;
+ } else if(i==tics) {
+ /* position for the top value, do not draw logarithmic tics above graph */
+ ypos=io->pixmap_height-bottom_y_border-draw_height;
+ } else {
+ int j;
+ /* draw the logarithmic tics */
+ for(j=2;j<10;j++) {
+ ypos=(int)(io->pixmap_height-bottom_y_border-(draw_height-ystart)*(i+log10((double)j))/tics-ystart);
+ /* draw the tick */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (io->surface);
+#else
+ cr = gdk_cairo_create (io->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, io->pixmap_width-io->right_x_border+1.5, ypos+0.5);
+ cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5+xwidth,ypos+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ }
+ ypos=io->pixmap_height-bottom_y_border-(draw_height-ystart)*i/tics-ystart;
+ }
+ /* all "main" logarithmic lines are slightly longer */
+ xwidth=10;
+ } else {
+ if(!(i%5)){
+ /* first, middle and last tick are slightly longer */
+ xwidth=10;
+ }
+ ypos=io->pixmap_height-bottom_y_border-draw_height*i/10;
+ }
+ /* draw the tick */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (io->surface);
+#else
+ cr = gdk_cairo_create (io->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, io->pixmap_width-io->right_x_border+1.5, ypos+0.5);
+ cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5+xwidth,ypos+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ /* draw the labels */
+ if(xwidth==10) {
+ guint32 value;
+ if(io->max_y_units==LOGARITHMIC_YSCALE){
+ value=(guint32)(max_y/pow(10,tics-i));
+ if(draw_y_as_time){
+ print_time_scale_string(label_string, 15, value, value, TRUE);
+ } else {
+ g_snprintf(label_string, 15, "%d", value);
+ }
+ } else {
+ value=(max_y/10)*i;
+ if(draw_y_as_time){
+ print_time_scale_string(label_string, 15, value, max_y, FALSE);
+ } else {
+ g_snprintf(label_string, 15, "%d", value);
+ }
+ }
+
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (io->surface);
+#else
+ cr = gdk_cairo_create (io->pixmap);
+#endif
+ cairo_move_to (cr, io->pixmap_width-io->right_x_border+15+label_width-lwidth, ypos-label_height/2);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+
+ }
+ }
+
+ /*
+ * if we have not specified the last_interval via the gui,
+ * then just pick the current end of the capture so that is scrolls
+ * nicely when doing live captures
+ */
+ if(io->last_interval==0xffffffff){
+ last_interval=io->max_interval;
+ } else {
+ last_interval=io->last_interval;
+ }
+
+
+
+
+/*XXX*/
+ /* plot the x-scale */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (io->surface);
+#else
+ cr = gdk_cairo_create (io->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, io->left_x_border+0.5, io->pixmap_height-bottom_y_border+1.5);
+ cairo_line_to(cr, io->pixmap_width-io->right_x_border+1.5,io->pixmap_height-bottom_y_border+1.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ if((last_interval/io->interval)>=draw_width/io->pixels_per_tick){
+ first_interval=(last_interval/io->interval)-draw_width/io->pixels_per_tick+1;
+ first_interval*=io->interval;
+ } else {
+ first_interval=0;
+ }
+
+ interval_delta=(100/io->pixels_per_tick)*io->interval;
+ for(current_interval=last_interval;current_interval>=(gint32)first_interval;current_interval=current_interval-io->interval){
+ int x, xlen;
+
+ /* if pixels_per_tick is 1 or 2, only draw every 10 ticks */
+ /* if pixels_per_tick is 5, only draw every 5 ticks */
+ if(((io->pixels_per_tick<5) && (current_interval%(10*io->interval))) ||
+ ((io->pixels_per_tick==5) && (current_interval%(5*io->interval)))){
+ continue;
+ }
+
+ if(!(current_interval%interval_delta)){
+ xlen=10;
+ } else if(!(current_interval%(interval_delta/2))){
+ xlen=8;
+ } else {
+ xlen=5;
+ }
+ x=draw_width+io->left_x_border-((last_interval-current_interval)/io->interval)*io->pixels_per_tick;
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (io->surface);
+#else
+ cr = gdk_cairo_create (io->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, x-1-io->pixels_per_tick/2+0.5, io->pixmap_height-bottom_y_border+1.5);
+ cairo_line_to(cr, x-1-io->pixels_per_tick/2+0.5, io->pixmap_height-bottom_y_border+xlen+1.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ if(xlen==10){
+ int lwidth, x_pos;
+ print_interval_string (label_string, 15, current_interval, io, TRUE);
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+
+ if ((x-1-io->pixels_per_tick/2-lwidth/2) < 5) {
+ x_pos=5;
+ } else if ((x-1-io->pixels_per_tick/2+lwidth/2) > (io->pixmap_width-5)) {
+ x_pos=io->pixmap_width-lwidth-5;
+ } else {
+ x_pos=x-1-io->pixels_per_tick/2-lwidth/2;
+ }
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (io->surface);
+#else
+ cr = gdk_cairo_create (io->pixmap);
+#endif
+ cairo_move_to (cr, x_pos, io->pixmap_height-bottom_y_border+15);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+
+ }
+ g_object_unref(G_OBJECT(layout));
+
+
+ /*
+ * Loop over all graphs and draw them
+ */
+ for(i=MAX_GRAPHS-1;i>=0;i--){
+ guint64 val;
+ guint32 interval, x_pos, y_pos, prev_x_pos, prev_y_pos;
+ /* Moving average variables */
+ guint32 mavg_in_average_count = 0, mavg_left = 0, mavg_right = 0;
+ guint64 mavg_cumulated = 0;
+ guint32 mavg_to_remove = 0, mavg_to_add = 0;
+
+ if(!io->graphs[i].display){
+ continue;
+ }
+
+ if(io->filter_type == MOVING_AVERAGE_FILTER){
+ /* "Warm-up phase" - calculate average on some data not displayed;
+ just to make sure average on leftmost and rightmost displayed
+ values is as reliable as possible
+ */
+ guint32 warmup_interval;
+
+ if(first_interval/io->interval > io->filter_order/2){
+ warmup_interval = first_interval/io->interval - io->filter_order/2;
+ warmup_interval*= io->interval;
+ } else {
+ warmup_interval = 0;
+ }
+ mavg_to_remove = warmup_interval;
+ for(;warmup_interval<first_interval;warmup_interval+=io->interval){
+ mavg_cumulated += get_it_value(io, i, warmup_interval/io->interval);
+ mavg_in_average_count++;
+ mavg_left++;
+ }
+ mavg_cumulated += get_it_value(io, i, warmup_interval/io->interval);
+ mavg_in_average_count++;
+ for(warmup_interval+=io->interval;warmup_interval<(first_interval+(io->filter_order/2)*io->interval)&&(warmup_interval<=(io->num_items*io->interval));warmup_interval+=io->interval){
+ mavg_cumulated += get_it_value(io, i, warmup_interval/io->interval);
+ mavg_in_average_count++;
+ mavg_right++;
+ }
+ mavg_to_add = warmup_interval;
+ }
+
+ /* initialize prev x/y to the value of the first interval */
+ prev_x_pos=draw_width-1-io->pixels_per_tick*((last_interval-first_interval)/io->interval)+io->left_x_border;
+ val=get_it_value(io, i, first_interval/io->interval);
+ if(io->filter_type == MOVING_AVERAGE_FILTER){
+ if(mavg_in_average_count>0){
+ val = mavg_cumulated/mavg_in_average_count;
+ }
+ }
+ if(val>max_y){
+ prev_y_pos=0;
+ } else if(io->max_y_units==LOGARITHMIC_YSCALE){
+ if (val==0) {
+ prev_y_pos=(guint32)(draw_height-1+top_y_border);
+ } else {
+ prev_y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
+ }
+ } else {
+ prev_y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
+ }
+
+ for(interval=first_interval;interval<last_interval;interval+=io->interval){
+ x_pos=draw_width-1-io->pixels_per_tick*((last_interval-interval)/io->interval)+io->left_x_border;
+
+ val=get_it_value(io, i, interval/io->interval);
+ /* Moving average calculation */
+ if(io->filter_type == MOVING_AVERAGE_FILTER){
+ if(interval!=first_interval){
+ mavg_left++;
+ if(mavg_left > io->filter_order/2){
+ mavg_left--;
+ mavg_in_average_count--;
+ mavg_cumulated -= get_it_value(io, i, mavg_to_remove/io->interval);
+ mavg_to_remove += io->interval;
+ }
+ if(mavg_to_add<=io->num_items*io->interval){
+ mavg_in_average_count++;
+ mavg_cumulated += get_it_value(io, i, mavg_to_add/io->interval);
+ mavg_to_add += io->interval;
+ }else{
+ mavg_right--;
+ }
+ }
+ val = mavg_cumulated / mavg_in_average_count;
+ }
+
+ if(val>max_y){
+ y_pos=0;
+ } else if(io->max_y_units==LOGARITHMIC_YSCALE){
+ if (val==0) {
+ y_pos=(guint32)(draw_height-1+top_y_border);
+ } else {
+ y_pos=(guint32)((draw_height-ystart)-1-((log10((double)((gint64)val)))*(draw_height-ystart))/(log10((double)max_y))+top_y_border);
+ }
+ } else {
+ y_pos=(guint32)(draw_height-1-(val*draw_height)/max_y+top_y_border);
+ }
+
+ switch(io->graphs[i].plot_style){
+ case PLOT_STYLE_LINE:
+ /* dont need to draw anything if the segment
+ * is entirely above the top of the graph
+ */
+ if( (prev_y_pos!=0) || (y_pos!=0) ){
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (io->surface);
+#else
+ cr = gdk_cairo_create (io->pixmap);
+#endif
+ gdk_cairo_set_source_color (cr, &io->graphs[i].color);
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, prev_x_pos+0.5, prev_y_pos+0.5);
+ cairo_line_to(cr, x_pos+0.5, y_pos+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ }
+ break;
+ case PLOT_STYLE_IMPULSE:
+ if(val){
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (io->surface);
+#else
+ cr = gdk_cairo_create (io->pixmap);
+#endif
+ gdk_cairo_set_source_color (cr, &io->graphs[i].color);
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, x_pos+0.5, draw_height-1+top_y_border+0.5);
+ cairo_line_to(cr, x_pos+0.5, y_pos+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ }
+ break;
+ case PLOT_STYLE_FILLED_BAR:
+ if(val){
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (io->surface);
+#else
+ cr = gdk_cairo_create (io->pixmap);
+#endif
+ cairo_rectangle (cr,
+ x_pos-(gdouble)io->pixels_per_tick/2+0.5,
+ y_pos+0.5,
+ io->pixels_per_tick,
+ draw_height-1+top_y_border-y_pos);
+ gdk_cairo_set_source_color (cr, &io->graphs[i].color);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ }
+ break;
+ case PLOT_STYLE_DOT:
+ if(val){
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (io->surface);
+#else
+ cr = gdk_cairo_create (io->pixmap);
+#endif
+ cairo_arc (cr,
+ x_pos+0.5,
+ y_pos+0.5,
+ (gdouble)io->pixels_per_tick/2,
+ 0,
+ 2 * G_PI);
+ gdk_cairo_set_source_color (cr, &io->graphs[i].color);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ }
+ break;
+ }
+
+ prev_y_pos=y_pos;
+ prev_x_pos=x_pos;
+ }
+ }
+
+ cr = gdk_cairo_create (gtk_widget_get_window(io->draw_area));
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, io->surface, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, io->pixmap, 0, 0);
+#endif
+ cairo_rectangle (cr, 0, 0, io->pixmap_width, io->pixmap_height);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+
+ /* update the scrollbar */
+ if (io->max_interval == 0) {
+ gtk_adjustment_set_upper(io->scrollbar_adjustment, (gfloat) io->interval);
+ gtk_adjustment_set_step_increment(io->scrollbar_adjustment, (gfloat) (io->interval/10));
+ gtk_adjustment_set_page_increment(io->scrollbar_adjustment, (gfloat) io->interval);
+ } else {
+ gtk_adjustment_set_upper(io->scrollbar_adjustment, (gfloat) io->max_interval);
+ gtk_adjustment_set_step_increment(io->scrollbar_adjustment, (gfloat) ((last_interval-first_interval)/10));
+ gtk_adjustment_set_page_increment(io->scrollbar_adjustment, (gfloat) (last_interval-first_interval));
+ }
+ gtk_adjustment_set_page_size(io->scrollbar_adjustment, gtk_adjustment_get_page_increment(io->scrollbar_adjustment));
+ gtk_adjustment_set_value(io->scrollbar_adjustment, (gfloat)first_interval);
+ gtk_adjustment_changed(io->scrollbar_adjustment);
+ gtk_adjustment_value_changed(io->scrollbar_adjustment);
+
+}
+
+static void
+io_stat_redraw(io_stat_t *io)
+{
+ io->needs_redraw=TRUE;
+ io_stat_draw(io);
+}
+
+static void
+tap_iostat_draw(void *g)
+{
+ io_stat_graph_t *git=g;
+
+ io_stat_draw(git->io);
+}
+
+/* ok we get called with both the filter and the field.
+ make sure the field is part of the filter.
+ (make sure and make sure just append it)
+ the field MUST be part of the filter or else we wont
+ be able to pick up the field values after the edt tree has been
+ pruned
+*/
+static GString *
+enable_graph(io_stat_graph_t *gio, const char *filter, const char *field)
+{
+ char real_filter[262];
+
+ gio->display=TRUE;
+
+ real_filter[0]=0;
+ if(filter){
+ /* skip all whitespaces */
+ while(*filter){
+ if(*filter==' '){
+ filter++;
+ continue;
+ }
+ if(*filter=='\t'){
+ filter++;
+ continue;
+ }
+ break;
+ }
+ if(*filter){
+ g_snprintf(real_filter, 257, "(%s)", filter);
+ real_filter[257]=0;
+ }
+ }
+ if(field){
+ /* skip all whitespaces */
+ while(*field){
+ if(*field==' '){
+ field++;
+ continue;
+ }
+ if(*field=='\t'){
+ field++;
+ continue;
+ }
+ break;
+ }
+ if(*field){
+ if(real_filter[0]!=0){
+ g_strlcat(real_filter, " && ", 262);
+ }
+ g_strlcat(real_filter, field, 262);
+ }
+ }
+ return register_tap_listener("frame", gio, real_filter[0]?real_filter:NULL,
+ TL_REQUIRES_PROTO_TREE,
+ tap_iostat_reset, tap_iostat_packet, tap_iostat_draw);
+}
+
+static void
+disable_graph(io_stat_graph_t *gio)
+{
+ if (gio->display) {
+ gio->display=FALSE;
+ protect_thread_critical_region();
+ remove_tap_listener(gio);
+ unprotect_thread_critical_region();
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button),
+ FALSE);
+ }
+}
+
+static void
+iostat_init(const char *optarg _U_, void* userdata _U_)
+{
+ io_stat_t *io;
+ int i=0;
+ static GdkColor col[MAX_GRAPHS] = {
+ {0, 0x0000, 0x0000, 0x0000}, /* Black */
+ {0, 0xffff, 0x0000, 0x0000}, /* Red */
+ {0, 0x0000, 0xffff, 0x0000}, /* Green */
+ {0, 0x0000, 0x0000, 0xffff}, /* Blue */
+ {0, 0xffff, 0x5000, 0xffff} /* Light brilliant magenta */
+ };
+#if GTK_CHECK_VERSION(3,0,0)
+ static GdkRGBA rgba_col[MAX_GRAPHS] = {
+ {0.0, 0.0, 0.0, 1.0}, /* Black */
+ {1.0, 0.0, 0.1, 1.0}, /* Red */
+ {0.0, 1.0, 0.0, 1.0}, /* Green */
+ {0.0, 0.0, 1.0, 1.0}, /* Blue */
+ {1.0, 0.314, 1.0, 1.0} /* Light brilliant magenta */
+ };
+#endif
+ GString *error_string;
+
+ io=g_malloc(sizeof(io_stat_t));
+ io->needs_redraw=TRUE;
+ io->interval=tick_interval_values[DEFAULT_TICK_VALUE_INDEX];
+ io->window=NULL;
+ io->draw_area=NULL;
+#if GTK_CHECK_VERSION(2,22,0)
+ io->surface=NULL;
+#else
+ io->pixmap=NULL;
+#endif
+ io->scrollbar=NULL;
+ io->scrollbar_adjustment=NULL;
+ io->pixmap_width=500;
+ io->pixmap_height=200;
+ io->pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK_INDEX];
+ io->max_y_units=AUTO_MAX_YSCALE;
+ io->count_type=0;
+ io->last_interval=0xffffffff;
+ io->max_interval=0;
+ io->num_items=0;
+ io->left_x_border=0;
+ io->right_x_border=500;
+ io->view_as_time=FALSE;
+ io->start_time.secs=0;
+ io->start_time.nsecs=0;
+
+ for(i=0;i<MAX_GRAPHS;i++){
+ io->graphs[i].color.pixel=col[i].pixel;
+ io->graphs[i].color.red=col[i].red;
+ io->graphs[i].color.green=col[i].green;
+ io->graphs[i].color.blue=col[i].blue;
+#if GTK_CHECK_VERSION(3,0,0)
+ io->graphs[i].rgba_color.red=rgba_col[i].red;
+ io->graphs[i].rgba_color.green=rgba_col[i].green;
+ io->graphs[i].rgba_color.blue=rgba_col[i].blue;
+ io->graphs[i].rgba_color.alpha=rgba_col[i].alpha;
+#endif
+ io->graphs[i].display=0;
+ io->graphs[i].display_button=NULL;
+ io->graphs[i].filter_field=NULL;
+ io->graphs[i].advanced_buttons=NULL;
+ io->graphs[i].io=io;
+
+ io->graphs[i].args=g_malloc(sizeof(construct_args_t));
+ io->graphs[i].args->title = NULL;
+ io->graphs[i].args->wants_apply_button=TRUE;
+ io->graphs[i].args->activate_on_ok=TRUE;
+ io->graphs[i].args->modal_and_transient=FALSE;
+
+ io->graphs[i].filter_bt=NULL;
+ }
+ io_stat_reset(io);
+
+ error_string=enable_graph(&io->graphs[0], NULL, NULL);
+ g_assert((error_string == NULL) && "Can't attach io_stat tap !");
+#if 0
+ if(error_string){
+
+ fprintf(stderr, "wireshark: Can't attach io_stat tap: %s\n",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ io->graphs[0].display=0;
+ io->graphs[0].display_button=NULL;
+ io->graphs[0].filter_field=NULL;
+ io->graphs[0].advanced_buttons=NULL;
+ exit(10);
+ }
+#endif
+ /* build the GUI */
+ init_io_stat_window(io);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(io->window));
+ io_stat_redraw(io);
+}
+
+static void
+draw_area_destroy_cb(GtkWidget *widget _U_, gpointer user_data)
+{
+ io_stat_t *io = user_data;
+ int i;
+ GtkWidget *save_bt = g_object_get_data(G_OBJECT(io->window), "save_bt");
+ surface_info_t *surface_info = g_object_get_data(G_OBJECT(save_bt), "surface-info");
+
+ g_free(surface_info);
+
+ for(i=0;i<MAX_GRAPHS;i++){
+ if(io->graphs[i].display){
+ protect_thread_critical_region();
+ remove_tap_listener(&io->graphs[i]);
+ unprotect_thread_critical_region();
+
+ g_free( (gpointer) (io->graphs[i].args->title) );
+ io->graphs[i].args->title=NULL;
+
+ g_free(io->graphs[i].args);
+ io->graphs[i].args=NULL;
+ }
+ }
+ g_free(io);
+
+ return;
+}
+
+static gboolean
+pixmap_clicked_event(GtkWidget *widget _U_, GdkEventButton *event, gpointer user_data)
+{
+ io_stat_t *io = user_data;
+ guint32 draw_width, interval, last_interval;
+ guint frame_num;
+
+ draw_width=io->pixmap_width-io->right_x_border-io->left_x_border;
+
+ if ((event->x <= (draw_width+io->left_x_border+1-(draw_width/io->pixels_per_tick)*io->pixels_per_tick)) ||
+ (event->x >= (draw_width+io->left_x_border-io->pixels_per_tick/2))) {
+ /* Outside draw area */
+ return FALSE;
+ }
+
+#if GTK_CHECK_VERSION(2,22,0)
+ if ((event->button==1 || event->button==3) && io->surface!=NULL) {
+#else
+ if ((event->button==1 || event->button==3) && io->pixmap!=NULL) {
+#endif
+ /*
+ * Button 1 selects the first package in the interval.
+ * Button 3 selects the last package in the interval.
+ */
+ if (io->last_interval==0xffffffff) {
+ last_interval=io->max_interval;
+ } else {
+ last_interval=io->last_interval;
+ }
+
+ interval=(guint32)((last_interval/io->interval)-(draw_width+io->left_x_border-event->x-io->pixels_per_tick/2-1)/io->pixels_per_tick);
+ frame_num=get_frame_num (io, interval, event->button==1?TRUE:FALSE);
+ if (frame_num != 0) {
+ cf_goto_frame(&cfile, frame_num);
+ }
+ }
+
+ return TRUE;
+}
+
+/* create a new backing pixmap of the appropriate size */
+static gboolean
+draw_area_configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer user_data)
+{
+ io_stat_t *io = user_data;
+ GtkWidget *save_bt;
+ GtkAllocation widget_alloc;
+ cairo_t *cr;
+#if GTK_CHECK_VERSION(2,22,0)
+ surface_info_t *surface_info = g_new(surface_info_t, 1);
+#endif
+
+#if GTK_CHECK_VERSION(2,22,0)
+ if(io->surface){
+ cairo_surface_destroy (io->surface);
+ io->surface=NULL;
+ }
+#else
+ if(io->pixmap){
+ g_object_unref(io->pixmap);
+ io->pixmap=NULL;
+ }
+#endif
+
+ gtk_widget_get_allocation(widget, &widget_alloc);
+#if GTK_CHECK_VERSION(2,22,0)
+ io->surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
+ CAIRO_CONTENT_COLOR,
+ widget_alloc.width,
+ widget_alloc.height);
+
+#else
+ io->pixmap=gdk_pixmap_new(gtk_widget_get_window(widget),
+ widget_alloc.width,
+ widget_alloc.height,
+ -1);
+#endif
+ io->pixmap_width=widget_alloc.width;
+ io->pixmap_height=widget_alloc.height;
+
+ save_bt = g_object_get_data(G_OBJECT(io->window), "save_bt");
+#if GTK_CHECK_VERSION(2,22,0)
+ surface_info->surface = io->surface;
+ surface_info->width = widget_alloc.width;
+ surface_info->height = widget_alloc.height;
+ g_object_set_data(G_OBJECT(save_bt), "surface-info", surface_info);
+ gtk_widget_set_sensitive(save_bt, TRUE);
+
+ cr = cairo_create (io->surface);
+#else
+ g_object_set_data(G_OBJECT(save_bt), "pixmap", io->pixmap);
+ gtk_widget_set_sensitive(save_bt, TRUE);
+
+ cr = gdk_cairo_create (io->pixmap);
+#endif
+ cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+ io_stat_redraw(io);
+ return TRUE;
+}
+
+static void
+scrollbar_changed(GtkWidget *widget _U_, gpointer user_data)
+{
+ io_stat_t *io = user_data;
+ guint32 mi;
+
+ mi=(guint32) (gtk_adjustment_get_value(io->scrollbar_adjustment) + gtk_adjustment_get_page_size(io->scrollbar_adjustment));
+ if(io->last_interval==mi){
+ return;
+ }
+ if( (io->last_interval==0xffffffff)
+ && (mi==io->max_interval) ){
+ return;
+ }
+
+ io->last_interval=(mi/io->interval)*io->interval;
+ io_stat_redraw(io);
+
+ return;
+}
+#if GTK_CHECK_VERSION(3,0,0)
+static gboolean
+draw_area_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
+{
+ io_stat_t *io = user_data;
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation (widget, &allocation);
+ cairo_set_source_surface (cr, io->surface, 0, 0);
+ cairo_rectangle (cr, 0, 0, allocation.width, allocation.width);
+ cairo_fill (cr);
+
+ return FALSE;
+}
+#else
+/* redraw the screen from the backing pixmap */
+static gboolean
+draw_area_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
+{
+ io_stat_t *io = user_data;
+ cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(widget));
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, io->surface, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, io->pixmap, 0, 0);
+#endif
+ cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+#endif
+static void
+create_draw_area(io_stat_t *io, GtkWidget *box)
+{
+ io->draw_area=gtk_drawing_area_new();
+ g_signal_connect(io->draw_area, "destroy", G_CALLBACK(draw_area_destroy_cb), io);
+
+ gtk_widget_set_size_request(io->draw_area, io->pixmap_width, io->pixmap_height);
+
+ /* signals needed to handle backing pixmap */
+#if GTK_CHECK_VERSION(3,0,0)
+ g_signal_connect(io->draw_area, "draw", G_CALLBACK(draw_area_draw), io);
+#else
+ g_signal_connect(io->draw_area, "expose-event", G_CALLBACK(draw_area_expose_event), io);
+#endif
+ g_signal_connect(io->draw_area, "configure-event", G_CALLBACK(draw_area_configure_event), io);
+ gtk_widget_add_events (io->draw_area, GDK_BUTTON_PRESS_MASK);
+ g_signal_connect(io->draw_area, "button-press-event", G_CALLBACK(pixmap_clicked_event), io);
+
+ gtk_widget_show(io->draw_area);
+ gtk_box_pack_start(GTK_BOX(box), io->draw_area, TRUE, TRUE, 0);
+
+ /* create the associated scrollbar */
+ io->scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
+ io->scrollbar=gtk_hscrollbar_new(io->scrollbar_adjustment);
+ gtk_widget_show(io->scrollbar);
+ gtk_box_pack_start(GTK_BOX(box), io->scrollbar, FALSE, FALSE, 0);
+ g_signal_connect(io->scrollbar_adjustment, "value-changed", G_CALLBACK(scrollbar_changed), io);
+}
+
+static void
+tick_interval_select(GtkWidget *item, gpointer user_data)
+{
+ io_stat_t *io = user_data;
+ int i;
+
+ i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
+
+ io->interval=tick_interval_values[i];
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(io->window));
+ io_stat_redraw(io);
+}
+
+static void
+pixels_per_tick_select(GtkWidget *item, gpointer user_data)
+{
+ io_stat_t *io = user_data;
+ int i;
+
+ i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
+ io->pixels_per_tick=pixels_per_tick[i];
+ io_stat_redraw(io);
+}
+
+static void
+plot_style_select(GtkWidget *item, gpointer user_data)
+{
+ io_stat_graph_t *ppt = user_data;
+ int val;
+
+ val=gtk_combo_box_get_active (GTK_COMBO_BOX(item));
+
+ ppt->plot_style=val;
+
+ io_stat_redraw(ppt->io);
+}
+
+static GtkWidget *
+create_pixels_per_tick_menu_items(io_stat_t *io)
+{
+ char str[5];
+ GtkWidget *combo_box;
+ int i;
+ combo_box = gtk_combo_box_text_new ();
+
+ for(i=0;i<MAX_PIXELS_PER_TICK;i++){
+ g_snprintf(str, 5, "%u", pixels_per_tick[i]);
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
+ }
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PIXELS_PER_TICK_INDEX);
+ g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), io);
+
+ return combo_box;
+}
+
+static void
+yscale_select(GtkWidget *item, gpointer user_data)
+{
+ io_stat_t *io = user_data;
+ int i;
+
+ i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
+
+ io->max_y_units = yscale_max[i];
+ io_stat_redraw(io);
+}
+
+static void
+filter_select(GtkWidget *item, gpointer user_data)
+{
+ io_stat_t *io = user_data;
+ int i;
+
+ i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
+
+ if(i==NO_FILTER_ORDER){
+ io->filter_type = NO_FILTER;
+ } else {
+ io->filter_type = MOVING_AVERAGE_FILTER;
+ io->filter_order = moving_average_orders[i];
+ }
+ io_stat_redraw(io);
+}
+
+static GtkWidget *
+create_tick_interval_menu_items(io_stat_t *io)
+{
+ GtkWidget *combo_box;
+ char str[15];
+ int i;
+
+ combo_box = gtk_combo_box_text_new ();
+
+ for(i=0;i<MAX_TICK_VALUES;i++){
+ if(tick_interval_values[i]>=60000){
+ g_snprintf(str, sizeof(str), "%u min", tick_interval_values[i]/60000);
+ } else if(tick_interval_values[i]>=1000){
+ g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
+ } else if(tick_interval_values[i]>=100){
+ g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
+ } else if(tick_interval_values[i]>=10){
+ g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
+ } else {
+ g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
+ }
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
+ }
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_TICK_VALUE_INDEX);
+ g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), io);
+
+ return combo_box;
+}
+
+static GtkWidget *
+create_yscale_max_menu_items(io_stat_t *io)
+{
+ char str[15];
+ GtkWidget *combo_box;
+ int i;
+
+ combo_box = gtk_combo_box_text_new ();
+ for(i=0;i<MAX_YSCALE;i++){
+ if(yscale_max[i]==LOGARITHMIC_YSCALE){
+ g_strlcpy(str, "Logarithmic", 15);
+ } else if(yscale_max[i]==AUTO_MAX_YSCALE){
+ g_strlcpy(str, "Auto", 15);
+ } else {
+ g_snprintf(str, 15, "%u", yscale_max[i]);
+ }
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
+ }
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_YSCALE_INDEX);
+ g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), io);
+ return combo_box;
+}
+
+static GtkWidget *
+create_filter_menu_items(io_stat_t *io)
+{
+ char str[15];
+ GtkWidget *combo_box;
+ int i;
+
+ combo_box = gtk_combo_box_text_new ();
+
+ for(i=0;i<MAX_MOVING_AVERAGE_ORDER;i++){
+ if(i==NO_FILTER_ORDER){
+ g_strlcpy(str, "No filter", 15);
+ } else {
+ g_snprintf(str, 15, "M.avg %u", moving_average_orders[i]);
+ }
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
+ }
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0);
+ g_signal_connect(combo_box, "changed", G_CALLBACK(filter_select), io);
+ return combo_box;
+}
+
+static void
+count_type_select(GtkWidget *item, gpointer user_data)
+{
+ io_stat_t *io = user_data;
+ static gboolean advanced_visible=FALSE;
+ int i;
+ GtkAllocation widget_alloc;
+
+ io->count_type = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
+
+ if(io->count_type==COUNT_TYPE_ADVANCED){
+ for(i=0;i<MAX_GRAPHS;i++){
+ disable_graph(&io->graphs[i]);
+ gtk_widget_show(io->graphs[i].advanced_buttons);
+ /* redraw the entire window so the unhidden widgets show up, hopefully */
+ gtk_widget_get_allocation(io->window, &widget_alloc);
+ gtk_widget_queue_draw_area(io->window,
+ 0,
+ 0,
+ widget_alloc.width,
+ widget_alloc.height);
+ }
+ advanced_visible=TRUE;
+ io_stat_redraw(io);
+ } else if (advanced_visible) {
+ for(i=0;i<MAX_GRAPHS;i++){
+ gtk_widget_hide(io->graphs[i].advanced_buttons);
+ filter_callback(item, &io->graphs[i]);
+ }
+ advanced_visible=FALSE;
+ } else {
+ io_stat_redraw(io);
+ }
+}
+
+static GtkWidget *
+create_frames_or_bytes_menu_items(io_stat_t *io)
+{
+ GtkWidget *combo_box;
+ int i;
+
+ combo_box = gtk_combo_box_text_new ();
+
+ for(i=0;i<MAX_COUNT_TYPES;i++){
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), count_type_names[i]);
+ }
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_COUNT_TYPE);
+ g_signal_connect(combo_box, "changed", G_CALLBACK(count_type_select), io);
+ return combo_box;
+}
+
+static void
+create_ctrl_menu(io_stat_t *io, GtkWidget *box, const char *name, GtkWidget * (*func)(io_stat_t *io))
+{
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *combo_box;
+
+ hbox=gtk_hbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(box), hbox);
+ gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
+ gtk_widget_show(hbox);
+
+ label=gtk_label_new(name);
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+ combo_box = (*func)(io);
+ gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
+ gtk_widget_show(combo_box);
+}
+
+static void
+view_as_time_toggle_dest(GtkWidget *widget _U_, gpointer user_data)
+{
+ io_stat_t *io = user_data;
+
+ io->view_as_time = io->view_as_time ? FALSE : TRUE;
+
+ io_stat_redraw(io);
+}
+
+static void
+create_ctrl_area(io_stat_t *io, GtkWidget *box)
+{
+ GtkWidget *frame_vbox;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *view_cb;
+
+ frame_vbox=gtk_vbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(box), frame_vbox, FALSE, FALSE, 0);
+ gtk_widget_show(frame_vbox);
+
+ frame = gtk_frame_new("X Axis");
+ gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
+ gtk_widget_show(frame);
+
+ vbox=gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(frame), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
+ gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
+ gtk_widget_show(vbox);
+
+ create_ctrl_menu(io, vbox, "Tick interval:", create_tick_interval_menu_items);
+ create_ctrl_menu(io, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
+
+ view_cb = gtk_check_button_new_with_mnemonic("_View as time of day");
+ gtk_container_add(GTK_CONTAINER(vbox), view_cb);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(view_cb), io->view_as_time);
+ g_signal_connect(view_cb, "toggled", G_CALLBACK(view_as_time_toggle_dest), io);
+ gtk_widget_show(view_cb);
+
+ frame = gtk_frame_new("Y Axis");
+ gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
+ gtk_widget_show(frame);
+
+ vbox=gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(frame), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
+ gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
+ gtk_widget_show(vbox);
+
+ create_ctrl_menu(io, vbox, "Unit:", create_frames_or_bytes_menu_items);
+ create_ctrl_menu(io, vbox, "Scale:", create_yscale_max_menu_items);
+ create_ctrl_menu(io, vbox, "Smooth:", create_filter_menu_items);
+
+ return;
+}
+
+static void
+filter_callback(GtkWidget *widget _U_, gpointer user_data)
+{
+ io_stat_graph_t *gio = user_data;
+ const char *filter;
+ const char *field=NULL;
+ header_field_info *hfi;
+ dfilter_t *dfilter;
+
+ /* this graph is not active, just update display and redraw */
+ if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gio->display_button))){
+ disable_graph(gio);
+ io_stat_redraw(gio->io);
+ return;
+ }
+
+ /* first check if the field string is valid */
+ if(gio->io->count_type==COUNT_TYPE_ADVANCED){
+ field=gtk_entry_get_text(GTK_ENTRY(gio->calc_field));
+
+ /* warn and bail out if there was no field specified */
+ if(field==NULL || field[0]==0){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "You didn't specify a field name.");
+ disable_graph(gio);
+ io_stat_redraw(gio->io);
+ return;
+ }
+ /* warn and bail out if the field could not be found */
+ hfi=proto_registrar_get_byname(field);
+ if(hfi==NULL){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "There is no field named '%s'.", field);
+ disable_graph(gio);
+ io_stat_redraw(gio->io);
+ return;
+ }
+ gio->hf_index=hfi->id;
+ /* check that the type is compatible */
+ switch(hfi->type){
+ case FT_UINT8:
+ case FT_UINT16:
+ case FT_UINT24:
+ case FT_UINT32:
+ case FT_INT8:
+ case FT_INT16:
+ case FT_INT24:
+ case FT_INT32:
+ case FT_FLOAT:
+ case FT_DOUBLE:
+ /* these values support all calculations except LOAD */
+ switch(gio->calc_type){
+ case CALC_TYPE_LOAD:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "LOAD(*) is only supported for relative-time fields.");
+ disable_graph(gio);
+ io_stat_redraw(gio->io);
+ return;
+ }
+ /* these types support all calculations */
+ break;
+ case FT_RELATIVE_TIME:
+ /* this type only supports COUNT, MAX, MIN, AVG */
+ switch(gio->calc_type){
+ case CALC_TYPE_SUM:
+ case CALC_TYPE_COUNT:
+ case CALC_TYPE_MAX:
+ case CALC_TYPE_MIN:
+ case CALC_TYPE_AVG:
+ case CALC_TYPE_LOAD:
+ break;
+ default:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s is a relative-time field, so %s calculations are not supported on it.",
+ field,
+ calc_type_names[gio->calc_type]);
+ disable_graph(gio);
+ io_stat_redraw(gio->io);
+ return;
+ }
+ break;
+ case FT_UINT64:
+ case FT_INT64:
+ /*
+ * XXX - support this if gint64/guint64 are
+ * available?
+ */
+ if(gio->calc_type!=CALC_TYPE_COUNT){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s is a 64-bit integer, so %s calculations are not supported on it.",
+ field,
+ calc_type_names[gio->calc_type]);
+ disable_graph(gio);
+ io_stat_redraw(gio->io);
+ return;
+ }
+ break;
+ default:
+ if(gio->calc_type!=CALC_TYPE_COUNT){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s doesn't have integral or float values, so %s calculations are not supported on it.",
+ field,
+ calc_type_names[gio->calc_type]);
+ disable_graph(gio);
+ io_stat_redraw(gio->io);
+ return;
+ }
+ break;
+ }
+ }
+
+ /* first check if the filter string is valid. */
+ filter=gtk_entry_get_text(GTK_ENTRY(gio->filter_field));
+ if(!dfilter_compile(filter, &dfilter)) {
+ bad_dfilter_alert_box(filter);
+ disable_graph(gio);
+ io_stat_redraw(gio->io);
+ return;
+ }
+ if (dfilter != NULL)
+ dfilter_free(dfilter);
+
+ /* ok, we have a valid filter and the graph is active.
+ first just try to delete any previous settings and then apply
+ the new ones.
+ */
+ protect_thread_critical_region();
+ remove_tap_listener(gio);
+ unprotect_thread_critical_region();
+
+ io_stat_reset(gio->io);
+ enable_graph(gio, filter, field);
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(gio->io->window));
+ io_stat_redraw(gio->io);
+
+ return;
+}
+
+static void
+calc_type_select(GtkWidget *item, gpointer user_data)
+{
+ io_stat_graph_t *gio = user_data;
+
+ gio->calc_type=gtk_combo_box_get_active (GTK_COMBO_BOX(item));
+
+ /* disable the graph */
+ disable_graph(gio);
+ io_stat_redraw(gio->io);
+}
+
+static GtkWidget *
+create_calc_types_menu_items(io_stat_graph_t *gio)
+{
+ GtkWidget *combo_box;
+ int i;
+
+ combo_box = gtk_combo_box_text_new ();
+ for(i=0;i<MAX_CALC_TYPES;i++){
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), calc_type_names[i]);
+ }
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_CALC_TYPE);
+ g_signal_connect(combo_box, "changed", G_CALLBACK(calc_type_select), gio);
+ return combo_box;
+}
+
+static void
+create_advanced_menu(io_stat_graph_t *gio, GtkWidget *box, const char *name, GtkWidget *(*func)(io_stat_graph_t *io))
+{
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *combo_box;
+
+ hbox=gtk_hbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(box), hbox);
+ gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
+ gtk_widget_show(hbox);
+
+ label=gtk_label_new(name);
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+ combo_box = (*func)(gio);
+ gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
+ gtk_widget_show(combo_box);
+}
+
+static void
+create_advanced_field(io_stat_graph_t *gio, GtkWidget *box)
+{
+
+ gio->calc_field=gtk_entry_new();
+ gtk_entry_set_max_length(GTK_ENTRY(gio->calc_field),100);
+ gtk_box_pack_start(GTK_BOX(box), gio->calc_field, TRUE, TRUE, 0);
+ gtk_widget_show(gio->calc_field);
+ g_signal_connect(gio->calc_field, "activate", G_CALLBACK(filter_callback), gio);
+ g_object_set_data (G_OBJECT(gio->calc_field), E_FILT_FIELD_NAME_ONLY_KEY, "");
+ g_signal_connect(gio->calc_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
+ g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+ g_signal_connect(gio->calc_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
+ g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
+ colorize_filter_te_as_empty(gio->calc_field);
+}
+
+static void
+create_advanced_box(io_stat_graph_t *gio, GtkWidget *box)
+{
+ GtkWidget *hbox;
+
+ hbox=gtk_hbox_new(FALSE, 0);
+ gio->advanced_buttons=hbox;
+ gtk_container_add(GTK_CONTAINER(box), hbox);
+ gtk_box_set_child_packing(GTK_BOX(box), hbox, TRUE, TRUE, 0, GTK_PACK_START);
+ gtk_widget_hide(hbox);
+
+ gio->calc_type=CALC_TYPE_SUM;
+ create_advanced_menu(gio, hbox, "Calc:", create_calc_types_menu_items);
+ create_advanced_field(gio, hbox);
+}
+
+static void
+filter_button_clicked(GtkWidget *w, gpointer user_data)
+{
+ io_stat_graph_t *gio = user_data;
+
+ display_filter_construct_cb(w, gio->args);
+ return;
+}
+
+static void
+create_filter_box(io_stat_graph_t *gio, GtkWidget *box, int num)
+{
+ GtkWidget *combo_box;
+ GtkWidget *hbox;
+ GtkWidget *label;
+ char str[256];
+ int i;
+
+ hbox=gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(box), hbox);
+ gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
+ gtk_widget_show(hbox);
+
+ g_snprintf(str, 256, "Graph %d", num);
+ gio->display_button=gtk_toggle_button_new_with_label(str);
+ gtk_box_pack_start(GTK_BOX(hbox), gio->display_button, FALSE, FALSE, 0);
+ gtk_widget_show(gio->display_button);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gio->display_button), gio->display);
+ g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);
+
+ label=gtk_label_new("Color");
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_color(label, GTK_STATE_NORMAL, &gio->rgba_color);
+ gtk_widget_override_color(label, GTK_STATE_ACTIVE, &gio->rgba_color);
+ gtk_widget_override_color(label, GTK_STATE_PRELIGHT, &gio->rgba_color);
+ gtk_widget_override_color(label, GTK_STATE_SELECTED, &gio->rgba_color);
+ gtk_widget_override_color(label, GTK_STATE_INSENSITIVE, &gio->rgba_color);
+#else
+ gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &gio->color);
+ gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &gio->color);
+ gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &gio->color);
+ gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &gio->color);
+ gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &gio->color);
+#endif
+/* g_signal_connect(gio->display_button, "toggled", G_CALLBACK(filter_callback), gio);*/
+
+
+ /* filter prefs dialog */
+ gio->filter_bt=gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
+
+ g_snprintf(str, 256, "Wireshark: Display Filter IO-Stat (Filter:%d)", num);
+ g_free( (gpointer) (gio->args->title) );
+ gio->args->title=g_strdup(str);
+
+ g_signal_connect(gio->filter_bt, "clicked", G_CALLBACK(filter_button_clicked), gio);
+ g_signal_connect(gio->filter_bt, "destroy", G_CALLBACK(filter_button_destroy_cb), NULL);
+
+ gtk_box_pack_start(GTK_BOX(hbox), gio->filter_bt, FALSE, TRUE, 0);
+ gtk_widget_show(gio->filter_bt);
+
+ gio->filter_field=gtk_entry_new();
+ gtk_entry_set_max_length(GTK_ENTRY(gio->filter_field),256);
+ /* filter prefs dialog */
+ g_object_set_data(G_OBJECT(gio->filter_bt), E_FILT_TE_PTR_KEY, gio->filter_field);
+ /* filter prefs dialog */
+
+ gtk_box_pack_start(GTK_BOX(hbox), gio->filter_field, TRUE, TRUE, 0);
+ gtk_widget_show(gio->filter_field);
+ g_signal_connect(gio->filter_field, "activate", G_CALLBACK(filter_callback), gio);
+ g_signal_connect(gio->filter_field, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
+ g_object_set_data(G_OBJECT(box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+ g_signal_connect(gio->filter_field, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
+ g_signal_connect(gio->io->window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
+ colorize_filter_te_as_empty(gio->filter_field);
+
+ create_advanced_box(gio, hbox);
+
+ /*
+ * create PlotStyle menu
+ */
+ g_snprintf(str, 256, " Style:");
+ label=gtk_label_new(str);
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+ combo_box = gtk_combo_box_text_new ();
+ for(i=0;i<MAX_PLOT_STYLES;i++){
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), plot_style_name[i]);
+ }
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PLOT_STYLE);
+ g_signal_connect(combo_box, "changed", G_CALLBACK(plot_style_select), &gio->io->graphs[num-1]);
+
+ gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
+ gtk_widget_show(combo_box);
+
+ return;
+}
+
+static void
+create_filter_area(io_stat_t *io, GtkWidget *box)
+{
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ int i;
+
+ frame=gtk_frame_new("Graphs");
+ gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
+ gtk_widget_show(frame);
+
+ vbox=gtk_vbox_new(FALSE, 1);
+ gtk_container_add(GTK_CONTAINER(frame), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
+ gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
+ gtk_widget_show(vbox);
+
+ for(i=0;i<MAX_GRAPHS;i++){
+ create_filter_box(&io->graphs[i], vbox, i+1);
+ }
+
+ return;
+}
+
+static void
+copy_as_csv_cb(GtkWindow *copy_bt _U_, gpointer user_data)
+{
+ guint32 i, interval, val;
+ char string[15];
+ GtkClipboard *cb;
+ GString *CSV_str=g_string_new("");
+ io_stat_t *io = user_data;
+
+ g_string_append(CSV_str, "\"Interval start\"");
+ for(i=0;i<MAX_GRAPHS;i++) {
+ if (io->graphs[i].display) {
+ g_string_append_printf(CSV_str, ",\"Graph %d\"", i+1);
+ }
+ }
+ g_string_append(CSV_str,"\n");
+
+ for(interval=0; interval<io->max_interval; interval+=io->interval) {
+ print_interval_string (string, 15, interval, io, FALSE);
+ g_string_append_printf(CSV_str, "\"%s\"", string);
+ for(i=0;i<MAX_GRAPHS;i++) {
+ if (io->graphs[i].display) {
+ val=get_it_value(io, i, interval/io->interval);
+ g_string_append_printf(CSV_str, ",\"%d\"", val);
+ }
+ }
+ g_string_append(CSV_str,"\n");
+ }
+
+ /* Now that we have the CSV data, copy it into the default clipboard */
+ cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); /* Get the default clipboard */
+ gtk_clipboard_set_text(cb, CSV_str->str, -1); /* Copy the CSV data into the clipboard */
+ g_string_free(CSV_str, TRUE); /* Free the memory */
+}
+
+static void
+init_io_stat_window(io_stat_t *io)
+{
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *bbox;
+ GtkWidget *close_bt, *help_bt;
+ GtkWidget *copy_bt;
+ GtkWidget *save_bt;
+
+ /* create the main window, transient_for top_level */
+ io->window = dlg_window_new("I/O Graphs");
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(io->window), TRUE);
+
+ vbox=gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(io->window), vbox);
+ gtk_widget_show(vbox);
+
+ create_draw_area(io, vbox);
+
+ hbox=gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
+ gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
+ gtk_widget_show(hbox);
+
+ create_filter_area(io, hbox);
+ create_ctrl_area(io, hbox);
+
+ io_stat_set_title(io);
+
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_SAVE,
+ GTK_STOCK_COPY, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(io->window, close_bt, window_cancel_button_cb);
+ gtk_widget_set_tooltip_text(close_bt, "Close this dialog");
+ save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
+ gtk_widget_set_sensitive(save_bt, FALSE);
+ gtk_widget_set_tooltip_text(save_bt, "Save the displayed graph to a file");
+ g_signal_connect(save_bt, "clicked", G_CALLBACK(pixmap_save_cb), NULL);
+ g_object_set_data(G_OBJECT(io->window), "save_bt", save_bt);
+
+ copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY);
+ gtk_widget_set_tooltip_text(copy_bt, "Copy values from selected graphs to the clipboard in CSV (Comma Separated Values) format");
+ g_signal_connect(copy_bt, "clicked", G_CALLBACK(copy_as_csv_cb), io);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_IO_GRAPH_DIALOG);
+ gtk_widget_set_tooltip_text (help_bt, "Show topic specific help");
+ g_signal_connect(io->window, "delete-event", G_CALLBACK(window_delete_event_cb), NULL);
+
+ gtk_widget_show(io->window);
+ window_present(io->window);
+}
+
+void
+gui_iostat_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ iostat_init(NULL,NULL);
+}
+
+void
+register_tap_listener_gtk_iostat(void)
+{
+ register_stat_cmd_arg("io,stat", iostat_init,NULL);
+}
diff --git a/ui/gtk/keys.h b/ui/gtk/keys.h
new file mode 100644
index 0000000000..13dc6ccd49
--- /dev/null
+++ b/ui/gtk/keys.h
@@ -0,0 +1,126 @@
+/* keys.h
+ * Key definitions for various objects
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __KEYS_H__
+#define __KEYS_H__
+
+/** @file
+ * Various keys for g_object_set_data().
+ */
+#define E_DFILTER_CM_KEY "display_filter_combo"
+
+#define E_DFILTER_TE_KEY "display_filter_entry"
+#define E_RFILTER_TE_KEY "read_filter_te"
+#define E_MPACKET_LIST_KEY "menu_packet_list"
+#define E_MPACKET_LIST_ROW_KEY "menu_packet_list_row"
+#define E_MPACKET_LIST_COL_KEY "menu_packet_list_col"
+#define E_MPACKET_LIST_COLUMN_KEY "menu_packet_list_column"
+#define E_MPACKET_LIST_PREV_COLUMN_KEY "menu_packet_list_prev_column"
+
+#define PRINT_CMD_LB_KEY "printer_command_label"
+#define PRINT_CMD_TE_KEY "printer_command_entry"
+#define PRINT_FILE_BT_KEY "printer_file_button"
+#define PRINT_FILE_TE_KEY "printer_file_entry"
+
+#define PLUGINS_DFILTER_TE "plugins_dfilter_te"
+
+#define PM_MENU_LIST_KEY "popup_menu_menu_list"
+#define PM_PACKET_LIST_COL_KEY "popup_menu_packet_list_column"
+#define PM_PACKET_LIST_KEY "popup_menu_packet_list"
+#define PM_TREE_VIEW_KEY "popup_menu_tree_view"
+#define PM_BYTES_VIEW_KEY "popup_menu_bytes_view"
+#define PM_STATUSBAR_PROFILES_KEY "popup_menu_statusbar_profiles"
+
+#define E_TB_MAIN_KEY "toolbar_main"
+#define E_TB_FILTER_KEY "toolbar_filter"
+
+#ifdef HAVE_AIRPCAP
+#define AIRPCAP_TOOLBAR_KEY "airpcap_toolbar_key"
+#define AIRPCAP_TOOLBAR_INTERFACE_KEY "airpcap_toolbar_if_key"
+#define AIRPCAP_TOOLBAR_LINK_TYPE_KEY "airpcap_toolbar_lt_key"
+#define AIRPCAP_TOOLBAR_CHANNEL_KEY "airpcap_toolbar_ch_key"
+#define AIRPCAP_TOOLBAR_CHANNEL_LABEL_KEY "airpcap_toolbar_ch_lb_key"
+#define AIRPCAP_TOOLBAR_CHANNEL_OFFSET_KEY "airpcap_toolbar_ch_offset_key"
+#define AIRPCAP_TOOLBAR_CHANNEL_OFFSET_LABEL_KEY "airpcap_toolbar_ch_offset_lb_key"
+#define AIRPCAP_TOOLBAR_FCS_CHECK_KEY "airpcap_toolbar_fcs_check_key"
+#define AIRPCAP_TOOLBAR_FCS_FILTER_LABEL_KEY "airpcap_toolbar_fcs_filter_lb_key"
+#define AIRPCAP_TOOLBAR_FCS_FILTER_KEY "airpcap_toolbar_fcs_filter_key"
+#define AIRPCAP_TOOLBAR_ADVANCED_KEY "airpcap_toolbar_advanced_key"
+#define AIRPCAP_TOOLBAR_KEY_MANAGEMENT_KEY "airpcap_toolbar_key_management_key"
+#define AIRPCAP_TOOLBAR_DECRYPTION_KEY "airpcap_toolbar_decryption_key"
+#define AIRPCAP_TOOLBAR_DECRYPTION_LABEL_KEY "airpcap_toolbar_decryption_lb_key"
+
+#define AIRPCAP_ADVANCED_KEY "airpcap_advanced_key"
+#define AIRPCAP_ADVANCED_INTERFACE_KEY "airpcap_advanced_if_key"
+#define AIRPCAP_ADVANCED_LINK_TYPE_KEY "airpcap_advanced_lt_key"
+#define AIRPCAP_ADVANCED_CHANNEL_KEY "airpcap_advanced_ch_key"
+#define AIRPCAP_ADVANCED_CHANNEL_OFFSET_KEY "airpcap_advanced_ch_offset_key"
+#define AIRPCAP_ADVANCED_FCS_CHECK_KEY "airpcap_advanced_fcs_check_key"
+#define AIRPCAP_ADVANCED_FCS_FILTER_KEY "airpcap_advanced_fcs_filter_key"
+#define AIRPCAP_ADVANCED_BLINK_KEY "airpcap_advanced_blink_key"
+#define AIRPCAP_ADVANCED_ADD_KEY_TEXT_KEY "airpcap_advanced_add_key_text_key"
+#define AIRPCAP_ADVANCED_ADD_KEY_OK_KEY "airpcap_advanced_add_key_ok_key"
+#define AIRPCAP_ADVANCED_ADD_KEY_LIST_KEY "airpcap_advanced_add_key_list_key"
+#define AIRPCAP_ADVANCED_ADD_KEY_TYPE_KEY "airpcap_advanced_add_key_type_key"
+#define AIRPCAP_ADVANCED_ADD_KEY_KEY_KEY "airpcap_advanced_add_key_key_key"
+#define AIRPCAP_ADVANCED_ADD_KEY_SSID_KEY "airpcap_advanced_add_key_ssid_key"
+#define AIRPCAP_ADVANCED_ADD_KEY_KEY_LABEL_KEY "airpcap_advanced_add_key_key_label_key"
+#define AIRPCAP_ADVANCED_ADD_KEY_SSID_LABEL_KEY "airpcap_advanced_add_key_ssid_label_key"
+#define AIRPCAP_ADVANCED_EDIT_KEY_SELECTED_KEY "airpcap_advanced_edit_key_selected_key"
+#define AIRPCAP_ADVANCED_EDIT_KEY_OK_KEY "airpcap_advanced_edit_key_ok_key"
+#define AIRPCAP_ADVANCED_EDIT_KEY_LIST_KEY "airpcap_advanced_edit_key_list_key"
+#define AIRPCAP_ADVANCED_EDIT_KEY_TYPE_KEY "airpcap_advanced_edit_key_type_key"
+#define AIRPCAP_ADVANCED_EDIT_KEY_KEY_KEY "airpcap_advanced_edit_key_key_key"
+#define AIRPCAP_ADVANCED_EDIT_KEY_SSID_KEY "airpcap_advanced_edit_key_ssid_key"
+#define AIRPCAP_ADVANCED_EDIT_KEY_KEY_LABEL_KEY "airpcap_advanced_edit_key_key_label_key"
+#define AIRPCAP_ADVANCED_EDIT_KEY_SSID_LABEL_KEY "airpcap_advanced_edit_key_ssid_label_key"
+#define AIRPCAP_ADVANCED_DECRYPTION_MODE_KEY "airpcap_advanced_decryption_mode_key"
+#define AIRPCAP_ADVANCED_WPA_DECRYPTION_KEY "airpcap_advanced_wpa_decryption_key"
+#define AIRPCAP_ADVANCED_NOTEBOOK_KEY "airpcap_advanced_notebook_key"
+#define AIRPCAP_ADVANCED_CANCEL_KEY "airpcap_advanced_cancel_key"
+#define AIRPCAP_ADVANCED_OK_KEY "airpcap_advanced_ok_key"
+#define AIRPCAP_ADVANCED_KEYLIST_KEY "airpcap_advanced_keylist_key"
+#define AIRPCAP_CHECK_WINDOW_RADIO_KEEP_KEY "airpcap_check_window_radio_keep_key"
+#define AIRPCAP_CHECK_WINDOW_RADIO_MERGE_KEY "airpcap_check_window_radio_merge_key"
+#define AIRPCAP_CHECK_WINDOW_RADIO_IMPORT_KEY "airpcap_check_window_radio_import_key"
+#define AIRPCAP_CHECK_WINDOW_RADIO_IGNORE_KEY "airpcap_check_window_radio_ignore_key"
+#define AIRPCAP_CHECK_WINDOW_RADIO_GROUP_KEY "airpcap_check_window_radio_group_key"
+
+#define AIRPCAP_CHECK_WINDOW_KEY "airpcap_check_window_key"
+
+#define AIRPCAP_ADVANCED_EDIT_KEY_SELECTION_KEY "airpcap_advanced_edit_key_selection_key"
+
+#define AIRPCAP_OPTIONS_ADVANCED_KEY "airpcap_options_advanced_key"
+
+#define AIRPCAP_ADVANCED_FROM_KEY "airpcap_advanced_from_key"
+
+#define AIRPCAP_KEY_MGMT_NEW_KEY "airpcap_key_mgmt_new_key"
+#define AIRPCAP_KEY_MGMT_EDIT_KEY "airpcap_key_mgmt_edit_key"
+#define AIRPCAP_KEY_MGMT_DELETE_KEY "airpcap_key_mgmt_delete_key"
+#define AIRPCAP_KEY_MGMT_UP_KEY "airpcap_key_mgmt_up_key"
+#define AIRPCAP_KEY_MGMT_DOWN_KEY "airpcap_key_mgmt_down_key"
+#endif /* HAVE_AIRPCAP */
+
+#endif /* __KEYS_H__ */
+
diff --git a/ui/gtk/ldap_stat.c b/ui/gtk/ldap_stat.c
new file mode 100644
index 0000000000..ce24391b22
--- /dev/null
+++ b/ui/gtk/ldap_stat.c
@@ -0,0 +1,262 @@
+/* ldap_stat.c
+ * ldap_stat 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/value_string.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-ldap.h>
+
+#include "../timestats.h"
+#include "../simple_dialog.h"
+#include "../file.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/service_response_time_table.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/main.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _ldapstat_t {
+ GtkWidget *win;
+ srt_stat_table ldap_srt_table;
+} ldapstat_t;
+
+static void
+ldapstat_set_title(ldapstat_t *ldap)
+{
+ char *title;
+
+ title = g_strdup_printf("LDAP Service Response Time statistics: %s",
+ cf_get_display_name(&cfile));
+ gtk_window_set_title(GTK_WINDOW(ldap->win), title);
+ g_free(title);
+}
+
+static void
+ldapstat_reset(void *pldap)
+{
+ ldapstat_t *ldap=(ldapstat_t *)pldap;
+
+ reset_srt_table_data(&ldap->ldap_srt_table);
+ ldapstat_set_title(ldap);
+}
+
+static int
+ldapstat_packet(void *pldap, packet_info *pinfo, epan_dissect_t *edt _U_, const void *psi)
+{
+ const ldap_call_response_t *ldap=psi;
+ ldapstat_t *fs=(ldapstat_t *)pldap;
+
+ /* we are only interested in reply packets */
+ if(ldap->is_request){
+ return 0;
+ }
+ /* if we havnt seen the request, just ignore it */
+ if(!ldap->req_frame){
+ return 0;
+ }
+
+ /* only use the commands we know how to handle */
+ switch(ldap->protocolOpTag){
+ case LDAP_REQ_BIND:
+ case LDAP_REQ_SEARCH:
+ case LDAP_REQ_MODIFY:
+ case LDAP_REQ_ADD:
+ case LDAP_REQ_DELETE:
+ case LDAP_REQ_MODRDN:
+ case LDAP_REQ_COMPARE:
+ case LDAP_REQ_EXTENDED:
+ break;
+ default:
+ return 0;
+ }
+
+ add_srt_table_data(&fs->ldap_srt_table, ldap->protocolOpTag, &ldap->req_time, pinfo);
+
+ return 1;
+}
+
+
+
+static void
+ldapstat_draw(void *pldap)
+{
+ ldapstat_t *ldap=(ldapstat_t *)pldap;
+
+ draw_srt_table_data(&ldap->ldap_srt_table);
+}
+
+
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ ldapstat_t *ldap=(ldapstat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(ldap);
+ unprotect_thread_critical_region();
+
+ free_srt_table_data(&ldap->ldap_srt_table);
+ g_free(ldap);
+}
+
+
+static void
+gtk_ldapstat_init(const char *optarg, void *userdata _U_)
+{
+ ldapstat_t *ldap;
+ const char *filter=NULL;
+ GtkWidget *label;
+ char *filter_string;
+ GString *error_string;
+ GtkWidget *vbox;
+ GtkWidget *bbox;
+ GtkWidget *close_bt;
+
+ if(!strncmp(optarg,"ldap,srt,",9)){
+ filter=optarg+9;
+ } else {
+ filter=NULL;
+ }
+
+ ldap=g_malloc(sizeof(ldapstat_t));
+
+ ldap->win = dlg_window_new("ldap-stat");
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(ldap->win), TRUE);
+ gtk_window_set_default_size(GTK_WINDOW(ldap->win), 550, 400);
+ ldapstat_set_title(ldap);
+
+ vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(ldap->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ label=gtk_label_new("LDAP Service Response Time statistics");
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ filter_string = g_strdup_printf("Filter: %s", filter ? filter : "");
+ label=gtk_label_new(filter_string);
+ g_free(filter_string);
+ gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ label=gtk_label_new("LDAP Commands");
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ /* We must display TOP LEVEL Widget before calling init_srt_table() */
+ gtk_widget_show_all(ldap->win);
+
+ init_srt_table(&ldap->ldap_srt_table, 24, vbox, NULL);
+ init_srt_table_row(&ldap->ldap_srt_table, 0, "Bind");
+ init_srt_table_row(&ldap->ldap_srt_table, 1, "<unknown>");
+ init_srt_table_row(&ldap->ldap_srt_table, 2, "<unknown>");
+ init_srt_table_row(&ldap->ldap_srt_table, 3, "Search");
+ init_srt_table_row(&ldap->ldap_srt_table, 4, "<unknown>");
+ init_srt_table_row(&ldap->ldap_srt_table, 5, "<unknown>");
+ init_srt_table_row(&ldap->ldap_srt_table, 6, "Modify");
+ init_srt_table_row(&ldap->ldap_srt_table, 7, "<unknown>");
+ init_srt_table_row(&ldap->ldap_srt_table, 8, "Add");
+ init_srt_table_row(&ldap->ldap_srt_table, 9, "<unknown>");
+ init_srt_table_row(&ldap->ldap_srt_table, 10, "Delete");
+ init_srt_table_row(&ldap->ldap_srt_table, 11, "<unknown>");
+ init_srt_table_row(&ldap->ldap_srt_table, 12, "Modrdn");
+ init_srt_table_row(&ldap->ldap_srt_table, 13, "<unknown>");
+ init_srt_table_row(&ldap->ldap_srt_table, 14, "Compare");
+ init_srt_table_row(&ldap->ldap_srt_table, 15, "<unknown>");
+ init_srt_table_row(&ldap->ldap_srt_table, 16, "<unknown>");
+ init_srt_table_row(&ldap->ldap_srt_table, 17, "<unknown>");
+ init_srt_table_row(&ldap->ldap_srt_table, 18, "<unknown>");
+ init_srt_table_row(&ldap->ldap_srt_table, 19, "<unknown>");
+ init_srt_table_row(&ldap->ldap_srt_table, 20, "<unknown>");
+ init_srt_table_row(&ldap->ldap_srt_table, 21, "<unknown>");
+ init_srt_table_row(&ldap->ldap_srt_table, 22, "<unknown>");
+ init_srt_table_row(&ldap->ldap_srt_table, 23, "Extended");
+
+
+ error_string=register_tap_listener("ldap", ldap, filter, 0, ldapstat_reset, ldapstat_packet, ldapstat_draw);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(ldap);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(ldap->win, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(ldap->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(ldap->win, "destroy", G_CALLBACK(win_destroy_cb), ldap);
+
+ gtk_widget_show_all(ldap->win);
+ window_present(ldap->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(ldap->win));
+}
+
+static tap_param ldap_stat_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg ldap_stat_dlg = {
+ "LDAP Service Response Time Statistics",
+ "ldap,srt",
+ gtk_ldapstat_init,
+ -1,
+ G_N_ELEMENTS(ldap_stat_params),
+ ldap_stat_params
+};
+
+void
+register_tap_listener_gtkldapstat(void)
+{
+ register_dfilter_stat(&ldap_stat_dlg, "LDAP",
+ REGISTER_STAT_GROUP_RESPONSE_TIME);
+}
+void ldap_srt_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &ldap_stat_dlg);
+}
+
diff --git a/ui/gtk/libui.vcproj b/ui/gtk/libui.vcproj
new file mode 100644
index 0000000000..c26e3fb2f5
--- /dev/null
+++ b/ui/gtk/libui.vcproj
@@ -0,0 +1,1095 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9,00"
+ Name="libui"
+ ProjectGUID="{57231C4A-CF70-4CC6-8AED-4C1D6ABC3E94}"
+ Keyword="MakeFileProj"
+ TargetFrameworkVersion="131072"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine=""
+ ReBuildCommandLine=""
+ CleanCommandLine=""
+ Output="libui.exe"
+ PreprocessorDefinitions="WIN32;_DEBUG;HAVE_CONFIG_H"
+ IncludeSearchPath=""
+ ForcedIncludes=""
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="0"
+ >
+ <Tool
+ Name="VCNMakeTool"
+ BuildCommandLine=""
+ ReBuildCommandLine=""
+ CleanCommandLine=""
+ Output="libui.exe"
+ PreprocessorDefinitions="WIN32;NDEBUG;HAVE_LIBPCAP;HAVE_LIBPORTAUDIO"
+ IncludeSearchPath=""
+ ForcedIncludes=""
+ AssemblySearchPath=""
+ ForcedUsingAssemblies=""
+ CompileAsManaged=""
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\about_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\airpcap_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\airpcap_gui_utils.c"
+ >
+ </File>
+ <File
+ RelativePath=".\capture_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\capture_file_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\capture_if_details_dlg_win32.c"
+ >
+ </File>
+ <File
+ RelativePath=".\capture_if_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\capture_info_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\cfilter_combo_utils.c"
+ >
+ </File>
+ <File
+ RelativePath=".\color_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\color_edit_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\color_utils.c"
+ >
+ </File>
+ <File
+ RelativePath=".\conversations_table.c"
+ >
+ </File>
+ <File
+ RelativePath=".\decode_as_ber.c"
+ >
+ </File>
+ <File
+ RelativePath=".\decode_as_dcerpc.c"
+ >
+ </File>
+ <File
+ RelativePath=".\decode_as_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\dfilter_expr_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\dlg_utils.c"
+ >
+ </File>
+ <File
+ RelativePath=".\drag_and_drop.c"
+ >
+ </File>
+ <File
+ RelativePath=".\expert_comp_table.c"
+ >
+ </File>
+ <File
+ RelativePath=".\export_object.c"
+ >
+ </File>
+ <File
+ RelativePath=".\export_object_http.c"
+ >
+ </File>
+ <File
+ RelativePath=".\file_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\file_dlg_win32.c"
+ >
+ </File>
+ <File
+ RelativePath=".\fileset_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\filter_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\find_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\firewall_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\follow_ssl.c"
+ >
+ </File>
+ <File
+ RelativePath=".\follow_stream.c"
+ >
+ </File>
+ <File
+ RelativePath=".\follow_tcp.c"
+ >
+ </File>
+ <File
+ RelativePath=".\follow_udp.c"
+ >
+ </File>
+ <File
+ RelativePath=".\font_utils.c"
+ >
+ </File>
+ <File
+ RelativePath=".\goto_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\graph_analysis.c"
+ >
+ </File>
+ <File
+ RelativePath=".\gui_stat_util.c"
+ >
+ </File>
+ <File
+ RelativePath=".\gui_utils.c"
+ >
+ </File>
+ <File
+ RelativePath=".\help_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\hostlist_table.c"
+ >
+ </File>
+ <File
+ RelativePath=".\macros_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\main.c"
+ >
+ </File>
+ <File
+ RelativePath=".\main_airpcap_toolbar.c"
+ >
+ </File>
+ <File
+ RelativePath=".\main_filter_toolbar.c"
+ >
+ </File>
+ <File
+ RelativePath=".\main_proto_draw.c"
+ >
+ </File>
+ <File
+ RelativePath=".\main_statusbar.c"
+ >
+ </File>
+ <File
+ RelativePath=".\main_toolbar.c"
+ >
+ </File>
+ <File
+ RelativePath=".\main_welcome.c"
+ >
+ </File>
+ <File
+ RelativePath=".\mcast_stream.c"
+ >
+ </File>
+ <File
+ RelativePath=".\packet_history.c"
+ >
+ </File>
+ <File
+ RelativePath=".\packet_win.c"
+ >
+ </File>
+ <File
+ RelativePath=".\pixmap_save.c"
+ >
+ </File>
+ <File
+ RelativePath=".\plugins_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\prefs_capture.c"
+ >
+ </File>
+ <File
+ RelativePath=".\prefs_column.c"
+ >
+ </File>
+ <File
+ RelativePath=".\prefs_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\prefs_gui.c"
+ >
+ </File>
+ <File
+ RelativePath=".\prefs_layout.c"
+ >
+ </File>
+ <File
+ RelativePath=".\prefs_nameres.c"
+ >
+ </File>
+ <File
+ RelativePath=".\prefs_print.c"
+ >
+ </File>
+ <File
+ RelativePath=".\prefs_rtp_player.c"
+ >
+ </File>
+ <File
+ RelativePath=".\prefs_stream.c"
+ >
+ </File>
+ <File
+ RelativePath=".\print_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\print_win32.c"
+ >
+ </File>
+ <File
+ RelativePath=".\profile_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\progress_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\proto_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\proto_hier_stats_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\range_utils.c"
+ >
+ </File>
+ <File
+ RelativePath=".\recent.c"
+ >
+ </File>
+ <File
+ RelativePath=".\rtp_player.c"
+ >
+ </File>
+ <File
+ RelativePath=".\rtp_stream.c"
+ >
+ </File>
+ <File
+ RelativePath=".\sctp_byte_graph_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\sctp_error_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\sctp_graph_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\sctp_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\service_response_time_table.c"
+ >
+ </File>
+ <File
+ RelativePath=".\simple_dialog.c"
+ >
+ </File>
+ <File
+ RelativePath=".\stock_icons.c"
+ >
+ </File>
+ <File
+ RelativePath=".\summary_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\supported_protos_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\tap_dfilter_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\text_page_utils.c"
+ >
+ </File>
+ <File
+ RelativePath=".\u3.c"
+ >
+ </File>
+ <File
+ RelativePath=".\uat_gui.c"
+ >
+ </File>
+ <File
+ RelativePath=".\voip_calls.c"
+ >
+ </File>
+ <File
+ RelativePath=".\webbrowser.c"
+ >
+ </File>
+ <File
+ RelativePath=".\wireshark-tap-register.c"
+ >
+ </File>
+ <Filter
+ Name="Taps"
+ >
+ <File
+ RelativePath=".\afp_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\ansi_a_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\ansi_map_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\bootp_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\camel_counter.c"
+ >
+ </File>
+ <File
+ RelativePath=".\camel_srt.c"
+ >
+ </File>
+ <File
+ RelativePath=".\conversations_eth.c"
+ >
+ </File>
+ <File
+ RelativePath=".\conversations_fc.c"
+ >
+ </File>
+ <File
+ RelativePath=".\conversations_fddi.c"
+ >
+ </File>
+ <File
+ RelativePath=".\conversations_ip.c"
+ >
+ </File>
+ <File
+ RelativePath=".\conversations_ipx.c"
+ >
+ </File>
+ <File
+ RelativePath=".\conversations_jxta.c"
+ >
+ </File>
+ <File
+ RelativePath=".\conversations_ncp.c"
+ >
+ </File>
+ <File
+ RelativePath=".\conversations_rsvp.c"
+ >
+ </File>
+ <File
+ RelativePath=".\conversations_sctp.c"
+ >
+ </File>
+ <File
+ RelativePath=".\conversations_tcpip.c"
+ >
+ </File>
+ <File
+ RelativePath=".\conversations_tr.c"
+ >
+ </File>
+ <File
+ RelativePath=".\conversations_udpip.c"
+ >
+ </File>
+ <File
+ RelativePath=".\conversations_usb.c"
+ >
+ </File>
+ <File
+ RelativePath=".\conversations_wlan.c"
+ >
+ </File>
+ <File
+ RelativePath=".\dcerpc_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\expert_comp_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\expert_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\fc_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\flow_graph.c"
+ >
+ </File>
+ <File
+ RelativePath=".\funnel_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\gsm_a_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\gsm_map_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\gsm_map_summary.c"
+ >
+ </File>
+ <File
+ RelativePath=".\h225_counter.c"
+ >
+ </File>
+ <File
+ RelativePath=".\h225_ras_srt.c"
+ >
+ </File>
+ <File
+ RelativePath=".\hostlist_eth.c"
+ >
+ </File>
+ <File
+ RelativePath=".\hostlist_fc.c"
+ >
+ </File>
+ <File
+ RelativePath=".\hostlist_fddi.c"
+ >
+ </File>
+ <File
+ RelativePath=".\hostlist_ip.c"
+ >
+ </File>
+ <File
+ RelativePath=".\hostlist_ipx.c"
+ >
+ </File>
+ <File
+ RelativePath=".\hostlist_jxta.c"
+ >
+ </File>
+ <File
+ RelativePath=".\hostlist_ncp.c"
+ >
+ </File>
+ <File
+ RelativePath=".\hostlist_rsvp.c"
+ >
+ </File>
+ <File
+ RelativePath=".\hostlist_sctp.c"
+ >
+ </File>
+ <File
+ RelativePath=".\hostlist_tcpip.c"
+ >
+ </File>
+ <File
+ RelativePath=".\hostlist_tr.c"
+ >
+ </File>
+ <File
+ RelativePath=".\hostlist_udpip.c"
+ >
+ </File>
+ <File
+ RelativePath=".\hostlist_usb.c"
+ >
+ </File>
+ <File
+ RelativePath=".\hostlist_wlan.c"
+ >
+ </File>
+ <File
+ RelativePath=".\io_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\ldap_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\mcast_stream_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\mgcp_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\mtp3_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\mtp3_summary.c"
+ >
+ </File>
+ <File
+ RelativePath=".\ncp_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\radius_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\rpc_progs.c"
+ >
+ </File>
+ <File
+ RelativePath=".\rpc_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\rtp_analysis.c"
+ >
+ </File>
+ <File
+ RelativePath=".\rtp_stream_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\scsi_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\sctp_assoc_analyse.c"
+ >
+ </File>
+ <File
+ RelativePath=".\sctp_chunk_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\sctp_chunk_stat_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\sctp_stat_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\sip_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\smb2_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\smb_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\stats_tree_stat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\t38_analysis.c"
+ >
+ </File>
+ <File
+ RelativePath=".\tcp_graph.c"
+ >
+ </File>
+ <File
+ RelativePath=".\voip_calls_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\wlan_stat_dlg.c"
+ >
+ </File>
+ <File
+ RelativePath=".\wsp_stat.c"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\about_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\airpcap_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\airpcap_gui_utils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\capture_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\capture_file_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\capture_if_details_dlg_win32.h"
+ >
+ </File>
+ <File
+ RelativePath=".\cfilter_combo_utils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\color_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\color_edit_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\color_utils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\conversations_table.h"
+ >
+ </File>
+ <File
+ RelativePath=".\decode_as_ber.h"
+ >
+ </File>
+ <File
+ RelativePath=".\decode_as_dcerpc.h"
+ >
+ </File>
+ <File
+ RelativePath=".\decode_as_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\dfilter_expr_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\dlg_utils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\drag_and_drop.h"
+ >
+ </File>
+ <File
+ RelativePath=".\expert_comp_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\expert_comp_table.h"
+ >
+ </File>
+ <File
+ RelativePath=".\export_object.h"
+ >
+ </File>
+ <File
+ RelativePath=".\file_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\file_dlg_win32.h"
+ >
+ </File>
+ <File
+ RelativePath=".\fileset_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\filter_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\find_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\firewall_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\follow_ssl.h"
+ >
+ </File>
+ <File
+ RelativePath=".\follow_stream.h"
+ >
+ </File>
+ <File
+ RelativePath=".\follow_tcp.h"
+ >
+ </File>
+ <File
+ RelativePath=".\follow_udp.h"
+ >
+ </File>
+ <File
+ RelativePath=".\font_utils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\goto_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\graph_analysis.h"
+ >
+ </File>
+ <File
+ RelativePath=".\gsm_map_stat.h"
+ >
+ </File>
+ <File
+ RelativePath=".\gtkglobals.h"
+ >
+ </File>
+ <File
+ RelativePath=".\gui_stat_menu.h"
+ >
+ </File>
+ <File
+ RelativePath=".\gui_stat_util.h"
+ >
+ </File>
+ <File
+ RelativePath=".\gui_utils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\help_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\hostlist_table.h"
+ >
+ </File>
+ <File
+ RelativePath=".\keys.h"
+ >
+ </File>
+ <File
+ RelativePath=".\macros_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\main.h"
+ >
+ </File>
+ <File
+ RelativePath=".\main_airpcap_toolbar.h"
+ >
+ </File>
+ <File
+ RelativePath=".\main_filter_toolbar.h"
+ >
+ </File>
+ <File
+ RelativePath=".\main_proto_draw.h"
+ >
+ </File>
+ <File
+ RelativePath=".\main_statusbar.h"
+ >
+ </File>
+ <File
+ RelativePath=".\main_toolbar.h"
+ >
+ </File>
+ <File
+ RelativePath=".\main_welcome.h"
+ >
+ </File>
+ <File
+ RelativePath=".\mcast_stream.h"
+ >
+ </File>
+ <File
+ RelativePath=".\mcast_stream_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\menus.h"
+ >
+ </File>
+ <File
+ RelativePath=".\mtp3_stat.h"
+ >
+ </File>
+ <File
+ RelativePath=".\packet_history.h"
+ >
+ </File>
+ <File
+ RelativePath=".\packet_win.h"
+ >
+ </File>
+ <File
+ RelativePath=".\pixmap_save.h"
+ >
+ </File>
+ <File
+ RelativePath=".\plugins_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\prefs_capture.h"
+ >
+ </File>
+ <File
+ RelativePath=".\prefs_column.h"
+ >
+ </File>
+ <File
+ RelativePath=".\prefs_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\prefs_gui.h"
+ >
+ </File>
+ <File
+ RelativePath=".\prefs_layout.h"
+ >
+ </File>
+ <File
+ RelativePath=".\prefs_nameres.h"
+ >
+ </File>
+ <File
+ RelativePath=".\prefs_print.h"
+ >
+ </File>
+ <File
+ RelativePath=".\prefs_rtp_player.h"
+ >
+ </File>
+ <File
+ RelativePath=".\prefs_stream.h"
+ >
+ </File>
+ <File
+ RelativePath=".\print_win32.h"
+ >
+ </File>
+ <File
+ RelativePath=".\profile_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\proto_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\proto_hier_stats_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\range_utils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\recent.h"
+ >
+ </File>
+ <File
+ RelativePath=".\rtp_analysis.h"
+ >
+ </File>
+ <File
+ RelativePath=".\rtp_player.h"
+ >
+ </File>
+ <File
+ RelativePath=".\rtp_stream.h"
+ >
+ </File>
+ <File
+ RelativePath=".\rtp_stream_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sat.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sctp_stat.h"
+ >
+ </File>
+ <File
+ RelativePath=".\service_response_time_table.h"
+ >
+ </File>
+ <File
+ RelativePath=".\stock_icons.h"
+ >
+ </File>
+ <File
+ RelativePath=".\summary_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\supported_protos_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\tap_dfilter_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\text_page_utils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\u3.h"
+ >
+ </File>
+ <File
+ RelativePath=".\uat_gui.h"
+ >
+ </File>
+ <File
+ RelativePath=".\voip_calls.h"
+ >
+ </File>
+ <File
+ RelativePath=".\voip_calls_dlg.h"
+ >
+ </File>
+ <File
+ RelativePath=".\webbrowser.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ <File
+ RelativePath=".\doxygen.cfg.in"
+ >
+ </File>
+ <File
+ RelativePath=".\Makefile.am"
+ >
+ </File>
+ <File
+ RelativePath=".\Makefile.common"
+ >
+ </File>
+ <File
+ RelativePath=".\Makefile.nmake"
+ >
+ </File>
+ <File
+ RelativePath=".\STATUS.gtk2"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/ui/gtk/mac_lte_stat_dlg.c b/ui/gtk/mac_lte_stat_dlg.c
new file mode 100644
index 0000000000..0989132eac
--- /dev/null
+++ b/ui/gtk/mac_lte_stat_dlg.c
@@ -0,0 +1,1346 @@
+/* mac_lte_stat_dlg.c
+ * Copyright 2009 Martin Mathieson
+ * (originally based upon wlan_stat_dlg.c)
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+/* TODO:
+ - CSV export?
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "ui/gtk/gtkglobals.h"
+
+#include <epan/packet.h>
+#include <epan/packet_info.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-mac-lte.h>
+
+#include "../simple_dialog.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/main.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+/**********************************************/
+/* Table column identifiers and title strings */
+
+enum {
+ RNTI_COLUMN,
+ RNTI_TYPE_COLUMN,
+ UEID_COLUMN,
+ UL_FRAMES_COLUMN,
+ UL_BYTES_COLUMN,
+ UL_BW_COLUMN,
+ UL_PADDING_PERCENT_COLUMN,
+ UL_RETX_FRAMES_COLUMN,
+ DL_FRAMES_COLUMN,
+ DL_BYTES_COLUMN,
+ DL_BW_COLUMN,
+ DL_CRC_FAILED_COLUMN,
+ DL_CRC_HIGH_CODE_RATE_COLUMN,
+ DL_CRC_PDSCH_LOST_COLUMN,
+ DL_CRC_DUPLICATE_NONZERO_RV,
+ DL_RETX_FRAMES_COLUMN,
+ TABLE_COLUMN,
+ NUM_UE_COLUMNS
+};
+
+enum {
+ CCCH_COLUMN=1,
+ LCID1_COLUMN,
+ LCID2_COLUMN,
+ LCID3_COLUMN,
+ LCID4_COLUMN,
+ LCID5_COLUMN,
+ LCID6_COLUMN,
+ LCID7_COLUMN,
+ LCID8_COLUMN,
+ LCID9_COLUMN,
+ LCID10_COLUMN,
+ PREDEFINED_COLUMN,
+ NUM_CHANNEL_COLUMNS
+};
+
+static const gchar *ue_titles[] = { "RNTI", "Type", "UEId",
+ "UL Frames", "UL Bytes", "UL MBit/sec", "UL Padding %", "UL ReTX",
+ "DL Frames", "DL Bytes", "DL MBit/sec", "DL CRC Failed", "DL CRC High Code Rate", "DL CRC PDSCH Lost", "DL CRC Dup NonZero RV", "DL ReTX"};
+
+static const gchar *channel_titles[] = { "CCCH",
+ "LCID 1", "LCID 2", "LCID 3", "LCID 4", "LCID 5",
+ "LCID 6", "LCID 7", "LCID 8", "LCID 9", "LCID 10",
+ "Predefined"};
+
+
+/* Stats for one UE */
+typedef struct mac_lte_row_data {
+ /* Key for matching this row */
+ guint16 rnti;
+ guint8 rnti_type;
+ guint16 ueid;
+
+ gboolean is_predefined_data;
+
+ guint32 UL_frames;
+ guint32 UL_raw_bytes; /* all bytes */
+ guint32 UL_total_bytes; /* payload */
+ nstime_t UL_time_start;
+ nstime_t UL_time_stop;
+ guint32 UL_padding_bytes;
+ guint32 UL_CRC_errors;
+ guint32 UL_retx_frames;
+
+ guint32 DL_frames;
+ guint32 DL_total_bytes;
+ nstime_t DL_time_start;
+ nstime_t DL_time_stop;
+ guint32 DL_CRC_failures;
+ guint32 DL_CRC_high_code_rate;
+ guint32 DL_CRC_PDSCH_lost;
+ guint32 DL_CRC_Duplicate_Nonzero_rv;
+ guint32 DL_retx_frames;
+
+ guint32 UL_bytes_for_lcid[11];
+ guint32 UL_sdus_for_lcid[11];
+ guint32 DL_bytes_for_lcid[11];
+ guint32 DL_sdus_for_lcid[11];
+} mac_lte_row_data;
+
+
+/* One row/UE in the UE table */
+typedef struct mac_lte_ep {
+ struct mac_lte_ep* next;
+ struct mac_lte_row_data stats;
+ GtkTreeIter iter;
+ gboolean iter_valid;
+} mac_lte_ep_t;
+
+
+/* Common channel stats */
+typedef struct mac_lte_common_stats {
+ guint32 all_frames;
+ guint32 bch_frames;
+ guint32 bch_bytes;
+ guint32 pch_frames;
+ guint32 pch_bytes;
+ guint32 rar_frames;
+ guint32 rar_entries;
+
+ guint16 max_ul_ues_in_tti;
+ guint16 max_dl_ues_in_tti;
+} mac_lte_common_stats;
+
+
+static const char * selected_ue_row_names[] = {"UL SDUs", "UL Bytes", "DL SDUs", "DL Bytes"};
+
+
+/* Used to keep track of whole MAC LTE statistics window */
+typedef struct mac_lte_stat_t {
+ /* Stats window itself */
+ GtkWidget *mac_lte_stat_dlg_w;
+
+ char *filter;
+
+ /* Labels */
+ GtkWidget *mac_lte_stat_ues_lb;
+ GtkWidget *ul_filter_bt;
+ GtkWidget *dl_filter_bt;
+ GtkWidget *uldl_filter_bt;
+
+ GtkWidget *show_dct_errors_cb;
+ GtkWidget *dct_error_substring_lb;
+ GtkWidget *dct_error_substring_te;
+
+ GtkWidget *ul_max_ues_per_tti;
+ GtkWidget *dl_max_ues_per_tti;
+
+ /* Common stats */
+ mac_lte_common_stats common_stats;
+ GtkWidget *common_bch_frames;
+ GtkWidget *common_bch_bytes;
+ GtkWidget *common_pch_frames;
+ GtkWidget *common_pch_bytes;
+ GtkWidget *common_rar_frames;
+ GtkWidget *common_rar_entries;
+
+ /* Keep track of unique rntis & ueids */
+ guint8 used_ueids[65535];
+ guint8 used_rntis[65535];
+ guint16 number_of_ueids;
+ guint16 number_of_rntis;
+
+ guint16 selected_rnti;
+ guint16 selected_ueid;
+
+ /* Labels in selected UE 'table' */
+ GtkWidget *selected_ue_column_entry[NUM_CHANNEL_COLUMNS][5];
+
+ GtkTreeView *ue_table;
+ mac_lte_ep_t *ep_list;
+} mac_lte_stat_t;
+
+
+/* Reset the statistics window */
+static void
+mac_lte_stat_reset(void *phs)
+{
+ mac_lte_stat_t* mac_lte_stat = (mac_lte_stat_t *)phs;
+ mac_lte_ep_t* list = mac_lte_stat->ep_list;
+ gchar title[256];
+ GtkListStore *store;
+ gint i, n;
+
+ /* Set the title */
+ if (mac_lte_stat->mac_lte_stat_dlg_w != NULL) {
+ g_snprintf(title, sizeof(title), "Wireshark: LTE MAC Traffic Statistics: %s (filter=\"%s\")",
+ cf_get_display_name(&cfile),
+ strlen(mac_lte_stat->filter) ? mac_lte_stat->filter : "none");
+ gtk_window_set_title(GTK_WINDOW(mac_lte_stat->mac_lte_stat_dlg_w), title);
+ }
+
+ g_snprintf(title, sizeof(title), "UL/DL-SCH data (0 entries)");
+ gtk_frame_set_label(GTK_FRAME(mac_lte_stat->mac_lte_stat_ues_lb), title);
+
+ /* Reset counts of unique ueids & rntis */
+ memset(mac_lte_stat->used_ueids, 0, 65535);
+ mac_lte_stat->number_of_ueids = 0;
+ memset(mac_lte_stat->used_rntis, 0, 65535);
+ mac_lte_stat->number_of_rntis = 0;
+
+ /* Zero common stats */
+ memset(&(mac_lte_stat->common_stats), 0, sizeof(mac_lte_common_stats));
+
+ /* Remove all entries from the UE list */
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(mac_lte_stat->ue_table));
+ gtk_list_store_clear(store);
+
+ if (!list) {
+ return;
+ }
+
+ mac_lte_stat->ep_list = NULL;
+
+ /* Set all of the channel counters to 0 */
+ for (n=1; n <=4; n++) {
+ for (i=CCCH_COLUMN; i < NUM_CHANNEL_COLUMNS; i++) {
+ gtk_label_set_text(GTK_LABEL(mac_lte_stat->selected_ue_column_entry[i][n]), "0");
+ }
+ }
+}
+
+
+/* Allocate a mac_lte_ep_t struct to store info for new UE */
+static mac_lte_ep_t* alloc_mac_lte_ep(struct mac_lte_tap_info *si, packet_info *pinfo _U_)
+{
+ mac_lte_ep_t* ep;
+ int n;
+
+ if (!si) {
+ return NULL;
+ }
+
+ if (!(ep = g_malloc(sizeof(mac_lte_ep_t)))) {
+ return NULL;
+ }
+
+ /* Copy SI data into ep->stats */
+ ep->stats.rnti = si->rnti;
+ ep->stats.rnti_type = si->rntiType;
+ ep->stats.ueid = si->ueid;
+
+ /* Counts for new UE are all 0 */
+ ep->stats.UL_frames = 0;
+ ep->stats.DL_frames = 0;
+ ep->stats.UL_total_bytes = 0;
+ ep->stats.UL_raw_bytes = 0;
+ ep->stats.UL_padding_bytes = 0;
+ ep->stats.DL_total_bytes = 0;
+ ep->stats.UL_CRC_errors = 0;
+ ep->stats.DL_CRC_failures = 0;
+ ep->stats.DL_CRC_high_code_rate = 0;
+ ep->stats.DL_CRC_PDSCH_lost = 0;
+ ep->stats.DL_CRC_Duplicate_Nonzero_rv = 0;
+ ep->stats.UL_retx_frames = 0;
+ ep->stats.DL_retx_frames = 0;
+
+ for (n=0; n < 11; n++) {
+ ep->stats.UL_sdus_for_lcid[n] = 0;
+ ep->stats.UL_bytes_for_lcid[n] = 0;
+ }
+ ep->stats.DL_total_bytes = 0;
+ for (n=0; n < 11; n++) {
+ ep->stats.DL_sdus_for_lcid[n] = 0;
+ ep->stats.DL_bytes_for_lcid[n] = 0;
+ }
+
+ ep->next = NULL;
+
+ ep->iter_valid = FALSE;
+
+ return ep;
+}
+
+
+/* Update counts of unique rntis & ueids */
+static void update_ueid_rnti_counts(guint16 rnti, guint16 ueid, mac_lte_stat_t *hs)
+{
+ if (!hs->used_ueids[ueid]) {
+ hs->used_ueids[ueid] = TRUE;
+ hs->number_of_ueids++;
+ }
+ if (!hs->used_rntis[rnti]) {
+ hs->used_rntis[rnti] = TRUE;
+ hs->number_of_rntis++;
+ }
+}
+
+
+/* Process stat struct for a MAC LTE frame */
+static int
+mac_lte_stat_packet(void *phs, packet_info *pinfo, epan_dissect_t *edt _U_,
+ const void *phi)
+{
+ int n;
+
+ /* Get reference to stat window instance */
+ mac_lte_stat_t *hs = (mac_lte_stat_t *)phs;
+ mac_lte_ep_t *tmp = NULL, *te = NULL;
+
+ /* Cast tap info struct */
+ struct mac_lte_tap_info *si = (struct mac_lte_tap_info *)phi;
+
+ if (!hs) {
+ return 0;
+ }
+
+ hs->common_stats.all_frames++;
+
+ /* For common channels, just update global counters */
+ switch (si->rntiType) {
+ case P_RNTI:
+ hs->common_stats.pch_frames++;
+ hs->common_stats.pch_bytes += si->single_number_of_bytes;
+ return 1;
+ case SI_RNTI:
+ case NO_RNTI:
+ hs->common_stats.bch_frames++;
+ hs->common_stats.bch_bytes += si->single_number_of_bytes;
+ return 1;
+ case RA_RNTI:
+ hs->common_stats.rar_frames++;
+ hs->common_stats.rar_entries += si->number_of_rars;
+ return 1;
+ case C_RNTI:
+ case SPS_RNTI:
+ /* Drop through for per-UE update */
+ break;
+
+ default:
+ /* Error */
+ return 0;
+ }
+
+ /* Check max UEs/tti counter */
+ switch (si->direction) {
+ case DIRECTION_UPLINK:
+ hs->common_stats.max_ul_ues_in_tti =
+ MAX(hs->common_stats.max_ul_ues_in_tti, si->ueInTTI);
+ break;
+ case DIRECTION_DOWNLINK:
+ hs->common_stats.max_dl_ues_in_tti =
+ MAX(hs->common_stats.max_dl_ues_in_tti, si->ueInTTI);
+ break;
+ }
+
+ /* For per-UE data, must create a new row if none already existing */
+ if (!hs->ep_list) {
+ /* Allocate new list */
+ hs->ep_list = alloc_mac_lte_ep(si, pinfo);
+ /* Make it the first/only entry */
+ te = hs->ep_list;
+
+ /* Update counts of unique ueids & rntis */
+ update_ueid_rnti_counts(si->rnti, si->ueid, hs);
+ } else {
+ /* Look among existing rows for this RNTI */
+ for (tmp = hs->ep_list;(tmp != NULL); tmp = tmp->next) {
+ /* Match only by RNTI and UEId together */
+ if ((tmp->stats.rnti == si->rnti) &&
+ (tmp->stats.ueid == si->ueid)){
+ te = tmp;
+ break;
+ }
+ }
+
+ /* Not found among existing, so create a new one anyway */
+ if (te == NULL) {
+ if ((te = alloc_mac_lte_ep(si, pinfo))) {
+ /* Add new item to end of list */
+ mac_lte_ep_t *p = hs->ep_list;
+ while (p->next) {
+ p = p->next;
+ }
+ p->next = te;
+ te->next = NULL;
+
+ /* Update counts of unique ueids & rntis */
+ update_ueid_rnti_counts(si->rnti, si->ueid, hs);
+ }
+ }
+ }
+
+ /* Really should have a row pointer by now */
+ if (!te) {
+ return 0;
+ }
+
+ /* Update entry with details from si */
+ te->stats.rnti = si->rnti;
+ te->stats.is_predefined_data = si->isPredefinedData;
+
+ /* Uplink */
+ if (si->direction == DIRECTION_UPLINK) {
+ if (si->isPHYRetx) {
+ te->stats.UL_retx_frames++;
+ return 1;
+ }
+
+ if (si->crcStatusValid && (si->crcStatus != crc_success)) {
+ te->stats.UL_CRC_errors++;
+ return 1;
+ }
+
+ /* Update time range */
+ if (te->stats.UL_frames == 0) {
+ te->stats.UL_time_start = si->time;
+ }
+ te->stats.UL_time_stop = si->time;
+
+ te->stats.UL_frames++;
+
+ te->stats.UL_raw_bytes += si->raw_length;
+ te->stats.UL_padding_bytes += si->padding_bytes;
+
+ if (si->isPredefinedData) {
+ te->stats.UL_total_bytes += si->single_number_of_bytes;
+ }
+ else {
+ for (n=0; n < 11; n++) {
+ if (si->bytes_for_lcid[n]) {
+ te->stats.UL_sdus_for_lcid[n] += si->sdus_for_lcid[n];
+ }
+ te->stats.UL_bytes_for_lcid[n] += si->bytes_for_lcid[n];
+ te->stats.UL_total_bytes += si->bytes_for_lcid[n];
+ }
+ }
+ }
+
+ /* Downlink */
+ else {
+ if (si->isPHYRetx) {
+ te->stats.DL_retx_frames++;
+ return 1;
+ }
+
+ if (si->crcStatusValid && (si->crcStatus != crc_success)) {
+ switch (si->crcStatus) {
+ case crc_fail:
+ te->stats.DL_CRC_failures++;
+ break;
+ case crc_high_code_rate:
+ te->stats.DL_CRC_high_code_rate++;
+ break;
+ case crc_pdsch_lost:
+ te->stats.DL_CRC_PDSCH_lost++;
+ break;
+ case crc_duplicate_nonzero_rv:
+ te->stats.DL_CRC_Duplicate_Nonzero_rv++;
+ break;
+
+ default:
+ /* Something went wrong! */
+ break;
+ }
+ return 1;
+ }
+
+ /* Update time range */
+ if (te->stats.DL_frames == 0) {
+ te->stats.DL_time_start = si->time;
+ }
+ te->stats.DL_time_stop = si->time;
+
+ te->stats.DL_frames++;
+
+ if (si->isPredefinedData) {
+ te->stats.DL_total_bytes += si->single_number_of_bytes;
+ }
+ else {
+ for (n=0; n < 11; n++) {
+ if (si->bytes_for_lcid[n]) {
+ te->stats.DL_sdus_for_lcid[n] += si->sdus_for_lcid[n];
+ }
+ te->stats.DL_bytes_for_lcid[n] += si->bytes_for_lcid[n];
+ te->stats.DL_total_bytes += si->bytes_for_lcid[n];
+ }
+ }
+ }
+
+ return 1;
+}
+
+
+/* Draw the UE details table according to the current UE selection */
+static void
+mac_lte_ue_details(mac_lte_ep_t *mac_stat_ep, mac_lte_stat_t *hs)
+{
+ int n;
+ gchar buff[32];
+ guint8 show_dct_errors;
+
+ /**********************************/
+ /* Set data one row at a time */
+
+ /* UL SDUs */
+ for (n=0; n < PREDEFINED_COLUMN-1; n++) {
+ g_snprintf(buff, sizeof(buff), "%u",
+ (mac_stat_ep != NULL) ? mac_stat_ep->stats.UL_sdus_for_lcid[n] : 0);
+ gtk_label_set_text(GTK_LABEL(hs->selected_ue_column_entry[n+1][1]), buff);
+ }
+
+ /* Predefined */
+ if (mac_stat_ep != NULL) {
+ g_snprintf(buff, sizeof(buff), "%u",
+ mac_stat_ep->stats.is_predefined_data ? mac_stat_ep->stats.UL_frames : 0);
+ }
+ else {
+ g_snprintf(buff, sizeof(buff), "%u", 0);
+ }
+ gtk_label_set_text(GTK_LABEL(hs->selected_ue_column_entry[PREDEFINED_COLUMN][1]), buff);
+
+
+ /* UL Bytes */
+ for (n=0; n < PREDEFINED_COLUMN-1; n++) {
+ g_snprintf(buff, sizeof(buff), "%u",
+ (mac_stat_ep != NULL) ? mac_stat_ep->stats.UL_bytes_for_lcid[n] : 0);
+ gtk_label_set_text(GTK_LABEL(hs->selected_ue_column_entry[n+1][2]), buff);
+ }
+
+ /* Predefined */
+ if (mac_stat_ep != NULL) {
+ g_snprintf(buff, sizeof(buff), "%u",
+ mac_stat_ep->stats.is_predefined_data ? mac_stat_ep->stats.UL_total_bytes : 0);
+ }
+ else {
+ g_snprintf(buff, sizeof(buff), "%u", 0);
+ }
+ gtk_label_set_text(GTK_LABEL(hs->selected_ue_column_entry[PREDEFINED_COLUMN][2]), buff);
+
+
+ /* DL SDUs */
+ for (n=0; n < PREDEFINED_COLUMN-1; n++) {
+ g_snprintf(buff, sizeof(buff), "%u",
+ (mac_stat_ep != NULL) ? mac_stat_ep->stats.DL_sdus_for_lcid[n] : 0);
+ gtk_label_set_text(GTK_LABEL(hs->selected_ue_column_entry[n+1][3]), buff);
+ }
+ /* Predefined */
+ if (mac_stat_ep != NULL) {
+ g_snprintf(buff, sizeof(buff), "%u",
+ mac_stat_ep->stats.is_predefined_data ? mac_stat_ep->stats.DL_frames : 0);
+ }
+ else {
+ g_snprintf(buff, sizeof(buff), "%u", 0);
+ }
+ gtk_label_set_text(GTK_LABEL(hs->selected_ue_column_entry[PREDEFINED_COLUMN][3]), buff);
+
+
+ /* DL Bytes */
+ for (n=0; n < PREDEFINED_COLUMN-1; n++) {
+ g_snprintf(buff, sizeof(buff), "%u",
+ (mac_stat_ep != NULL) ? mac_stat_ep->stats.DL_bytes_for_lcid[n] : 0);
+ gtk_label_set_text(GTK_LABEL(hs->selected_ue_column_entry[n+1][4]), buff);
+ }
+ /* Predefined */
+ if (mac_stat_ep != NULL) {
+ g_snprintf(buff, sizeof(buff), "%u",
+ mac_stat_ep->stats.is_predefined_data ? mac_stat_ep->stats.DL_total_bytes : 0);
+ }
+ else {
+ g_snprintf(buff, sizeof(buff), "%u", 0);
+ }
+ gtk_label_set_text(GTK_LABEL(hs->selected_ue_column_entry[PREDEFINED_COLUMN][4]), buff);
+
+ /* Remember selected UE */
+ if (mac_stat_ep) {
+ hs->selected_rnti = mac_stat_ep->stats.rnti;
+ hs->selected_ueid = mac_stat_ep->stats.ueid;
+ }
+
+ /* Enable/disable filter controls */
+ gtk_widget_set_sensitive(hs->ul_filter_bt, mac_stat_ep != NULL);
+ gtk_widget_set_sensitive(hs->dl_filter_bt, mac_stat_ep != NULL);
+ gtk_widget_set_sensitive(hs->uldl_filter_bt, mac_stat_ep != NULL);
+ gtk_widget_set_sensitive(hs->show_dct_errors_cb, mac_stat_ep != NULL);
+
+ /* Enabling substring control only if errors enabled */
+ show_dct_errors = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hs->show_dct_errors_cb));
+ gtk_widget_set_sensitive(hs->dct_error_substring_lb, show_dct_errors && (mac_stat_ep != NULL));
+ gtk_widget_set_sensitive(hs->dct_error_substring_te, show_dct_errors && (mac_stat_ep != NULL));
+}
+
+
+/* Calculate and return a bandwidth figure, in Mbs */
+static float calculate_bw(nstime_t *start_time, nstime_t *stop_time, guint32 bytes)
+{
+ if (memcmp(start_time, stop_time, sizeof(nstime_t)) != 0) {
+ float elapsed_ms = (((float)stop_time->secs - (float)start_time->secs) * 1000) +
+ (((float)stop_time->nsecs - (float)start_time->nsecs) / 1000000);
+ return ((bytes * 8) / elapsed_ms) / 1000;
+ }
+ else {
+ return 0.0;
+ }
+}
+
+
+/* (Re)draw the whole dialog window */
+static void
+mac_lte_stat_draw(void *phs)
+{
+ gchar buff[32];
+ guint16 number_of_ues = 0;
+ gchar title[256];
+
+ /* Look up the statistics window */
+ mac_lte_stat_t *hs = (mac_lte_stat_t *)phs;
+ mac_lte_ep_t* list = hs->ep_list, *tmp = 0;
+
+ GtkListStore *ues_store;
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ /* System data */
+ g_snprintf(buff, sizeof(buff), "Max UL UEs/TTI: %u", hs->common_stats.max_ul_ues_in_tti);
+ gtk_label_set_text(GTK_LABEL(hs->ul_max_ues_per_tti), buff);
+ g_snprintf(buff, sizeof(buff), "Max DL UEs/TTI: %u", hs->common_stats.max_dl_ues_in_tti);
+ gtk_label_set_text(GTK_LABEL(hs->dl_max_ues_per_tti), buff);
+
+ /* Common channel data */
+ g_snprintf(buff, sizeof(buff), "BCH Frames: %u", hs->common_stats.bch_frames);
+ gtk_label_set_text(GTK_LABEL(hs->common_bch_frames), buff);
+ g_snprintf(buff, sizeof(buff), "BCH Bytes: %u", hs->common_stats.bch_bytes);
+ gtk_label_set_text(GTK_LABEL(hs->common_bch_bytes), buff);
+ g_snprintf(buff, sizeof(buff), "PCH Frames: %u", hs->common_stats.pch_frames);
+ gtk_label_set_text(GTK_LABEL(hs->common_pch_frames), buff);
+ g_snprintf(buff, sizeof(buff), "PCH Bytes: %u", hs->common_stats.pch_bytes);
+ gtk_label_set_text(GTK_LABEL(hs->common_pch_bytes), buff);
+ g_snprintf(buff, sizeof(buff), "RAR Frames: %u", hs->common_stats.rar_frames);
+ gtk_label_set_text(GTK_LABEL(hs->common_rar_frames), buff);
+ g_snprintf(buff, sizeof(buff), "RAR Entries: %u", hs->common_stats.rar_entries);
+ gtk_label_set_text(GTK_LABEL(hs->common_rar_entries), buff);
+
+
+ /* Per-UE table entries */
+ ues_store = GTK_LIST_STORE(gtk_tree_view_get_model(hs->ue_table));
+
+ /* Set title that shows how many UEs currently in table */
+ for (tmp = list; (tmp!=NULL); tmp=tmp->next, number_of_ues++);
+ g_snprintf(title, sizeof(title), "UL/DL-SCH data (%u entries - %u unique RNTIs, %u unique UEIds)",
+ number_of_ues, hs->number_of_rntis, hs->number_of_ueids);
+ gtk_frame_set_label(GTK_FRAME(hs->mac_lte_stat_ues_lb), title);
+
+ /* Update title to include number of UEs and frames */
+ g_snprintf(title, sizeof(title), "Wireshark: LTE MAC Traffic Statistics: %s (%u UEs, %u frames) (filter=\"%s\")",
+ cf_get_display_name(&cfile),
+ number_of_ues,
+ hs->common_stats.all_frames,
+ strlen(hs->filter) ? hs->filter : "none");
+ gtk_window_set_title(GTK_WINDOW(hs->mac_lte_stat_dlg_w), title);
+
+
+ for (tmp = list; tmp; tmp=tmp->next) {
+
+ /* Calculate bandwidth */
+ float UL_bw = calculate_bw(&tmp->stats.UL_time_start,
+ &tmp->stats.UL_time_stop,
+ tmp->stats.UL_total_bytes);
+ float DL_bw = calculate_bw(&tmp->stats.DL_time_start,
+ &tmp->stats.DL_time_stop,
+ tmp->stats.DL_total_bytes);
+
+ if (tmp->iter_valid != TRUE) {
+ /* Add to list control if not drawn this UE before */
+ gtk_list_store_append(ues_store, &tmp->iter);
+ tmp->iter_valid = TRUE;
+ }
+
+ /* Set each column for this row */
+ gtk_list_store_set(ues_store, &tmp->iter,
+ RNTI_COLUMN, tmp->stats.rnti,
+ RNTI_TYPE_COLUMN,
+ (tmp->stats.rnti_type == C_RNTI) ? "C-RNTI" : "SPS-RNTI",
+ UEID_COLUMN, tmp->stats.ueid,
+ UL_FRAMES_COLUMN, tmp->stats.UL_frames,
+ UL_BYTES_COLUMN, tmp->stats.UL_total_bytes,
+ UL_BW_COLUMN, UL_bw,
+ UL_PADDING_PERCENT_COLUMN,
+ tmp->stats.UL_total_bytes ?
+ (((float)tmp->stats.UL_padding_bytes / (float)tmp->stats.UL_raw_bytes) * 100.0) :
+ 0.0,
+ UL_RETX_FRAMES_COLUMN, tmp->stats.UL_retx_frames,
+ DL_FRAMES_COLUMN, tmp->stats.DL_frames,
+ DL_BYTES_COLUMN, tmp->stats.DL_total_bytes,
+ DL_BW_COLUMN, DL_bw,
+ DL_CRC_FAILED_COLUMN, tmp->stats.DL_CRC_failures,
+ DL_CRC_HIGH_CODE_RATE_COLUMN, tmp->stats.DL_CRC_high_code_rate,
+ DL_CRC_PDSCH_LOST_COLUMN, tmp->stats.DL_CRC_PDSCH_lost,
+ DL_CRC_DUPLICATE_NONZERO_RV, tmp->stats.DL_CRC_Duplicate_Nonzero_rv,
+ DL_RETX_FRAMES_COLUMN, tmp->stats.DL_retx_frames,
+ TABLE_COLUMN, tmp,
+ -1);
+ }
+
+ /* Reselect UE? */
+ if (hs->selected_rnti != 0) {
+ GtkTreeIter *ue_iter = NULL;
+ mac_lte_ep_t *ep = hs->ep_list;
+ while (ep != NULL) {
+ if ((ep->stats.ueid == hs->selected_ueid) &&
+ (ep->stats.rnti == hs->selected_rnti)) {
+ ue_iter = &ep->iter;
+ break;
+ }
+ ep = ep->next;
+ }
+ if (ue_iter != NULL) {
+ /* Make selection */
+ gtk_tree_selection_select_iter(gtk_tree_view_get_selection(hs->ue_table),
+ ue_iter);
+ }
+ }
+
+ /* If there is a UE selected, update its counters in details window */
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->ue_table));
+ if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ mac_lte_ep_t *ep;
+
+ gtk_tree_model_get(model, &iter, TABLE_COLUMN, &ep, -1);
+ mac_lte_ue_details(ep, hs);
+ }
+}
+
+
+/* Compose and set appropriate display filter */
+typedef enum Direction_t {UL_Only, DL_Only, UL_and_DL} Direction_t;
+static void set_filter_expression(guint16 ueid,
+ guint16 rnti,
+ Direction_t direction,
+ gint showDCTErrors,
+ const gchar *DCTErrorSubstring,
+ mac_lte_stat_t *hs)
+{
+ #define MAX_FILTER_LEN 256
+ static char buffer[MAX_FILTER_LEN];
+ int offset = 0;
+
+ /* Create the filter expression */
+
+ /* Errors */
+ if (showDCTErrors) {
+ if (strlen(DCTErrorSubstring) > 0) {
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
+ "(dct2000.error-comment and (dct2000.comment contains \"%s\")) or (",
+ DCTErrorSubstring);
+ }
+ else {
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
+ "dct2000.error-comment or (");
+ }
+ }
+
+ /* Filter expression */
+ if (strlen(hs->filter)) {
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, "%s and ", hs->filter);
+ }
+
+ /* Direction */
+ switch (direction) {
+ case UL_Only:
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, "(mac-lte.direction == 0) and ");
+ break;
+ case DL_Only:
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, "(mac-lte.direction == 1) and ");
+ break;
+ default:
+ break;
+ }
+
+ /* Selected UE */
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
+ "mac-lte.rnti == %u and mac-lte.ueid == %u",
+ rnti, ueid);
+
+ /* Close parenthesis */
+ if (showDCTErrors) {
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
+ ")");
+ }
+
+ /* Set its value to our new string */
+ gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), buffer);
+
+ /* Run the filter */
+ main_filter_packets(&cfile, buffer, TRUE);
+}
+
+/* Respond to UL filter button being clicked by building and using filter */
+static void ul_filter_clicked(GtkWindow *win _U_, mac_lte_stat_t* hs)
+{
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ /* If there is a UE selected, update its counters in details window */
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->ue_table));
+ if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ mac_lte_ep_t *ep;
+
+ /* Get the UE details */
+ gtk_tree_model_get(model, &iter, TABLE_COLUMN, &ep, -1);
+
+ set_filter_expression(ep->stats.ueid, ep->stats.rnti, UL_Only,
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hs->show_dct_errors_cb)),
+ gtk_entry_get_text(GTK_ENTRY(hs->dct_error_substring_te)),
+ hs);
+ }
+}
+
+/* Respond to DL filter button being clicked by building and using filter */
+static void dl_filter_clicked(GtkWindow *win _U_, mac_lte_stat_t* hs)
+{
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ /* If there is a UE selected, update its counters in details window */
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->ue_table));
+ if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ mac_lte_ep_t *ep;
+
+ /* Get the UE details */
+ gtk_tree_model_get(model, &iter, TABLE_COLUMN, &ep, -1);
+
+ set_filter_expression(ep->stats.ueid, ep->stats.rnti, DL_Only,
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hs->show_dct_errors_cb)),
+ gtk_entry_get_text(GTK_ENTRY(hs->dct_error_substring_te)),
+ hs);
+ }
+
+}
+
+/* Respond to UL/DL filter button being clicked by building and using filter */
+static void uldl_filter_clicked(GtkWindow *win _U_, mac_lte_stat_t* hs)
+{
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ /* If there is a UE selected, update its counters in details window */
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->ue_table));
+ if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ mac_lte_ep_t *ep;
+
+ /* Get the UE details */
+ gtk_tree_model_get(model, &iter, TABLE_COLUMN, &ep, -1);
+
+ set_filter_expression(ep->stats.ueid, ep->stats.rnti, UL_and_DL,
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hs->show_dct_errors_cb)),
+ gtk_entry_get_text(GTK_ENTRY(hs->dct_error_substring_te)),
+ hs);
+ }
+
+}
+
+
+/* What to do when a list item is selected/unselected */
+static void mac_lte_select_cb(GtkTreeSelection *sel, gpointer data)
+{
+ mac_lte_stat_t *hs = (mac_lte_stat_t *)data;
+ mac_lte_ep_t *ep;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ /* Show details of selected UE */
+ gtk_tree_model_get(model, &iter, TABLE_COLUMN, &ep, -1);
+ mac_lte_ue_details(ep, hs);
+ }
+ else {
+ mac_lte_ue_details(NULL, hs);
+ }
+}
+
+/* When DCT errors check-box is toggled, enable substring controls accordingly */
+static void mac_lte_dct_errors_cb(GtkTreeSelection *sel _U_, gpointer data)
+{
+ mac_lte_stat_t *hs = (mac_lte_stat_t*)data;
+ guint8 show_dct_errors = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hs->show_dct_errors_cb));
+
+ gtk_widget_set_sensitive(hs->dct_error_substring_lb, show_dct_errors);
+ gtk_widget_set_sensitive(hs->dct_error_substring_te, show_dct_errors);
+}
+
+
+/* Destroy the stats window */
+static void win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ mac_lte_stat_t *hs = (mac_lte_stat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(hs);
+ unprotect_thread_critical_region();
+
+ if (hs->mac_lte_stat_dlg_w != NULL) {
+ window_destroy(hs->mac_lte_stat_dlg_w);
+ hs->mac_lte_stat_dlg_w = NULL;
+ }
+ mac_lte_stat_reset(hs);
+ g_free(hs->filter);
+ g_free(hs);
+}
+
+
+/* Create a new MAC LTE stats dialog */
+static void gtk_mac_lte_stat_init(const char *optarg, void *userdata _U_)
+{
+ mac_lte_stat_t *hs;
+ const char *filter = NULL;
+ GString *error_string;
+ GtkWidget *ues_scrolled_window;
+ GtkWidget *bbox;
+ GtkWidget *top_level_vbox;
+
+ GtkWidget *system_row_hbox;
+ GtkWidget *common_row_hbox;
+ GtkWidget *ues_vb;
+ GtkWidget *selected_ue_hb;
+
+ GtkWidget *mac_lte_stat_system_lb;
+ GtkWidget *mac_lte_stat_common_channel_lb;
+ GtkWidget *mac_lte_stat_selected_ue_lb;
+ GtkWidget *selected_ue_vbox[NUM_CHANNEL_COLUMNS];
+ GtkWidget *selected_ue_column_titles[5];
+
+ GtkWidget *mac_lte_stat_filters_lb;
+ GtkWidget *filter_buttons_hb;
+
+ GtkWidget *close_bt;
+ GtkWidget *help_bt;
+
+ GtkListStore *store;
+
+ GtkTreeView *tree_view;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *sel;
+ gchar title[256];
+ gint i, n;
+
+ /* Check for a filter string */
+ if (strncmp(optarg, "mac-lte,stat,", 13) == 0) {
+ /* Skip those characters from filter to display */
+ filter = optarg + 13;
+ }
+ else {
+ /* No filter */
+ filter = NULL;
+ }
+
+
+ /* Create dialog */
+ hs = g_malloc(sizeof(mac_lte_stat_t));
+ hs->ep_list = NULL;
+
+ /* Copy filter (so can be used for window title at reset) */
+ if (filter) {
+ hs->filter = g_strdup(filter);
+ }
+ else {
+ hs->filter = NULL;
+ }
+
+ /* Set title */
+ g_snprintf(title, sizeof(title), "Wireshark: LTE MAC Statistics: %s",
+ cf_get_display_name(&cfile));
+
+ /* Create top-level window */
+ hs->mac_lte_stat_dlg_w = window_new_with_geom(GTK_WINDOW_TOPLEVEL, title, "LTE MAC Statistics");
+
+ /* Window size */
+ gtk_window_set_default_size(GTK_WINDOW(hs->mac_lte_stat_dlg_w), 750, 300);
+
+ /* Will stack widgets vertically inside dlg */
+ top_level_vbox = gtk_vbox_new(FALSE, 3); /* FALSE = not homogeneous */
+ gtk_container_add(GTK_CONTAINER(hs->mac_lte_stat_dlg_w), top_level_vbox);
+
+ gtk_container_set_border_width(GTK_CONTAINER(top_level_vbox), 6);
+ gtk_widget_show(top_level_vbox);
+
+
+ /**********************************************/
+ /* System data */
+ /**********************************************/
+ mac_lte_stat_system_lb = gtk_frame_new("System Data");
+
+ /* Add max UEs/TTI counts in one row */
+ system_row_hbox = gtk_hbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(mac_lte_stat_system_lb), system_row_hbox);
+ gtk_container_set_border_width(GTK_CONTAINER(system_row_hbox), 5);
+
+ gtk_box_pack_start(GTK_BOX(top_level_vbox), mac_lte_stat_system_lb, FALSE, FALSE, 0);
+
+ /* Create labels (that will hold label and counter value) */
+ hs->ul_max_ues_per_tti = gtk_label_new("Max UL UEs/TTI:");
+ gtk_misc_set_alignment(GTK_MISC(hs->ul_max_ues_per_tti), 0.0f, .5f);
+ gtk_container_add(GTK_CONTAINER(system_row_hbox), hs->ul_max_ues_per_tti);
+ gtk_widget_show(hs->ul_max_ues_per_tti);
+
+ hs->dl_max_ues_per_tti = gtk_label_new("Max DL UEs/TTI:");
+ gtk_misc_set_alignment(GTK_MISC(hs->dl_max_ues_per_tti), 0.0f, .5f);
+ gtk_container_add(GTK_CONTAINER(system_row_hbox), hs->dl_max_ues_per_tti);
+ gtk_widget_show(hs->dl_max_ues_per_tti);
+
+
+ /**********************************************/
+ /* Common Channel data */
+ /**********************************************/
+ mac_lte_stat_common_channel_lb = gtk_frame_new("Common Channel Data");
+
+ /* Will add BCH and PCH counters into one row */
+ common_row_hbox = gtk_hbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(mac_lte_stat_common_channel_lb), common_row_hbox);
+ gtk_container_set_border_width(GTK_CONTAINER(common_row_hbox), 5);
+
+ gtk_box_pack_start(GTK_BOX(top_level_vbox), mac_lte_stat_common_channel_lb, FALSE, FALSE, 0);
+
+ /* Create labels (that will hold label and counter value) */
+ hs->common_bch_frames = gtk_label_new("BCH Frames:");
+ gtk_misc_set_alignment(GTK_MISC(hs->common_bch_frames), 0.0f, .5f);
+ gtk_container_add(GTK_CONTAINER(common_row_hbox), hs->common_bch_frames);
+ gtk_widget_show(hs->common_bch_frames);
+
+ hs->common_bch_bytes = gtk_label_new("BCH Bytes:");
+ gtk_misc_set_alignment(GTK_MISC(hs->common_bch_bytes), 0.0f, .5f);
+ gtk_container_add(GTK_CONTAINER(common_row_hbox), hs->common_bch_bytes);
+ gtk_widget_show(hs->common_bch_bytes);
+
+ hs->common_pch_frames = gtk_label_new("PCH Frames:");
+ gtk_misc_set_alignment(GTK_MISC(hs->common_pch_frames), 0.0f, .5f);
+ gtk_container_add(GTK_CONTAINER(common_row_hbox), hs->common_pch_frames);
+ gtk_widget_show(hs->common_pch_frames);
+
+ hs->common_pch_bytes = gtk_label_new("PCH Bytes:");
+ gtk_misc_set_alignment(GTK_MISC(hs->common_pch_bytes), 0.0f, .5f);
+ gtk_container_add(GTK_CONTAINER(common_row_hbox), hs->common_pch_bytes);
+ gtk_widget_show(hs->common_pch_bytes);
+
+ hs->common_rar_frames = gtk_label_new("RAR Frames:");
+ gtk_misc_set_alignment(GTK_MISC(hs->common_rar_frames), 0.0f, .5f);
+ gtk_container_add(GTK_CONTAINER(common_row_hbox), hs->common_rar_frames);
+ gtk_widget_show(hs->common_rar_frames);
+
+ hs->common_rar_entries = gtk_label_new("RAR Entries:");
+ gtk_misc_set_alignment(GTK_MISC(hs->common_rar_entries), 0.0f, .5f);
+ gtk_container_add(GTK_CONTAINER(common_row_hbox), hs->common_rar_entries);
+ gtk_widget_show(hs->common_rar_entries);
+
+ /**********************************************/
+ /* UL/DL-SCH data */
+ /**********************************************/
+
+ hs->mac_lte_stat_ues_lb = gtk_frame_new("UL/DL-SCH Data (0 UEs)");
+ ues_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(hs->mac_lte_stat_ues_lb), ues_vb);
+ gtk_container_set_border_width(GTK_CONTAINER(ues_vb), 5);
+
+ ues_scrolled_window = scrolled_window_new(NULL, NULL);
+ gtk_box_pack_start(GTK_BOX(ues_vb), ues_scrolled_window, TRUE, TRUE, 0);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(ues_scrolled_window),
+ GTK_SHADOW_IN);
+
+ /* Create the table of UE data */
+ store = gtk_list_store_new(NUM_UE_COLUMNS, G_TYPE_INT, G_TYPE_STRING, G_TYPE_INT,
+ G_TYPE_INT, G_TYPE_INT, G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_INT, /* UL */
+ G_TYPE_INT, G_TYPE_INT, G_TYPE_FLOAT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, /* DL */
+ G_TYPE_POINTER);
+ hs->ue_table = GTK_TREE_VIEW(tree_view_new(GTK_TREE_MODEL(store)));
+ gtk_container_add(GTK_CONTAINER (ues_scrolled_window), GTK_WIDGET(hs->ue_table));
+ g_object_unref(G_OBJECT(store));
+
+ tree_view = hs->ue_table;
+ gtk_tree_view_set_headers_visible(tree_view, TRUE);
+ gtk_tree_view_set_headers_clickable(tree_view, TRUE);
+
+ /* Create the titles for each column of the per-UE table */
+ for (i = 0; i < TABLE_COLUMN; i++) {
+ if (i == UL_PADDING_PERCENT_COLUMN) {
+ /* Show % as progress bar */
+ renderer = gtk_cell_renderer_progress_new();
+ column = gtk_tree_view_column_new_with_attributes(ue_titles[i], renderer,
+ "text", i,
+ "value", i,
+ NULL);
+ }
+ else {
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(ue_titles[i], renderer,
+ "text", i, NULL);
+ }
+ gtk_tree_view_column_set_sort_column_id(column, i);
+
+ if (i == 0) {
+ /* Expand first column (RNTI, which is Key) */
+ gtk_tree_view_column_set_expand(column, TRUE);
+ } else {
+ /* For other columns, set all of the free space to be on the left */
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+ }
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_append_column(tree_view, column);
+ }
+
+ /* Set callback function for selecting a row in the UE table */
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->ue_table));
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+ g_signal_connect(sel, "changed", G_CALLBACK(mac_lte_select_cb), hs);
+
+ gtk_box_pack_start(GTK_BOX(top_level_vbox), hs->mac_lte_stat_ues_lb, TRUE, TRUE, 0);
+
+
+ /**********************************************/
+ /* Details of selected UE */
+ /**********************************************/
+
+ mac_lte_stat_selected_ue_lb = gtk_frame_new("Selected UE details");
+
+ selected_ue_hb = gtk_hbox_new(FALSE, 6);
+ gtk_container_add(GTK_CONTAINER(mac_lte_stat_selected_ue_lb), selected_ue_hb);
+ gtk_container_set_border_width(GTK_CONTAINER(selected_ue_hb), 5);
+
+ /********************************/
+ /* First (row titles) column */
+ selected_ue_vbox[0] = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(selected_ue_hb), selected_ue_vbox[0]);
+
+ selected_ue_column_titles[0] = gtk_label_new("");
+ gtk_misc_set_alignment(GTK_MISC(selected_ue_column_titles[0]), 0.0f, 0.0f);
+ gtk_container_add(GTK_CONTAINER(selected_ue_vbox[0]), selected_ue_column_titles[0]);
+
+ for (n=1; n < 5; n++) {
+ selected_ue_column_titles[n] = gtk_label_new(selected_ue_row_names[n-1]);
+ gtk_misc_set_alignment(GTK_MISC(selected_ue_column_titles[n]), 0.0f, 0.0f);
+ gtk_container_add(GTK_CONTAINER(selected_ue_vbox[0]), selected_ue_column_titles[n]);
+ gtk_widget_show(selected_ue_column_titles[n]);
+ }
+
+
+ /*************************/
+ /* Other columns */
+ for (i=CCCH_COLUMN; i < NUM_CHANNEL_COLUMNS; i++) {
+ selected_ue_vbox[i] = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(selected_ue_hb), selected_ue_vbox[i]);
+
+ /* Channel title */
+ hs->selected_ue_column_entry[i][0] = gtk_label_new(channel_titles[i-1]);
+ gtk_misc_set_alignment(GTK_MISC(hs->selected_ue_column_entry[i][0]), 0.5f, 0.0f);
+ gtk_container_add(GTK_CONTAINER(selected_ue_vbox[i]), hs->selected_ue_column_entry[i][0]);
+
+ /* Counts for this channel */
+ for (n=1; n < 5; n++) {
+ hs->selected_ue_column_entry[i][n] = gtk_label_new("0");
+ gtk_misc_set_alignment(GTK_MISC(hs->selected_ue_column_entry[i][n]), 1.0f, 0.0f);
+ gtk_container_add(GTK_CONTAINER(selected_ue_vbox[i]), hs->selected_ue_column_entry[i][n]);
+ gtk_widget_show(hs->selected_ue_column_entry[i][n]);
+ }
+ }
+
+ gtk_box_pack_start(GTK_BOX(top_level_vbox), mac_lte_stat_selected_ue_lb, FALSE, FALSE, 0);
+
+
+ /**************************************/
+ /* Filter on RNTI/UEId */
+ /**************************************/
+ mac_lte_stat_filters_lb = gtk_frame_new("Filter on UE");
+
+ /* Horizontal row of filter controls */
+ filter_buttons_hb = gtk_hbox_new(FALSE, 6);
+ gtk_container_add(GTK_CONTAINER(mac_lte_stat_filters_lb), filter_buttons_hb);
+ gtk_container_set_border_width(GTK_CONTAINER(filter_buttons_hb), 2);
+
+ /* Add filters box to top-level window */
+ gtk_box_pack_start(GTK_BOX(top_level_vbox), mac_lte_stat_filters_lb, FALSE, FALSE, 0);
+
+ /* Filter buttons */
+
+ /* UL only */
+ hs->ul_filter_bt = gtk_button_new_with_label("Set UL display filter on selected this RNTI / UEId");
+ gtk_box_pack_start(GTK_BOX(filter_buttons_hb), hs->ul_filter_bt, FALSE, FALSE, 0);
+ g_signal_connect(hs->ul_filter_bt, "clicked", G_CALLBACK(ul_filter_clicked), hs);
+ gtk_widget_set_sensitive(hs->ul_filter_bt, FALSE);
+ gtk_widget_show(hs->ul_filter_bt);
+ gtk_widget_set_tooltip_text(hs->ul_filter_bt,
+ "Generate and set a filter showing only UL frames with selected RNTI and UEId");
+
+ /* DL only */
+ hs->dl_filter_bt = gtk_button_new_with_label("Set DL display filter on selected this RNTI / UEId");
+ gtk_box_pack_start(GTK_BOX(filter_buttons_hb), hs->dl_filter_bt, FALSE, FALSE, 0);
+ g_signal_connect(hs->dl_filter_bt, "clicked", G_CALLBACK(dl_filter_clicked), hs);
+ gtk_widget_set_sensitive(hs->dl_filter_bt, FALSE);
+ gtk_widget_show(hs->dl_filter_bt);
+ gtk_widget_set_tooltip_text(hs->dl_filter_bt,
+ "Generate and set a filter showing only DL frames with selected RNTI and UEId");
+
+ /* UL and DL */
+ hs->uldl_filter_bt = gtk_button_new_with_label("Set UL / DL display filter on selected this RNTI / UEId");
+ gtk_box_pack_start(GTK_BOX(filter_buttons_hb), hs->uldl_filter_bt, FALSE, FALSE, 0);
+ g_signal_connect(hs->uldl_filter_bt, "clicked", G_CALLBACK(uldl_filter_clicked), hs);
+ gtk_widget_set_sensitive(hs->uldl_filter_bt, FALSE);
+ gtk_widget_show(hs->uldl_filter_bt);
+ gtk_widget_set_tooltip_text(hs->uldl_filter_bt,
+ "Generate and set a filter showing only frames with selected RNTI and UEId");
+
+
+ /* Allow DCT errors to be shown... */
+ hs->show_dct_errors_cb = gtk_check_button_new_with_mnemonic("Show DCT2000 error strings");
+ gtk_container_add(GTK_CONTAINER(filter_buttons_hb), hs->show_dct_errors_cb);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hs->show_dct_errors_cb), FALSE);
+ g_signal_connect(hs->show_dct_errors_cb, "toggled", G_CALLBACK(mac_lte_dct_errors_cb), hs);
+ gtk_widget_set_tooltip_text(hs->show_dct_errors_cb, "When checked, generated filters will "
+ "include DCT2000 error strings");
+ /* Initially disabled */
+ gtk_widget_set_sensitive(hs->show_dct_errors_cb, FALSE);
+
+ /* ... optionally limited by a substring */
+ hs->dct_error_substring_lb = gtk_label_new("...containing");
+ gtk_box_pack_start(GTK_BOX(filter_buttons_hb), hs->dct_error_substring_lb, FALSE, FALSE, 0);
+ gtk_widget_show(hs->dct_error_substring_lb);
+ gtk_widget_set_sensitive(hs->dct_error_substring_lb, FALSE);
+
+ hs->dct_error_substring_te = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(filter_buttons_hb), hs->dct_error_substring_te, FALSE, FALSE, 0);
+ gtk_widget_show(hs->dct_error_substring_te);
+ gtk_widget_set_sensitive(hs->dct_error_substring_te, FALSE);
+ gtk_widget_set_tooltip_text(hs->dct_error_substring_te,
+ "If given, only match error strings containing this substring");
+
+
+ /**********************************************/
+ /* Register the tap listener */
+ /**********************************************/
+
+ error_string = register_tap_listener("mac-lte", hs,
+ filter, 0,
+ mac_lte_stat_reset,
+ mac_lte_stat_packet,
+ mac_lte_stat_draw);
+ if (error_string) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(hs);
+ return;
+ }
+
+
+ /************************************/
+ /* Button row. */
+ /************************************/
+
+ bbox = dlg_button_row_new (GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_end (GTK_BOX(top_level_vbox), bbox, FALSE, FALSE, 0);
+
+ /* Add the close button */
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(hs->mac_lte_stat_dlg_w, close_bt, window_cancel_button_cb);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_LTE_MAC_TRAFFIC_DIALOG);
+
+ /* Set callbacks */
+ g_signal_connect(hs->mac_lte_stat_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(hs->mac_lte_stat_dlg_w, "destroy", G_CALLBACK(win_destroy_cb), hs);
+
+ /* Show the window */
+ gtk_widget_show_all(hs->mac_lte_stat_dlg_w);
+ window_present(hs->mac_lte_stat_dlg_w);
+
+ /* Retap */
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(hs->mac_lte_stat_dlg_w));
+}
+
+
+static tap_param mac_lte_stat_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg mac_lte_stat_dlg = {
+ "LTE MAC Stats",
+ "mac-lte,stat",
+ gtk_mac_lte_stat_init,
+ -1,
+ G_N_ELEMENTS(mac_lte_stat_params),
+ mac_lte_stat_params
+};
+
+
+/* Register this tap listener (need void on own so line register function found) */
+void
+register_tap_listener_mac_lte_stat(void)
+{
+ register_dfilter_stat(&mac_lte_stat_dlg, "_LTE/_MAC", REGISTER_STAT_GROUP_TELEPHONY);
+}
+
+void mac_lte_stat_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &mac_lte_stat_dlg);
+}
+
diff --git a/ui/gtk/macros_dlg.c b/ui/gtk/macros_dlg.c
new file mode 100644
index 0000000000..6b224c3ee0
--- /dev/null
+++ b/ui/gtk/macros_dlg.c
@@ -0,0 +1,56 @@
+/* macros_dlg.c
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2001 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <stdlib.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/dfilter/dfilter-macro.h>
+#include <epan/uat-int.h>
+
+#include "globals.h"
+#include "ui/gtk/uat_gui.h"
+#include "ui/gtk/macros_dlg.h"
+#include "ui/gtk/gtkglobals.h"
+
+void macros_post_update(void) {
+ g_free (cfile.dfilter);
+ cfile.dfilter = NULL;
+ g_signal_emit_by_name(main_display_filter_widget, "changed");
+}
+
+void macros_init (void) {
+ void* dfmuat;
+ dfilter_macro_get_uat(&dfmuat);
+ ((uat_t*)dfmuat)->post_update_cb = macros_post_update;
+}
+
+void macros_dialog_cb(GtkWidget *w _U_, gpointer data _U_) {
+ void* dfmuat;
+ dfilter_macro_get_uat(&dfmuat);
+ uat_window_cb(NULL,dfmuat);
+}
+
diff --git a/ui/gtk/macros_dlg.h b/ui/gtk/macros_dlg.h
new file mode 100644
index 0000000000..73e1104221
--- /dev/null
+++ b/ui/gtk/macros_dlg.h
@@ -0,0 +1,31 @@
+/* macros_dlg.h
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2001 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MACROS_DLG_H__
+#define __MACROS_DLG_H__
+
+void macros_post_update(void);
+void macros_init (void);
+void macros_dialog_cb(GtkWidget*, gpointer);
+
+#endif /* __MACROS_DLG_H__ */
diff --git a/ui/gtk/main.c b/ui/gtk/main.c
new file mode 100644
index 0000000000..7d433bce5e
--- /dev/null
+++ b/ui/gtk/main.c
@@ -0,0 +1,3855 @@
+/* main.c
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * Richard Sharpe, 13-Feb-1999, added support for initializing structures
+ * needed by dissect routines
+ * Jeff Foster, 2001/03/12, added support tabbed hex display windowss
+ *
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#if GTK_CHECK_VERSION(3,0,0)
+# include <gdk/gdkkeysyms-compat.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <locale.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifndef HAVE_GETOPT
+#include "wsutil/wsgetopt.h"
+#endif
+
+#ifdef _WIN32 /* Needed for console I/O */
+#if _MSC_VER < 1500
+/* AttachConsole() needs this #define! */
+#define _WIN32_WINNT 0x0501
+#endif
+#include <fcntl.h>
+#include <conio.h>
+#endif
+
+#ifdef HAVE_LIBPORTAUDIO
+#include <portaudio.h>
+#endif /* HAVE_LIBPORTAUDIO */
+
+#include <epan/epan.h>
+#include <epan/filesystem.h>
+#include <wsutil/privileges.h>
+#include <epan/epan_dissect.h>
+#include <epan/timestamp.h>
+#include <epan/packet.h>
+#include <epan/plugins.h>
+#include <epan/dfilter/dfilter.h>
+#include <epan/strutil.h>
+#include <epan/addr_resolv.h>
+#include <epan/emem.h>
+#include <epan/ex-opt.h>
+#include <epan/funnel.h>
+#include <epan/expert.h>
+#include <epan/frequency-utils.h>
+#include <epan/prefs.h>
+#include <epan/prefs-int.h>
+#include <epan/tap.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/uat.h>
+#include <epan/column.h>
+
+/* general (not GTK specific) */
+#include "../file.h"
+#include "../summary.h"
+#include "../filters.h"
+#include "../disabled_protos.h"
+#include "../color.h"
+#include "../color_filters.h"
+#include "../print.h"
+#include "../simple_dialog.h"
+#include "../main_statusbar.h"
+#include "../register.h"
+#include "../ringbuffer.h"
+#include "../ui_util.h"
+#include "../util.h"
+#include "../clopts_common.h"
+#include "../console_io.h"
+#include "../cmdarg_err.h"
+#include "../version_info.h"
+#include "../merge.h"
+#include "../alert_box.h"
+#include "../log.h"
+#include "../u3.h"
+#include <wsutil/file_util.h>
+
+#ifdef HAVE_LIBPCAP
+#include "../capture_ui_utils.h"
+#include "../capture-pcap-util.h"
+#include "../capture_ifinfo.h"
+#include "../capture.h"
+#include "../capture_sync.h"
+#endif
+
+#ifdef _WIN32
+#include "../capture-wpcap.h"
+#include "../capture_wpcap_packet.h"
+#include <tchar.h> /* Needed for Unicode */
+#include <wsutil/unicode-utils.h>
+#include <commctrl.h>
+#include <shellapi.h>
+#endif /* _WIN32 */
+
+/* GTK related */
+#include "ui/gtk/file_dlg.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/color_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/color_dlg.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/uat_gui.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/main_airpcap_toolbar.h"
+#include "ui/gtk/main_filter_toolbar.h"
+#include "ui/gtk/menus.h"
+#include "ui/gtk/macros_dlg.h"
+#include "ui/gtk/main_statusbar_private.h"
+#include "ui/gtk/main_toolbar.h"
+#include "ui/gtk/main_welcome.h"
+#include "ui/gtk/drag_and_drop.h"
+#include "ui/gtk/capture_file_dlg.h"
+#include "ui/gtk/main_proto_draw.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/packet_win.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/find_dlg.h"
+#include "ui/gtk/recent.h"
+#include "ui/gtk/follow_tcp.h"
+#include "ui/gtk/font_utils.h"
+#include "ui/gtk/about_dlg.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/decode_as_dlg.h"
+#include "ui/gtk/webbrowser.h"
+#include "ui/gtk/capture_dlg.h"
+#include "ui/gtk/capture_if_dlg.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/prefs_column.h"
+#include "ui/gtk/prefs_dlg.h"
+#include "ui/gtk/proto_help.h"
+#include "ui/gtk/new_packet_list.h"
+#include "ui/gtk/filter_expression_save_dlg.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+#ifdef HAVE_LIBPCAP
+#include "../image/wsicon16.xpm"
+#include "../image/wsicon32.xpm"
+#include "../image/wsicon48.xpm"
+#include "../image/wsicon64.xpm"
+#include "../image/wsiconcap16.xpm"
+#include "../image/wsiconcap32.xpm"
+#include "../image/wsiconcap48.xpm"
+#endif
+
+#ifdef HAVE_AIRPCAP
+#include <airpcap.h>
+#include "airpcap_loader.h"
+#include "airpcap_dlg.h"
+#include "airpcap_gui_utils.h"
+#endif
+
+#include <epan/crypt/airpdcap_ws.h>
+
+
+#ifdef HAVE_GTKOSXAPPLICATION
+#include <igemacintegration/gtkosxapplication.h>
+#endif
+
+/*
+ * Files under personal and global preferences directories in which
+ * GTK settings for Wireshark are stored.
+ */
+#define RC_FILE "gtkrc"
+
+capture_file cfile;
+
+/* "exported" main widgets */
+GtkWidget *top_level = NULL, *pkt_scrollw, *tree_view_gbl, *byte_nb_ptr_gbl;
+
+/* placement widgets (can be a bit confusing, because of the many layout possibilities */
+static GtkWidget *main_vbox, *main_pane_v1, *main_pane_v2, *main_pane_h1, *main_pane_h2;
+static GtkWidget *main_first_pane, *main_second_pane;
+
+/* internally used widgets */
+static GtkWidget *menubar, *main_tb, *filter_tb, *tv_scrollw, *statusbar, *welcome_pane;
+
+#ifdef HAVE_AIRPCAP
+GtkWidget *airpcap_tb;
+int airpcap_dll_ret_val = -1;
+#endif
+
+GString *comp_info_str, *runtime_info_str;
+
+static gboolean have_capture_file = FALSE; /* XXX - is there an equivalent in cfile? */
+
+static guint tap_update_timer_id;
+
+#ifdef _WIN32
+static gboolean has_console; /* TRUE if app has console */
+static gboolean console_wait; /* "Press any key..." */
+static void destroy_console(void);
+static gboolean stdin_capture = FALSE; /* Don't grab stdin & stdout if TRUE */
+#endif
+static void console_log_handler(const char *log_domain,
+ GLogLevelFlags log_level, const char *message, gpointer user_data);
+
+#ifdef HAVE_LIBPCAP
+capture_options global_capture_opts;
+#endif
+
+
+static void create_main_window(gint, gint, gint, e_prefs*);
+static void show_main_window(gboolean);
+static void file_quit_answered_cb(gpointer dialog, gint btn, gpointer data);
+static void main_save_window_geometry(GtkWidget *widget);
+
+
+/* Match selected byte pattern */
+static void
+match_selected_cb_do(gpointer data, int action, gchar *text)
+{
+ GtkWidget *filter_te;
+ char *cur_filter, *new_filter;
+
+ if ((!text) || (0 == strlen(text))) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not acquire information to build a filter!\nTry expanding or choosing another item.");
+ return;
+ }
+
+ g_assert(data);
+ filter_te = g_object_get_data(G_OBJECT(data), E_DFILTER_TE_KEY);
+ g_assert(filter_te);
+
+ cur_filter = gtk_editable_get_chars(GTK_EDITABLE(filter_te), 0, -1);
+
+ switch (action&MATCH_SELECTED_MASK) {
+
+ case MATCH_SELECTED_REPLACE:
+ new_filter = g_strdup(text);
+ break;
+
+ case MATCH_SELECTED_AND:
+ if ((!cur_filter) || (0 == strlen(cur_filter)))
+ new_filter = g_strdup(text);
+ else
+ new_filter = g_strconcat("(", cur_filter, ") && (", text, ")", NULL);
+ break;
+
+ case MATCH_SELECTED_OR:
+ if ((!cur_filter) || (0 == strlen(cur_filter)))
+ new_filter = g_strdup(text);
+ else
+ new_filter = g_strconcat("(", cur_filter, ") || (", text, ")", NULL);
+ break;
+
+ case MATCH_SELECTED_NOT:
+ new_filter = g_strconcat("!(", text, ")", NULL);
+ break;
+
+ case MATCH_SELECTED_AND_NOT:
+ if ((!cur_filter) || (0 == strlen(cur_filter)))
+ new_filter = g_strconcat("!(", text, ")", NULL);
+ else
+ new_filter = g_strconcat("(", cur_filter, ") && !(", text, ")", NULL);
+ break;
+
+ case MATCH_SELECTED_OR_NOT:
+ if ((!cur_filter) || (0 == strlen(cur_filter)))
+ new_filter = g_strconcat("!(", text, ")", NULL);
+ else
+ new_filter = g_strconcat("(", cur_filter, ") || !(", text, ")", NULL);
+ break;
+
+ default:
+ g_assert_not_reached();
+ new_filter = NULL;
+ break;
+ }
+
+ /* Free up the copy we got of the old filter text. */
+ g_free(cur_filter);
+
+ /* Don't change the current display filter if we only want to copy the filter */
+ if (action&MATCH_SELECTED_COPY_ONLY) {
+ GString *gtk_text_str = g_string_new("");
+ g_string_append(gtk_text_str, new_filter);
+ copy_to_clipboard(gtk_text_str);
+ g_string_free(gtk_text_str, TRUE);
+ } else {
+ /* create a new one and set the display filter entry accordingly */
+ gtk_entry_set_text(GTK_ENTRY(filter_te), new_filter);
+
+ /* Run the display filter so it goes in effect. */
+ if (action&MATCH_SELECTED_APPLY_NOW)
+ main_filter_packets(&cfile, new_filter, FALSE);
+ }
+
+ /* Free up the new filter text. */
+ g_free(new_filter);
+}
+
+void
+match_selected_ptree_cb(GtkWidget *w, gpointer data, MATCH_SELECTED_E action)
+{
+ char *filter = NULL;
+
+ if (cfile.finfo_selected) {
+ filter = proto_construct_match_selected_string(cfile.finfo_selected,
+ cfile.edt);
+ match_selected_cb_do((data ? data : w), action, filter);
+ }
+}
+
+void
+colorize_selected_ptree_cb(GtkWidget *w _U_, gpointer data _U_, guint8 filt_nr)
+{
+ char *filter = NULL;
+
+ if (cfile.finfo_selected) {
+ filter = proto_construct_match_selected_string(cfile.finfo_selected,
+ cfile.edt);
+ if ((!filter) || (0 == strlen(filter))) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Could not acquire information to build a filter!\n"
+ "Try expanding or choosing another item.");
+ return;
+ }
+
+ if (filt_nr==0) {
+ color_display_with_filter(filter);
+ } else {
+ if (filt_nr==255) {
+ color_filters_reset_tmp();
+ } else {
+ color_filters_set_tmp(filt_nr,filter, FALSE);
+ }
+ new_packet_list_colorize_packets();
+ }
+ }
+}
+
+
+static void selected_ptree_info_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
+{
+ gchar *selected_proto_url;
+ gchar *proto_abbrev = data;
+
+
+ switch(btn) {
+ case(ESD_BTN_OK):
+ if (cfile.finfo_selected) {
+ /* open wiki page using the protocol abbreviation */
+ selected_proto_url = g_strdup_printf("http://wiki.wireshark.org/Protocols/%s", proto_abbrev);
+ browser_open_url(selected_proto_url);
+ g_free(selected_proto_url);
+ }
+ break;
+ case(ESD_BTN_CANCEL):
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+
+void
+selected_ptree_info_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ int field_id;
+ const gchar *proto_abbrev;
+ gpointer dialog;
+
+
+ if (cfile.finfo_selected) {
+ /* convert selected field to protocol abbreviation */
+ /* XXX - could this conversion be simplified? */
+ field_id = cfile.finfo_selected->hfinfo->id;
+ /* if the selected field isn't a protocol, get it's parent */
+ if(!proto_registrar_is_protocol(field_id)) {
+ field_id = proto_registrar_get_parent(cfile.finfo_selected->hfinfo->id);
+ }
+
+ proto_abbrev = proto_registrar_get_abbrev(field_id);
+
+ if (!proto_is_private(field_id)) {
+ /* ask the user if the wiki page really should be opened */
+ dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_OK_CANCEL,
+ "%sOpen Wireshark Wiki page of protocol \"%s\"?%s\n"
+ "\n"
+ "This will open the \"%s\" related Wireshark Wiki page in your Web browser.\n"
+ "\n"
+ "The Wireshark Wiki is a collaborative approach to provide information "
+ "about Wireshark in several ways (not limited to protocol specifics).\n"
+ "\n"
+ "This Wiki is new, so the page of the selected protocol "
+ "may not exist and/or may not contain valuable information.\n"
+ "\n"
+ "As everyone can edit the Wiki and add new content (or extend existing), "
+ "you are encouraged to add information if you can.\n"
+ "\n"
+ "Hint 1: If you are new to wiki editing, try out editing the Sandbox first!\n"
+ "\n"
+ "Hint 2: If you want to add a new protocol page, you should use the ProtocolTemplate, "
+ "which will save you a lot of editing and will give a consistent look over the pages.",
+ simple_dialog_primary_start(), proto_abbrev, simple_dialog_primary_end(), proto_abbrev);
+ simple_dialog_set_cb(dialog, selected_ptree_info_answered_cb, (gpointer)proto_abbrev);
+ } else {
+ /* appologize to the user that the wiki page cannot be opened */
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "%sCan't open Wireshark Wiki page of protocol \"%s\"%s\n"
+ "\n"
+ "This would open the \"%s\" related Wireshark Wiki page in your Web browser.\n"
+ "\n"
+ "Since this is a private protocol, such information is not available in "
+ "a public wiki. Therefore this wiki entry is blocked.\n"
+ "\n"
+ "Sorry for the inconvenience.\n",
+ simple_dialog_primary_start(), proto_abbrev, simple_dialog_primary_end(), proto_abbrev);
+ }
+ }
+}
+
+static void selected_ptree_ref_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
+{
+ gchar *selected_proto_url;
+ gchar *proto_abbrev = data;
+
+ switch(btn) {
+ case(ESD_BTN_OK):
+ if (cfile.finfo_selected) {
+ /* open reference page using the protocol abbreviation */
+ selected_proto_url = g_strdup_printf("http://www.wireshark.org/docs/dfref/%c/%s", proto_abbrev[0], proto_abbrev);
+ browser_open_url(selected_proto_url);
+ g_free(selected_proto_url);
+ }
+ break;
+ case(ESD_BTN_CANCEL):
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+void
+selected_ptree_ref_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ int field_id;
+ const gchar *proto_abbrev;
+ gpointer dialog;
+
+
+ if (cfile.finfo_selected) {
+ /* convert selected field to protocol abbreviation */
+ /* XXX - could this conversion be simplified? */
+ field_id = cfile.finfo_selected->hfinfo->id;
+ /* if the selected field isn't a protocol, get it's parent */
+ if(!proto_registrar_is_protocol(field_id)) {
+ field_id = proto_registrar_get_parent(cfile.finfo_selected->hfinfo->id);
+ }
+
+ proto_abbrev = proto_registrar_get_abbrev(field_id);
+
+ if (!proto_is_private(field_id)) {
+ /* ask the user if the wiki page really should be opened */
+ dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_OK_CANCEL,
+ "%sOpen Wireshark filter reference page of protocol \"%s\"?%s\n"
+ "\n"
+ "This will open the \"%s\" related Wireshark filter reference page in your Web browser.\n"
+ "\n",
+ simple_dialog_primary_start(), proto_abbrev, simple_dialog_primary_end(), proto_abbrev);
+ simple_dialog_set_cb(dialog, selected_ptree_ref_answered_cb, (gpointer)proto_abbrev);
+ } else {
+ /* appologize to the user that the wiki page cannot be opened */
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "%sCan't open Wireshark filter reference page of protocol \"%s\"%s\n"
+ "\n"
+ "This would open the \"%s\" related Wireshark filter reference page in your Web browser.\n"
+ "\n"
+ "Since this is a private protocol, such information is not available on "
+ "a public website. Therefore this filter entry is blocked.\n"
+ "\n"
+ "Sorry for the inconvenience.\n",
+ simple_dialog_primary_start(), proto_abbrev, simple_dialog_primary_end(), proto_abbrev);
+ }
+ }
+}
+
+static gboolean
+is_address_column (gint column)
+{
+ if (((cfile.cinfo.col_fmt[column] == COL_DEF_SRC) ||
+ (cfile.cinfo.col_fmt[column] == COL_RES_SRC) ||
+ (cfile.cinfo.col_fmt[column] == COL_DEF_DST) ||
+ (cfile.cinfo.col_fmt[column] == COL_RES_DST)) &&
+ strlen(cfile.cinfo.col_expr.col_expr_val[column]))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+GList *
+get_ip_address_list_from_packet_list_row(gpointer data)
+{
+ gint row = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(data), E_MPACKET_LIST_ROW_KEY));
+ gint column = new_packet_list_get_column_id (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(data), E_MPACKET_LIST_COL_KEY)));
+ gint col;
+ frame_data *fdata;
+ GList *addr_list = NULL;
+
+ fdata = (frame_data *) new_packet_list_get_row_data(row);
+
+ if (fdata != NULL) {
+ epan_dissect_t edt;
+
+ if (!cf_read_frame (&cfile, fdata))
+ return NULL; /* error reading the frame */
+
+ epan_dissect_init(&edt, FALSE, FALSE);
+ col_custom_prime_edt(&edt, &cfile.cinfo);
+
+ epan_dissect_run(&edt, &cfile.pseudo_header, cfile.pd, fdata, &cfile.cinfo);
+ epan_dissect_fill_in_columns(&edt, TRUE, TRUE);
+
+ /* First check selected column */
+ if (is_address_column (column)) {
+ addr_list = g_list_append (addr_list, se_strdup_printf("%s", cfile.cinfo.col_expr.col_expr_val[column]));
+ }
+
+ for (col = 0; col < cfile.cinfo.num_cols; col++) {
+ /* Then check all columns except the selected */
+ if ((col != column) && (is_address_column (col))) {
+ addr_list = g_list_append (addr_list, se_strdup_printf("%s", cfile.cinfo.col_expr.col_expr_val[col]));
+ }
+ }
+
+ epan_dissect_cleanup(&edt);
+ }
+
+ return addr_list;
+}
+
+static gchar *
+get_filter_from_packet_list_row_and_column(gpointer data)
+{
+ gint row = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(data), E_MPACKET_LIST_ROW_KEY));
+ gint column = new_packet_list_get_column_id (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(data), E_MPACKET_LIST_COL_KEY)));
+ frame_data *fdata;
+ gchar *buf=NULL;
+
+ fdata = (frame_data *) new_packet_list_get_row_data(row);
+
+ if (fdata != NULL) {
+ epan_dissect_t edt;
+
+ if (!cf_read_frame(&cfile, fdata))
+ return NULL; /* error reading the frame */
+ /* proto tree, visible. We need a proto tree if there's custom columns */
+ epan_dissect_init(&edt, have_custom_cols(&cfile.cinfo), FALSE);
+ col_custom_prime_edt(&edt, &cfile.cinfo);
+
+ epan_dissect_run(&edt, &cfile.pseudo_header, cfile.pd, fdata,
+ &cfile.cinfo);
+ epan_dissect_fill_in_columns(&edt, TRUE, TRUE);
+
+ if ((cfile.cinfo.col_custom_occurrence[column]) ||
+ (strchr (cfile.cinfo.col_expr.col_expr_val[column], ',') == NULL))
+ {
+ /* Only construct the filter when a single occurrence is displayed
+ * otherwise we might end up with a filter like "ip.proto==1,6".
+ *
+ * Or do we want to be able to filter on multiple occurrences so that
+ * the filter might be calculated as "ip.proto==1 && ip.proto==6"
+ * instead?
+ */
+ if (strlen(cfile.cinfo.col_expr.col_expr[column]) != 0 &&
+ strlen(cfile.cinfo.col_expr.col_expr_val[column]) != 0) {
+ /* leak a little but safer than ep_ here */
+ if (cfile.cinfo.col_fmt[column] == COL_CUSTOM) {
+ header_field_info *hfi = proto_registrar_get_byname(cfile.cinfo.col_custom_field[column]);
+ if (hfi->parent == -1) {
+ /* Protocol only */
+ buf = se_strdup(cfile.cinfo.col_expr.col_expr[column]);
+ } else if (hfi->type == FT_STRING) {
+ /* Custom string, add quotes */
+ buf = se_strdup_printf("%s == \"%s\"", cfile.cinfo.col_expr.col_expr[column],
+ cfile.cinfo.col_expr.col_expr_val[column]);
+ }
+ }
+ if (buf == NULL) {
+ buf = se_strdup_printf("%s == %s", cfile.cinfo.col_expr.col_expr[column],
+ cfile.cinfo.col_expr.col_expr_val[column]);
+ }
+ }
+ }
+
+ epan_dissect_cleanup(&edt);
+ }
+
+ return buf;
+}
+
+void
+match_selected_plist_cb(GtkWidget *w _U_, gpointer data, MATCH_SELECTED_E action)
+{
+ match_selected_cb_do(data,
+ action,
+ get_filter_from_packet_list_row_and_column(data));
+}
+
+/* This function allows users to right click in the details window and copy the text
+ * information to the operating systems clipboard.
+ *
+ * We first check to see if a string representation is setup in the tree and then
+ * read the string. If not available then we try to grab the value. If all else
+ * fails we display a message to the user to indicate the copy could not be completed.
+ */
+void
+copy_selected_plist_cb(GtkWidget *w _U_, gpointer data _U_, COPY_SELECTED_E action)
+{
+ GString *gtk_text_str = g_string_new("");
+ char labelstring[256];
+ char *stringpointer = labelstring;
+
+ switch(action)
+ {
+ case COPY_SELECTED_DESCRIPTION:
+ if (cfile.finfo_selected->rep &&
+ strlen (cfile.finfo_selected->rep->representation) > 0) {
+ g_string_append(gtk_text_str, cfile.finfo_selected->rep->representation);
+ }
+ break;
+ case COPY_SELECTED_FIELDNAME:
+ if (cfile.finfo_selected->hfinfo->abbrev != 0) {
+ g_string_append(gtk_text_str, cfile.finfo_selected->hfinfo->abbrev);
+ }
+ break;
+ case COPY_SELECTED_VALUE:
+ if (cfile.edt !=0 ) {
+ g_string_append(gtk_text_str,
+ get_node_field_value(cfile.finfo_selected, cfile.edt));
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (gtk_text_str->len == 0) {
+ /* If no representation then... Try to read the value */
+ proto_item_fill_label(cfile.finfo_selected, stringpointer);
+ g_string_append(gtk_text_str, stringpointer);
+ }
+
+ if (gtk_text_str->len == 0) {
+ /* Could not get item so display error msg */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not acquire information to copy, try expanding or choosing another item");
+ } else {
+ /* Copy string to clipboard */
+ copy_to_clipboard(gtk_text_str);
+ }
+ g_string_free(gtk_text_str, TRUE); /* Free the memory */
+}
+
+
+/* mark as reference time frame */
+void
+set_frame_reftime(gboolean set, frame_data *frame, gint row) {
+ if (row == -1)
+ return;
+ if (set) {
+ frame->flags.ref_time=1;
+ cfile.ref_time_count++;
+ } else {
+ frame->flags.ref_time=0;
+ cfile.ref_time_count--;
+ }
+ cf_reftime_packets(&cfile);
+ if (!frame->flags.ref_time && !frame->flags.passed_dfilter) {
+ new_packet_list_freeze();
+ cfile.displayed_count--;
+ new_packet_list_recreate_visible_rows();
+ new_packet_list_thaw();
+ }
+ new_packet_list_queue_draw();
+}
+
+
+static void reftime_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
+{
+ switch(btn) {
+ case(ESD_BTN_YES):
+ timestamp_set_type(TS_RELATIVE);
+ recent.gui_time_format = TS_RELATIVE;
+ cf_timestamp_auto_precision(&cfile);
+ new_packet_list_queue_draw();
+ break;
+ case(ESD_BTN_NO):
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (cfile.current_frame) {
+ set_frame_reftime(!cfile.current_frame->flags.ref_time,
+ cfile.current_frame, cfile.current_row);
+ }
+}
+
+
+void
+reftime_frame_cb(GtkWidget *w _U_, gpointer data _U_, REFTIME_ACTION_E action)
+{
+ static GtkWidget *reftime_dialog = NULL;
+
+ switch(action){
+ case REFTIME_TOGGLE:
+ if (cfile.current_frame) {
+ if(recent.gui_time_format != TS_RELATIVE && cfile.current_frame->flags.ref_time==0) {
+ reftime_dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_YES_NO,
+ "%sSwitch to the appropriate Time Display Format?%s\n\n"
+ "Time References don't work well with the currently selected Time Display Format.\n\n"
+ "Do you want to switch to \"Seconds Since Beginning of Capture\" now?",
+ simple_dialog_primary_start(), simple_dialog_primary_end());
+ simple_dialog_set_cb(reftime_dialog, reftime_answered_cb, NULL);
+ } else {
+ set_frame_reftime(!cfile.current_frame->flags.ref_time,
+ cfile.current_frame, cfile.current_row);
+ }
+ }
+ break;
+ case REFTIME_FIND_NEXT:
+ cf_find_packet_time_reference(&cfile, SD_FORWARD);
+ break;
+ case REFTIME_FIND_PREV:
+ cf_find_packet_time_reference(&cfile, SD_BACKWARD);
+ break;
+ }
+}
+
+void
+find_next_mark_cb(GtkWidget *w _U_, gpointer data _U_, int action _U_)
+{
+ cf_find_packet_marked(&cfile, SD_FORWARD);
+}
+
+void
+find_prev_mark_cb(GtkWidget *w _U_, gpointer data _U_, int action _U_)
+{
+ cf_find_packet_marked(&cfile, SD_BACKWARD);
+}
+
+static void
+tree_view_selection_changed_cb(GtkTreeSelection *sel, gpointer user_data _U_)
+{
+ field_info *finfo;
+ gchar len_str[2+10+1+5+1]; /* ", {N} bytes\0",
+ N < 4294967296 */
+ gboolean has_blurb = FALSE;
+ guint length = 0, byte_len;
+ GtkWidget *byte_view;
+ const guint8 *byte_data;
+ gint finfo_length;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ /* if nothing is selected */
+ if (!gtk_tree_selection_get_selected(sel, &model, &iter))
+ {
+ /*
+ * Which byte view is displaying the current protocol tree
+ * row's data?
+ */
+ byte_view = get_notebook_bv_ptr(byte_nb_ptr_gbl);
+ if (byte_view == NULL)
+ return; /* none */
+
+ byte_data = get_byte_view_data_and_length(byte_view, &byte_len);
+ if (byte_data == NULL)
+ return; /* none */
+
+ cf_unselect_field(&cfile);
+ packet_hex_print(byte_view, byte_data,
+ cfile.current_frame, NULL, byte_len);
+ proto_help_menu_modify(sel, &cfile);
+ return;
+ }
+ gtk_tree_model_get(model, &iter, 1, &finfo, -1);
+ if (!finfo) return;
+
+ set_notebook_page(byte_nb_ptr_gbl, finfo->ds_tvb);
+
+ byte_view = get_notebook_bv_ptr(byte_nb_ptr_gbl);
+ byte_data = get_byte_view_data_and_length(byte_view, &byte_len);
+ g_assert(byte_data != NULL);
+
+ cfile.finfo_selected = finfo;
+ set_menus_for_selected_tree_row(&cfile);
+
+ if (finfo->hfinfo) {
+ if (finfo->hfinfo->blurb != NULL &&
+ finfo->hfinfo->blurb[0] != '\0') {
+ has_blurb = TRUE;
+ length = (guint) strlen(finfo->hfinfo->blurb);
+ } else {
+ length = (guint) strlen(finfo->hfinfo->name);
+ }
+ finfo_length = finfo->length + finfo->appendix_length;
+
+ if (finfo_length == 0) {
+ len_str[0] = '\0';
+ } else if (finfo_length == 1) {
+ g_strlcpy (len_str, ", 1 byte", sizeof len_str);
+ } else {
+ g_snprintf (len_str, sizeof len_str, ", %d bytes", finfo_length);
+ }
+ statusbar_pop_field_msg(); /* get rid of current help msg */
+ if (length) {
+ statusbar_push_field_msg(" %s (%s)%s",
+ (has_blurb) ? finfo->hfinfo->blurb : finfo->hfinfo->name,
+ finfo->hfinfo->abbrev, len_str);
+ } else {
+ /*
+ * Don't show anything if the field name is zero-length;
+ * the pseudo-field for "proto_tree_add_text()" is such
+ * a field, and we don't want "Text (text)" showing up
+ * on the status line if you've selected such a field.
+ *
+ * XXX - there are zero-length fields for which we *do*
+ * want to show the field name.
+ *
+ * XXX - perhaps the name and abbrev field should be null
+ * pointers rather than null strings for that pseudo-field,
+ * but we'd have to add checks for null pointers in some
+ * places if we did that.
+ *
+ * Or perhaps protocol tree items added with
+ * "proto_tree_add_text()" should have -1 as the field index,
+ * with no pseudo-field being used, but that might also
+ * require special checks for -1 to be added.
+ */
+ statusbar_push_field_msg("%s", "");
+ }
+ }
+ packet_hex_print(byte_view, byte_data, cfile.current_frame, finfo,
+ byte_len);
+ proto_help_menu_modify(sel, &cfile);
+}
+
+void collapse_all_cb(GtkWidget *widget _U_, gpointer data _U_) {
+ if (cfile.edt->tree)
+ collapse_all_tree(cfile.edt->tree, tree_view_gbl);
+}
+
+void expand_all_cb(GtkWidget *widget _U_, gpointer data _U_) {
+ if (cfile.edt->tree)
+ expand_all_tree(cfile.edt->tree, tree_view_gbl);
+}
+
+void apply_as_custom_column_cb (GtkWidget *widget _U_, gpointer data _U_)
+{
+ if (cfile.finfo_selected) {
+ column_prefs_add_custom(COL_CUSTOM, cfile.finfo_selected->hfinfo->name,
+ cfile.finfo_selected->hfinfo->abbrev,0);
+ /* Recreate the packet list according to new preferences */
+ new_packet_list_recreate ();
+ if (!prefs.gui_use_pref_save) {
+ prefs_main_write();
+ }
+ cfile.cinfo.columns_changed = FALSE; /* Reset value */
+ }
+}
+
+void expand_tree_cb(GtkWidget *widget _U_, gpointer data _U_) {
+ GtkTreePath *path;
+
+ path = tree_find_by_field_info(GTK_TREE_VIEW(tree_view_gbl), cfile.finfo_selected);
+ if(path) {
+ /* the mouse position is at an entry, expand that one */
+ gtk_tree_view_expand_row(GTK_TREE_VIEW(tree_view_gbl), path, TRUE);
+ gtk_tree_path_free(path);
+ }
+}
+
+void resolve_name_cb(GtkWidget *widget _U_, gpointer data _U_) {
+ if (cfile.edt->tree) {
+ guint32 tmp = gbl_resolv_flags;
+ gbl_resolv_flags = RESOLV_ALL;
+ proto_tree_draw(cfile.edt->tree, tree_view_gbl);
+ gbl_resolv_flags = tmp;
+ }
+}
+
+static void
+main_set_for_capture_file(gboolean have_capture_file_in)
+{
+ have_capture_file = have_capture_file_in;
+
+ main_widgets_show_or_hide();
+}
+
+gboolean
+main_do_quit(void)
+{
+ /* get the current geometry, before writing it to disk */
+ main_save_window_geometry(top_level);
+
+ /* write user's recent file to disk
+ * It is no problem to write this file, even if we do not quit */
+ write_profile_recent();
+ write_recent();
+
+ /* XXX - should we check whether the capture file is an
+ unsaved temporary file for a live capture and, if so,
+ pop up a "do you want to exit without saving the capture
+ file?" dialog, and then just return, leaving said dialog
+ box to forcibly quit if the user clicks "OK"?
+
+ If so, note that this should be done in a subroutine that
+ returns TRUE if we do so, and FALSE otherwise, and if it
+ returns TRUE we should return TRUE without nuking anything.
+
+ Note that, if we do that, we might also want to check if
+ an "Update list of packets in real time" capture is in
+ progress and, if so, ask whether they want to terminate
+ the capture and discard it, and return TRUE, before nuking
+ any child capture, if they say they don't want to do so. */
+
+#ifdef HAVE_LIBPCAP
+ /* Nuke any child capture in progress. */
+ capture_kill_child(&global_capture_opts);
+#endif
+
+ /* Are we in the middle of reading a capture? */
+ if (cfile.state == FILE_READ_IN_PROGRESS) {
+ /* Yes, so we can't just close the file and quit, as
+ that may yank the rug out from under the read in
+ progress; instead, just set the state to
+ "FILE_READ_ABORTED" and return - the code doing the read
+ will check for that and, if it sees that, will clean
+ up and quit. */
+ cfile.state = FILE_READ_ABORTED;
+
+ /* Say that the window should *not* be deleted;
+ that'll be done by the code that cleans up. */
+ return TRUE;
+ } else {
+ /* Close any capture file we have open; on some OSes, you
+ can't unlink a temporary capture file if you have it
+ open.
+ "cf_close()" will unlink it after closing it if
+ it's a temporary file.
+
+ We do this here, rather than after the main loop returns,
+ as, after the main loop returns, the main window may have
+ been destroyed (if this is called due to a "destroy"
+ even on the main window rather than due to the user
+ selecting a menu item), and there may be a crash
+ or other problem when "cf_close()" tries to
+ clean up stuff in the main window.
+
+ XXX - is there a better place to put this?
+ Or should we have a routine that *just* closes the
+ capture file, and doesn't do anything with the UI,
+ which we'd call here, and another routine that
+ calls that routine and also cleans up the UI, which
+ we'd call elsewhere? */
+ cf_close(&cfile);
+
+ /* Exit by leaving the main loop, so that any quit functions
+ we registered get called. */
+ gtk_main_quit();
+
+ /* Say that the window should be deleted. */
+ return FALSE;
+ }
+}
+
+static gboolean
+main_window_delete_event_cb(GtkWidget *widget _U_, GdkEvent *event _U_, gpointer data _U_)
+{
+ gpointer dialog;
+
+ if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
+ gtk_window_present(GTK_WINDOW(top_level));
+ /* user didn't saved his current file, ask him */
+ dialog = simple_dialog(ESD_TYPE_CONFIRMATION,
+ ((cfile.state == FILE_READ_IN_PROGRESS) ? ESD_BTNS_QUIT_DONTSAVE_CANCEL : ESD_BTNS_SAVE_QUIT_DONTSAVE_CANCEL),
+ "%sSave capture file before program quit?%s\n\n"
+ "If you quit the program without saving, your capture data will be discarded.",
+ simple_dialog_primary_start(), simple_dialog_primary_end());
+ simple_dialog_set_cb(dialog, file_quit_answered_cb, NULL);
+ return TRUE;
+ } else {
+ /* unchanged file, just exit */
+ /* "main_do_quit()" indicates whether the main window should be deleted. */
+ return main_do_quit();
+ }
+}
+
+
+static void
+main_pane_load_window_geometry(void)
+{
+ if (recent.has_gui_geometry_main_upper_pane && recent.gui_geometry_main_upper_pane)
+ gtk_paned_set_position(GTK_PANED(main_first_pane), recent.gui_geometry_main_upper_pane);
+ if (recent.has_gui_geometry_main_lower_pane && recent.gui_geometry_main_lower_pane) {
+ gtk_paned_set_position(GTK_PANED(main_second_pane), recent.gui_geometry_main_lower_pane);
+ }
+}
+
+
+static void
+main_load_window_geometry(GtkWidget *widget)
+{
+ window_geometry_t geom;
+
+ geom.set_pos = prefs.gui_geometry_save_position;
+ geom.x = recent.gui_geometry_main_x;
+ geom.y = recent.gui_geometry_main_y;
+ geom.set_size = prefs.gui_geometry_save_size;
+ if (recent.gui_geometry_main_width > 0 &&
+ recent.gui_geometry_main_height > 0) {
+ geom.width = recent.gui_geometry_main_width;
+ geom.height = recent.gui_geometry_main_height;
+ geom.set_maximized = prefs.gui_geometry_save_maximized;
+ } else {
+ /* We assume this means the width and height weren't set in
+ the "recent" file (or that there is no "recent" file),
+ and weren't set to a default value, so we don't set the
+ size. (The "recent" file code rejects non-positive width
+ and height values.) */
+ geom.set_size = FALSE;
+ }
+ geom.maximized = recent.gui_geometry_main_maximized;
+
+ window_set_geometry(widget, &geom);
+
+ main_pane_load_window_geometry();
+ statusbar_load_window_geometry();
+}
+
+
+static void
+main_save_window_geometry(GtkWidget *widget)
+{
+ window_geometry_t geom;
+
+ window_get_geometry(widget, &geom);
+
+ if (prefs.gui_geometry_save_position) {
+ recent.gui_geometry_main_x = geom.x;
+ recent.gui_geometry_main_y = geom.y;
+ }
+
+ if (prefs.gui_geometry_save_size) {
+ recent.gui_geometry_main_width = geom.width;
+ recent.gui_geometry_main_height = geom.height;
+ }
+
+ if(prefs.gui_geometry_save_maximized) {
+ recent.gui_geometry_main_maximized = geom.maximized;
+ }
+
+ recent.gui_geometry_main_upper_pane = gtk_paned_get_position(GTK_PANED(main_first_pane));
+ recent.gui_geometry_main_lower_pane = gtk_paned_get_position(GTK_PANED(main_second_pane));
+ statusbar_save_window_geometry();
+}
+
+static void file_quit_answered_cb(gpointer dialog _U_, gint btn, gpointer data _U_)
+{
+ switch(btn) {
+ case(ESD_BTN_SAVE):
+ /* save file first */
+ file_save_as_cmd(after_save_exit, NULL, FALSE);
+ break;
+ case(ESD_BTN_QUIT_DONT_SAVE):
+ main_do_quit();
+ break;
+ case(ESD_BTN_CANCEL):
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+void
+file_quit_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ gpointer dialog;
+
+ if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
+ /* user didn't saved his current file, ask him */
+ dialog = simple_dialog(ESD_TYPE_CONFIRMATION,
+ ((cfile.state == FILE_READ_IN_PROGRESS) ? ESD_BTNS_QUIT_DONTSAVE_CANCEL : ESD_BTNS_SAVE_QUIT_DONTSAVE_CANCEL),
+ "%sSave capture file before program quit?%s\n\n"
+ "If you quit the program without saving, your capture data will be discarded.",
+ simple_dialog_primary_start(), simple_dialog_primary_end());
+ simple_dialog_set_cb(dialog, file_quit_answered_cb, NULL);
+ } else {
+ /* unchanged file, just exit */
+ main_do_quit();
+ }
+}
+
+static void
+print_usage(gboolean print_ver) {
+
+ FILE *output;
+
+#ifdef _WIN32
+ create_console();
+#endif
+
+ if (print_ver) {
+ output = stdout;
+ fprintf(output, "Wireshark " VERSION "%s\n"
+ "Interactively dump and analyze network traffic.\n"
+ "See http://www.wireshark.org for more information.\n"
+ "\n"
+ "%s",
+ wireshark_svnversion, get_copyright_info());
+ } else {
+ output = stderr;
+ }
+ fprintf(output, "\n");
+ fprintf(output, "Usage: wireshark [options] ... [ <infile> ]\n");
+ fprintf(output, "\n");
+
+#ifdef HAVE_LIBPCAP
+ fprintf(output, "Capture interface:\n");
+ fprintf(output, " -i <interface> name or idx of interface (def: first non-loopback)\n");
+ fprintf(output, " -f <capture filter> packet filter in libpcap filter syntax\n");
+ fprintf(output, " -s <snaplen> packet snapshot length (def: 65535)\n");
+ fprintf(output, " -p don't capture in promiscuous mode\n");
+ fprintf(output, " -k start capturing immediately (def: do nothing)\n");
+ fprintf(output, " -S update packet display when new packets are captured\n");
+ fprintf(output, " -l turn on automatic scrolling while -S is in use\n");
+#ifdef HAVE_PCAP_CREATE
+ fprintf(output, " -I capture in monitor mode, if available\n");
+#endif
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ fprintf(output, " -B <buffer size> size of kernel buffer (def: 1MB)\n");
+#endif
+ fprintf(output, " -y <link type> link layer type (def: first appropriate)\n");
+ fprintf(output, " -D print list of interfaces and exit\n");
+ fprintf(output, " -L print list of link-layer types of iface and exit\n");
+ fprintf(output, "\n");
+ fprintf(output, "Capture stop conditions:\n");
+ fprintf(output, " -c <packet count> stop after n packets (def: infinite)\n");
+ fprintf(output, " -a <autostop cond.> ... duration:NUM - stop after NUM seconds\n");
+ fprintf(output, " filesize:NUM - stop this file after NUM KB\n");
+ fprintf(output, " files:NUM - stop after NUM files\n");
+ /*fprintf(output, "\n");*/
+ fprintf(output, "Capture output:\n");
+ fprintf(output, " -b <ringbuffer opt.> ... duration:NUM - switch to next file after NUM secs\n");
+ fprintf(output, " filesize:NUM - switch to next file after NUM KB\n");
+ fprintf(output, " files:NUM - ringbuffer: replace after NUM files\n");
+#endif /* HAVE_LIBPCAP */
+
+ /*fprintf(output, "\n");*/
+ fprintf(output, "Input file:\n");
+ fprintf(output, " -r <infile> set the filename to read from (no pipes or stdin!)\n");
+
+ fprintf(output, "\n");
+ fprintf(output, "Processing:\n");
+ fprintf(output, " -R <read filter> packet filter in Wireshark display filter syntax\n");
+ fprintf(output, " -n disable all name resolutions (def: all enabled)\n");
+ fprintf(output, " -N <name resolve flags> enable specific name resolution(s): \"mntC\"\n");
+
+ fprintf(output, "\n");
+ fprintf(output, "User interface:\n");
+ fprintf(output, " -C <config profile> start with specified configuration profile\n");
+ fprintf(output, " -d <display filter> start with the given display filter\n");
+ fprintf(output, " -g <packet number> go to specified packet number after \"-r\"\n");
+ fprintf(output, " -J <jump filter> jump to the first packet matching the (display)\n");
+ fprintf(output, " filter\n");
+ fprintf(output, " -j search backwards for a matching packet after \"-J\"\n");
+ fprintf(output, " -m <font> set the font name used for most text\n");
+ fprintf(output, " -t ad|a|r|d|dd|e output format of time stamps (def: r: rel. to first)\n");
+ fprintf(output, " -u s|hms output format of seconds (def: s: seconds)\n");
+ fprintf(output, " -X <key>:<value> eXtension options, see man page for details\n");
+ fprintf(output, " -z <statistics> show various statistics, see man page for details\n");
+
+ fprintf(output, "\n");
+ fprintf(output, "Output:\n");
+ fprintf(output, " -w <outfile|-> set the output filename (or '-' for stdout)\n");
+
+ fprintf(output, "\n");
+ fprintf(output, "Miscellaneous:\n");
+ fprintf(output, " -h display this help and exit\n");
+ fprintf(output, " -v display version info and exit\n");
+ fprintf(output, " -P <key>:<path> persconf:path - personal configuration files\n");
+ fprintf(output, " persdata:path - personal data files\n");
+ fprintf(output, " -o <name>:<value> ... override preference or recent setting\n");
+ fprintf(output, " -K <keytab> keytab file to use for kerberos decryption\n");
+#ifndef _WIN32
+ fprintf(output, " --display=DISPLAY X display to use\n");
+#endif
+
+#ifdef _WIN32
+ destroy_console();
+#endif
+}
+
+static void
+show_version(void)
+{
+#ifdef _WIN32
+ create_console();
+#endif
+
+ printf(PACKAGE " " VERSION "%s\n"
+ "\n"
+ "%s"
+ "\n"
+ "%s"
+ "\n"
+ "%s",
+ wireshark_svnversion, get_copyright_info(), comp_info_str->str,
+ runtime_info_str->str);
+
+#ifdef _WIN32
+ destroy_console();
+#endif
+}
+
+/*
+ * Print to the standard error. On Windows, create a console for the
+ * standard error to show up on, if necessary.
+ * XXX - pop this up in a window of some sort on UNIX+X11 if the controlling
+ * terminal isn't the standard error?
+ */
+void
+vfprintf_stderr(const char *fmt, va_list ap)
+{
+#ifdef _WIN32
+ create_console();
+#endif
+ vfprintf(stderr, fmt, ap);
+}
+
+void
+fprintf_stderr(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf_stderr(fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Report an error in command-line arguments.
+ * Creates a console on Windows.
+ */
+void
+cmdarg_err(const char *fmt, ...)
+{
+ va_list ap;
+
+ fprintf_stderr("wireshark: ");
+ va_start(ap, fmt);
+ vfprintf_stderr(fmt, ap);
+ va_end(ap);
+ fprintf_stderr("\n");
+}
+
+/*
+ * Report additional information for an error in command-line arguments.
+ * Creates a console on Windows.
+ * XXX - pop this up in a window of some sort on UNIX+X11 if the controlling
+ * terminal isn't the standard error?
+ */
+void
+cmdarg_err_cont(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf_stderr(fmt, ap);
+ fprintf_stderr("\n");
+ va_end(ap);
+}
+
+/*
+ Once every 3 seconds we get a callback here which we use to update
+ the tap extensions.
+ */
+static gboolean
+tap_update_cb(gpointer data _U_)
+{
+ draw_tap_listeners(FALSE);
+ return TRUE;
+}
+
+/* Restart the tap update display timer with new configured interval */
+void reset_tap_update_timer(void)
+{
+ g_source_remove(tap_update_timer_id);
+ tap_update_timer_id = g_timeout_add(prefs.tap_update_interval, tap_update_cb, NULL);
+}
+
+void
+protect_thread_critical_region(void)
+{
+ /* Threading support for TAP:s removed
+ * http://www.wireshark.org/lists/wireshark-dev/200611/msg00199.html
+ * See the commit for removed code:
+ * http://anonsvn.wireshark.org/viewvc/viewvc.cgi?view=rev&revision=35027
+ */
+}
+void
+unprotect_thread_critical_region(void)
+{
+ /* Threading support for TAP:s removed
+ * http://www.wireshark.org/lists/wireshark-dev/200611/msg00199.html
+ */
+
+}
+
+/*
+ * Periodically process outstanding hostname lookups. If we have new items,
+ * redraw the packet list and tree view.
+ */
+
+static gboolean
+resolv_update_cb(gpointer data _U_)
+{
+ /* Anything new show up? */
+ if (host_name_lookup_process(NULL)) {
+ if (gtk_widget_get_window(pkt_scrollw))
+ gdk_window_invalidate_rect(gtk_widget_get_window(pkt_scrollw), NULL, TRUE);
+ if (gtk_widget_get_window(tv_scrollw))
+ gdk_window_invalidate_rect(gtk_widget_get_window(tv_scrollw), NULL, TRUE);
+ }
+
+ /* Always check. Even if we don't do async lookups we could still get
+ passive updates, e.g. from DNS packets. */
+ return TRUE;
+}
+
+
+/* Set main_window_name and it's icon title to the capture filename */
+static void
+set_display_filename(capture_file *cf)
+{
+ gchar *window_name;
+
+ if (cf->filename) {
+ window_name = g_strdup_printf("%s", cf_get_display_name(cf));
+ set_main_window_name(window_name);
+ g_free(window_name);
+ } else {
+ set_main_window_name("The Wireshark Network Analyzer");
+ }
+}
+
+static GtkWidget *close_dlg = NULL;
+
+static void
+priv_warning_dialog_cb(gpointer dialog, gint btn _U_, gpointer data _U_)
+{
+ recent.privs_warn_if_elevated = !simple_dialog_check_get(dialog);
+}
+
+#ifdef _WIN32
+static void
+npf_warning_dialog_cb(gpointer dialog, gint btn _U_, gpointer data _U_)
+{
+ recent.privs_warn_if_no_npf = !simple_dialog_check_get(dialog);
+}
+#endif
+
+static void
+main_cf_cb_file_closing(capture_file *cf)
+{
+
+ /* if we have more than 10000 packets, show a splash screen while closing */
+ /* XXX - don't know a better way to decide whether to show or not,
+ * as most of the time is spend in a single eth_clist_clear function,
+ * so we can't use a progress bar here! */
+ if(cf->count > 10000) {
+ close_dlg = simple_dialog(ESD_TYPE_STOP, ESD_BTN_NONE,
+ "%sClosing file!%s\n\nPlease wait ...",
+ simple_dialog_primary_start(),
+ simple_dialog_primary_end());
+ gtk_window_set_position(GTK_WINDOW(close_dlg), GTK_WIN_POS_CENTER_ON_PARENT);
+ }
+
+ /* Destroy all windows, which refer to the
+ capture file we're closing. */
+ destroy_packet_wins();
+ file_save_as_destroy();
+
+ /* Restore the standard title bar message. */
+ set_main_window_name("The Wireshark Network Analyzer");
+
+ /* Disable all menu items that make sense only if you have a capture. */
+ set_menus_for_capture_file(NULL);
+ set_menus_for_captured_packets(FALSE);
+ set_menus_for_selected_packet(cf);
+ set_menus_for_capture_in_progress(FALSE);
+ set_capture_if_dialog_for_capture_in_progress(FALSE);
+ set_menus_for_selected_tree_row(cf);
+
+ /* Set up main window for no capture file. */
+ main_set_for_capture_file(FALSE);
+
+ main_window_update();
+}
+
+static void
+main_cf_cb_file_closed(capture_file *cf _U_)
+{
+ if(close_dlg != NULL) {
+ splash_destroy(close_dlg);
+ close_dlg = NULL;
+ }
+}
+
+
+static void
+main_cf_cb_file_read_started(capture_file *cf _U_)
+{
+ tap_param_dlg_update();
+
+ /* Set up main window for a capture file. */
+ main_set_for_capture_file(TRUE);
+}
+
+static void
+main_cf_cb_file_read_finished(capture_file *cf)
+{
+ gchar *dir_path;
+
+ if (!cf->is_tempfile && cf->filename) {
+ /* Add this filename to the list of recent files in the "Recent Files" submenu */
+ add_menu_recent_capture_file(cf->filename);
+
+ /* Remember folder for next Open dialog and save it in recent */
+ dir_path = get_dirname(g_strdup(cf->filename));
+ set_last_open_dir(dir_path);
+ g_free(dir_path);
+ }
+ set_display_filename(cf);
+
+ /* Enable menu items that make sense if you have a capture file you've
+ finished reading. */
+ set_menus_for_capture_file(cf);
+
+ /* Enable menu items that make sense if you have some captured packets. */
+ set_menus_for_captured_packets(TRUE);
+}
+
+#ifdef HAVE_LIBPCAP
+static GList *icon_list_create(
+ const char **icon16_xpm,
+ const char **icon32_xpm,
+ const char **icon48_xpm,
+ const char **icon64_xpm)
+{
+ GList *icon_list = NULL;
+ GdkPixbuf * pixbuf16;
+ GdkPixbuf * pixbuf32;
+ GdkPixbuf * pixbuf48;
+ GdkPixbuf * pixbuf64;
+
+
+ if(icon16_xpm != NULL) {
+ pixbuf16 = gdk_pixbuf_new_from_xpm_data(icon16_xpm);
+ g_assert(pixbuf16);
+ icon_list = g_list_append(icon_list, pixbuf16);
+ }
+
+ if(icon32_xpm != NULL) {
+ pixbuf32 = gdk_pixbuf_new_from_xpm_data(icon32_xpm);
+ g_assert(pixbuf32);
+ icon_list = g_list_append(icon_list, pixbuf32);
+ }
+
+ if(icon48_xpm != NULL) {
+ pixbuf48 = gdk_pixbuf_new_from_xpm_data(icon48_xpm);
+ g_assert(pixbuf48);
+ icon_list = g_list_append(icon_list, pixbuf48);
+ }
+
+ if(icon64_xpm != NULL) {
+ pixbuf64 = gdk_pixbuf_new_from_xpm_data(icon64_xpm);
+ g_assert(pixbuf64);
+ icon_list = g_list_append(icon_list, pixbuf64);
+ }
+
+ return icon_list;
+}
+
+static void
+main_capture_set_main_window_title(capture_options *capture_opts)
+{
+ GString *title = g_string_new("");
+
+ g_string_append(title, "Capturing ");
+ g_string_append_printf(title, "from %s ", cf_get_tempfile_source(capture_opts->cf));
+ set_main_window_name(title->str);
+ g_string_free(title, TRUE);
+}
+
+static void
+main_capture_cb_capture_prepared(capture_options *capture_opts)
+{
+ static GList *icon_list = NULL;
+
+ main_capture_set_main_window_title(capture_opts);
+
+ if(icon_list == NULL) {
+ icon_list = icon_list_create(wsiconcap16_xpm, wsiconcap32_xpm, wsiconcap48_xpm, NULL);
+ }
+ gtk_window_set_icon_list(GTK_WINDOW(top_level), icon_list);
+
+ /* Disable menu items that make no sense if you're currently running
+ a capture. */
+ set_menus_for_capture_in_progress(TRUE);
+ set_capture_if_dialog_for_capture_in_progress(TRUE);
+
+ /* Don't set up main window for a capture file. */
+ main_set_for_capture_file(FALSE);
+}
+
+static void
+main_capture_cb_capture_update_started(capture_options *capture_opts)
+{
+ /* We've done this in "prepared" above, but it will be cleared while
+ switching to the next multiple file. */
+ main_capture_set_main_window_title(capture_opts);
+
+ set_menus_for_capture_in_progress(TRUE);
+ set_capture_if_dialog_for_capture_in_progress(TRUE);
+
+ /* Enable menu items that make sense if you have some captured
+ packets (yes, I know, we don't have any *yet*). */
+ set_menus_for_captured_packets(TRUE);
+
+ /* Set up main window for a capture file. */
+ main_set_for_capture_file(TRUE);
+}
+
+static void
+main_capture_cb_capture_update_finished(capture_options *capture_opts)
+{
+ capture_file *cf = capture_opts->cf;
+ static GList *icon_list = NULL;
+
+ if (!cf->is_tempfile && cf->filename) {
+ /* Add this filename to the list of recent files in the "Recent Files" submenu */
+ add_menu_recent_capture_file(cf->filename);
+ }
+ set_display_filename(cf);
+
+ /* Enable menu items that make sense if you're not currently running
+ a capture. */
+ set_menus_for_capture_in_progress(FALSE);
+ set_capture_if_dialog_for_capture_in_progress(FALSE);
+
+ /* Enable menu items that make sense if you have a capture file
+ you've finished reading. */
+ set_menus_for_capture_file(cf);
+
+ /* Set up main window for a capture file. */
+ main_set_for_capture_file(TRUE);
+
+ if(icon_list == NULL) {
+ icon_list = icon_list_create(wsicon16_xpm, wsicon32_xpm, wsicon48_xpm, wsicon64_xpm);
+ }
+ gtk_window_set_icon_list(GTK_WINDOW(top_level), icon_list);
+
+ if(global_capture_opts.quit_after_cap) {
+ /* command line asked us to quit after the capture */
+ /* don't pop up a dialog to ask for unsaved files etc. */
+ main_do_quit();
+ }
+}
+
+static void
+main_capture_cb_capture_fixed_started(capture_options *capture_opts _U_)
+{
+ /* Don't set up main window for a capture file. */
+ main_set_for_capture_file(FALSE);
+}
+
+static void
+main_capture_cb_capture_fixed_finished(capture_options *capture_opts _U_)
+{
+#if 0
+ capture_file *cf = capture_opts->cf;
+#endif
+ static GList *icon_list = NULL;
+
+ /*set_display_filename(cf);*/
+
+ /* Enable menu items that make sense if you're not currently running
+ a capture. */
+ set_menus_for_capture_in_progress(FALSE);
+ set_capture_if_dialog_for_capture_in_progress(FALSE);
+
+ /* Restore the standard title bar message */
+ /* (just in case we have trouble opening the capture file). */
+ set_main_window_name("The Wireshark Network Analyzer");
+
+ if(icon_list == NULL) {
+ icon_list = icon_list_create(wsicon16_xpm, wsicon32_xpm, wsicon48_xpm, wsicon64_xpm);
+ }
+ gtk_window_set_icon_list(GTK_WINDOW(top_level), icon_list);
+
+ /* We don't have loaded the capture file, this will be done later.
+ * For now we still have simply a blank screen. */
+
+ if(global_capture_opts.quit_after_cap) {
+ /* command line asked us to quit after the capture */
+ /* don't pop up a dialog to ask for unsaved files etc. */
+ main_do_quit();
+ }
+}
+
+#endif /* HAVE_LIBPCAP */
+
+static void
+main_cf_cb_packet_selected(gpointer data)
+{
+ capture_file *cf = data;
+
+ /* Display the GUI protocol tree and packet bytes.
+ XXX - why do we dump core if we call "proto_tree_draw()"
+ before calling "add_byte_views()"? */
+ add_main_byte_views(cf->edt);
+ main_proto_tree_draw(cf->edt->tree);
+
+ /* Note: Both string and hex value searches in the packet data produce a non-zero
+ search_pos if successful */
+ if(cf->search_in_progress &&
+ (cf->search_pos != 0 || (cf->string && cf->decode_data))) {
+ highlight_field(cf->edt->tvb, cf->search_pos,
+ (GtkTreeView *)tree_view_gbl, cf->edt->tree);
+ }
+
+ /* A packet is selected. */
+ set_menus_for_selected_packet(cf);
+}
+
+static void
+main_cf_cb_packet_unselected(capture_file *cf)
+{
+ /* Clear out the display of that packet. */
+ clear_tree_and_hex_views();
+
+ /* No packet is selected. */
+ set_menus_for_selected_packet(cf);
+}
+
+static void
+main_cf_cb_field_unselected(capture_file *cf)
+{
+ set_menus_for_selected_tree_row(cf);
+}
+
+static void
+main_cf_cb_file_save_reload_finished(gpointer data _U_)
+{
+ set_display_filename(&cfile);
+ set_menus_for_capture_file(&cfile);
+}
+
+static void
+main_cf_callback(gint event, gpointer data, gpointer user_data _U_)
+{
+ switch(event) {
+ case(cf_cb_file_closing):
+ g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Closing");
+ main_cf_cb_file_closing(data);
+ break;
+ case(cf_cb_file_closed):
+ g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Closed");
+ main_cf_cb_file_closed(data);
+ break;
+ case(cf_cb_file_read_started):
+ g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Read started");
+ main_cf_cb_file_read_started(data);
+ break;
+ case(cf_cb_file_read_finished):
+ g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Read finished");
+ main_cf_cb_file_read_finished(data);
+ break;
+ case(cf_cb_packet_selected):
+ main_cf_cb_packet_selected(data);
+ break;
+ case(cf_cb_packet_unselected):
+ main_cf_cb_packet_unselected(data);
+ break;
+ case(cf_cb_field_unselected):
+ main_cf_cb_field_unselected(data);
+ break;
+ case(cf_cb_file_save_started):
+ g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Save started");
+ break;
+ case(cf_cb_file_save_finished):
+ g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Save finished");
+ break;
+ case(cf_cb_file_save_reload_finished):
+ g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Reload finished");
+ main_cf_cb_file_save_reload_finished(data);
+ break;
+ case(cf_cb_file_save_failed):
+ g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Save failed");
+ break;
+ default:
+ g_warning("main_cf_callback: event %u unknown", event);
+ g_assert_not_reached();
+ }
+}
+
+#ifdef HAVE_LIBPCAP
+static void
+main_capture_callback(gint event, capture_options *capture_opts, gpointer user_data _U_)
+{
+#ifdef HAVE_GTKOSXAPPLICATION
+ GtkOSXApplication *theApp;
+#endif
+ switch(event) {
+ case(capture_cb_capture_prepared):
+ g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: capture prepared");
+ main_capture_cb_capture_prepared(capture_opts);
+ break;
+ case(capture_cb_capture_update_started):
+ g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: capture update started");
+ main_capture_cb_capture_update_started(capture_opts);
+#ifdef HAVE_GTKOSXAPPLICATION
+ theApp = g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
+ gtk_osxapplication_set_dock_icon_pixbuf(theApp,gdk_pixbuf_new_from_xpm_data(wsiconcap48_xpm));
+#endif
+ break;
+ case(capture_cb_capture_update_continue):
+ /*g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: capture update continue");*/
+ break;
+ case(capture_cb_capture_update_finished):
+ g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: capture update finished");
+ main_capture_cb_capture_update_finished(capture_opts);
+ break;
+ case(capture_cb_capture_fixed_started):
+ g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: capture fixed started");
+ main_capture_cb_capture_fixed_started(capture_opts);
+ break;
+ case(capture_cb_capture_fixed_continue):
+ g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: capture fixed continue");
+ break;
+ case(capture_cb_capture_fixed_finished):
+ g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: capture fixed finished");
+ main_capture_cb_capture_fixed_finished(capture_opts);
+ break;
+ case(capture_cb_capture_stopping):
+ g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: capture stopping");
+ /* Beware: this state won't be called, if the capture child
+ * closes the capturing on it's own! */
+#ifdef HAVE_GTKOSXAPPLICATION
+ theApp = g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
+ gtk_osxapplication_set_dock_icon_pixbuf(theApp,gdk_pixbuf_new_from_xpm_data(wsicon64_xpm));
+#endif
+ break;
+ default:
+ g_warning("main_capture_callback: event %u unknown", event);
+ g_assert_not_reached();
+ }
+}
+#endif
+
+static void
+get_gtk_compiled_info(GString *str)
+{
+ g_string_append(str, "with ");
+ g_string_append_printf(str,
+#ifdef GTK_MAJOR_VERSION
+ "GTK+ %d.%d.%d", GTK_MAJOR_VERSION, GTK_MINOR_VERSION,
+ GTK_MICRO_VERSION);
+#else
+ "GTK+ (version unknown)");
+#endif
+ g_string_append(str, ", ");
+ /* Cairo */
+ g_string_append(str, "with Cairo ");
+ g_string_append(str, CAIRO_VERSION_STRING);
+ g_string_append(str, ", ");
+
+ /* Pango */
+ g_string_append(str, "with Pango ");
+ g_string_append(str, PANGO_VERSION_STRING);
+ g_string_append(str, ", ");
+
+
+}
+
+static void
+get_gui_compiled_info(GString *str)
+{
+ epan_get_compiled_version_info(str);
+
+ g_string_append(str, ", ");
+#ifdef HAVE_LIBPORTAUDIO
+#ifdef PORTAUDIO_API_1
+ g_string_append(str, "with PortAudio <= V18");
+#else /* PORTAUDIO_API_1 */
+ g_string_append(str, "with ");
+ g_string_append(str, Pa_GetVersionText());
+#endif /* PORTAUDIO_API_1 */
+#else /* HAVE_LIBPORTAUDIO */
+ g_string_append(str, "without PortAudio");
+#endif /* HAVE_LIBPORTAUDIO */
+
+ g_string_append(str, ", ");
+#ifdef HAVE_AIRPCAP
+ get_compiled_airpcap_version(str);
+#else
+ g_string_append(str, "without AirPcap");
+#endif
+}
+
+static void
+get_gui_runtime_info(GString *str)
+{
+ epan_get_runtime_version_info(str);
+
+#ifdef HAVE_AIRPCAP
+ g_string_append(str, ", ");
+ get_runtime_airpcap_version(str);
+#endif
+
+ if(u3_active()) {
+ g_string_append(str, ", ");
+ u3_runtime_info(str);
+ }
+}
+
+static e_prefs *
+read_configuration_files(char **gdp_path, char **dp_path)
+{
+ int gpf_open_errno, gpf_read_errno;
+ int cf_open_errno, df_open_errno;
+ int gdp_open_errno, gdp_read_errno;
+ int dp_open_errno, dp_read_errno;
+ char *gpf_path, *pf_path;
+ char *cf_path, *df_path;
+ int pf_open_errno, pf_read_errno;
+ e_prefs *prefs_p;
+
+ /* load the decode as entries of this profile */
+ load_decode_as_entries();
+
+ /* Read the preference files. */
+ prefs_p = read_prefs(&gpf_open_errno, &gpf_read_errno, &gpf_path,
+ &pf_open_errno, &pf_read_errno, &pf_path);
+
+ if (gpf_path != NULL) {
+ if (gpf_open_errno != 0) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Could not open global preferences file\n\"%s\": %s.", gpf_path,
+ g_strerror(gpf_open_errno));
+ }
+ if (gpf_read_errno != 0) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "I/O error reading global preferences file\n\"%s\": %s.", gpf_path,
+ g_strerror(gpf_read_errno));
+ }
+ }
+ if (pf_path != NULL) {
+ if (pf_open_errno != 0) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Could not open your preferences file\n\"%s\": %s.", pf_path,
+ g_strerror(pf_open_errno));
+ }
+ if (pf_read_errno != 0) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "I/O error reading your preferences file\n\"%s\": %s.", pf_path,
+ g_strerror(pf_read_errno));
+ }
+ g_free(pf_path);
+ pf_path = NULL;
+ }
+
+#ifdef _WIN32
+ /* if the user wants a console to be always there, well, we should open one for him */
+ if (prefs_p->gui_console_open == console_open_always) {
+ create_console();
+ }
+#endif
+
+ /* Read the capture filter file. */
+ read_filter_list(CFILTER_LIST, &cf_path, &cf_open_errno);
+ if (cf_path != NULL) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Could not open your capture filter file\n\"%s\": %s.", cf_path,
+ g_strerror(cf_open_errno));
+ g_free(cf_path);
+ }
+
+ /* Read the display filter file. */
+ read_filter_list(DFILTER_LIST, &df_path, &df_open_errno);
+ if (df_path != NULL) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Could not open your display filter file\n\"%s\": %s.", df_path,
+ g_strerror(df_open_errno));
+ g_free(df_path);
+ }
+
+ /* Read the disabled protocols file. */
+ read_disabled_protos_list(gdp_path, &gdp_open_errno, &gdp_read_errno,
+ dp_path, &dp_open_errno, &dp_read_errno);
+ if (*gdp_path != NULL) {
+ if (gdp_open_errno != 0) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Could not open global disabled protocols file\n\"%s\": %s.",
+ *gdp_path, g_strerror(gdp_open_errno));
+ }
+ if (gdp_read_errno != 0) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "I/O error reading global disabled protocols file\n\"%s\": %s.",
+ *gdp_path, g_strerror(gdp_read_errno));
+ }
+ g_free(*gdp_path);
+ *gdp_path = NULL;
+ }
+ if (*dp_path != NULL) {
+ if (dp_open_errno != 0) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Could not open your disabled protocols file\n\"%s\": %s.", *dp_path,
+ g_strerror(dp_open_errno));
+ }
+ if (dp_read_errno != 0) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "I/O error reading your disabled protocols file\n\"%s\": %s.", *dp_path,
+ g_strerror(dp_read_errno));
+ }
+ g_free(*dp_path);
+ *dp_path = NULL;
+ }
+
+ return prefs_p;
+}
+
+/* Check if there's something important to tell the user during startup.
+ * We want to do this *after* showing the main window so that any windows
+ * we pop up will be above the main window.
+ */
+static void
+#ifdef _WIN32
+check_and_warn_user_startup(gchar *cf_name)
+#else
+check_and_warn_user_startup(gchar *cf_name _U_)
+#endif
+{
+ gchar *cur_user, *cur_group;
+ gpointer priv_warning_dialog;
+
+ /* Tell the user not to run as root. */
+ if (running_with_special_privs() && recent.privs_warn_if_elevated) {
+ cur_user = get_cur_username();
+ cur_group = get_cur_groupname();
+ priv_warning_dialog = simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Running as user \"%s\" and group \"%s\".\n"
+ "This could be dangerous.\n\n"
+ "If you're running Wireshark this way in order to perform live capture, "
+ "you may want to be aware that there is a better way documented at\n"
+ "http://wiki.wireshark.org/CaptureSetup/CapturePrivileges", cur_user, cur_group);
+ g_free(cur_user);
+ g_free(cur_group);
+ simple_dialog_check_set(priv_warning_dialog, "Don't show this message again.");
+ simple_dialog_set_cb(priv_warning_dialog, priv_warning_dialog_cb, NULL);
+ }
+
+#ifdef _WIN32
+ /* Warn the user if npf.sys isn't loaded. */
+ if (!stdin_capture && !cf_name && !npf_sys_is_running() && recent.privs_warn_if_no_npf && get_os_major_version() >= 6) {
+ priv_warning_dialog = simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "The NPF driver isn't running. You may have trouble\n"
+ "capturing or listing interfaces.");
+ simple_dialog_check_set(priv_warning_dialog, "Don't show this message again.");
+ simple_dialog_set_cb(priv_warning_dialog, npf_warning_dialog_cb, NULL);
+ }
+#endif
+
+}
+
+
+/* And now our feature presentation... [ fade to music ] */
+int
+main(int argc, char *argv[])
+{
+ char *init_progfile_dir_error;
+ char *s;
+ int opt;
+ gboolean arg_error = FALSE;
+
+ extern int info_update_freq; /* Found in about_dlg.c. */
+ const gchar *filter;
+
+#ifdef _WIN32
+ WSADATA wsaData;
+#endif /* _WIN32 */
+
+ char *rf_path;
+ int rf_open_errno;
+ char *gdp_path, *dp_path;
+ int err;
+#ifdef HAVE_LIBPCAP
+ gboolean start_capture = FALSE;
+ gboolean list_link_layer_types = FALSE;
+ GList *if_list;
+ gchar *err_str;
+#else
+ gboolean capture_option_specified = FALSE;
+#ifdef _WIN32
+#ifdef HAVE_AIRPCAP
+ gchar *err_str;
+#endif
+#endif
+#endif
+ gint pl_size = 280, tv_size = 95, bv_size = 75;
+ gchar *rc_file, *cf_name = NULL, *rfilter = NULL, *dfilter = NULL, *jfilter = NULL;
+ dfilter_t *rfcode = NULL;
+ gboolean rfilter_parse_failed = FALSE;
+ e_prefs *prefs_p;
+ char badopt;
+ GtkWidget *splash_win = NULL;
+ GLogLevelFlags log_flags;
+ guint go_to_packet = 0;
+ gboolean jump_backwards = FALSE;
+ dfilter_t *jump_to_filter = NULL;
+ int optind_initial;
+ int status;
+#ifdef HAVE_GTKOSXAPPLICATION
+ GtkOSXApplication *theApp;
+#endif
+
+#ifdef HAVE_LIBPCAP
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+#define OPTSTRING_B "B:"
+#else
+#define OPTSTRING_B ""
+#endif /* _WIN32 or HAVE_PCAP_CREATE */
+#else /* HAVE_LIBPCAP */
+#define OPTSTRING_B ""
+#endif /* HAVE_LIBPCAP */
+
+#ifdef HAVE_PCAP_CREATE
+#define OPTSTRING_I "I"
+#else
+#define OPTSTRING_I ""
+#endif
+
+#define OPTSTRING "a:b:" OPTSTRING_B "c:C:d:Df:g:Hhi:" OPTSTRING_I "jJ:kK:lLm:nN:o:P:pr:R:Ss:t:u:vw:X:y:z:"
+
+ static const char optstring[] = OPTSTRING;
+
+ /* Set the C-language locale to the native environment. */
+ setlocale(LC_ALL, "");
+#ifdef _WIN32
+ arg_list_utf_16to8(argc, argv);
+#endif /* _WIN32 */
+
+ /*
+ * Get credential information for later use, and drop privileges
+ * before doing anything else.
+ * Let the user know if anything happened.
+ */
+ init_process_policies();
+ relinquish_special_privs_perm();
+
+ /*
+ * Attempt to get the pathname of the executable file.
+ */
+ init_progfile_dir_error = init_progfile_dir(argv[0], main);
+
+ /* initialize the funnel mini-api */
+ initialize_funnel_ops();
+
+ AirPDcapInitContext(&airpdcap_ctx);
+
+#ifdef _WIN32
+ /* Load wpcap if possible. Do this before collecting the run-time version information */
+ load_wpcap();
+
+ /* ... and also load the packet.dll from wpcap */
+ wpcap_packet_load();
+
+#ifdef HAVE_AIRPCAP
+ /* Load the airpcap.dll. This must also be done before collecting
+ * run-time version information. */
+ airpcap_dll_ret_val = load_airpcap();
+
+ switch (airpcap_dll_ret_val) {
+ case AIRPCAP_DLL_OK:
+ /* load the airpcap interfaces */
+ airpcap_if_list = get_airpcap_interface_list(&err, &err_str);
+
+ if (airpcap_if_list == NULL || g_list_length(airpcap_if_list) == 0){
+ if (err == CANT_GET_AIRPCAP_INTERFACE_LIST && err_str != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", "Failed to open Airpcap Adapters!");
+ g_free(err_str);
+ }
+ airpcap_if_active = NULL;
+
+ } else {
+
+ /* select the first ad default (THIS SHOULD BE CHANGED) */
+ airpcap_if_active = airpcap_get_default_if(airpcap_if_list);
+ }
+ break;
+#if 0
+ /*
+ * XXX - Maybe we need to warn the user if one of the following happens???
+ */
+ case AIRPCAP_DLL_OLD:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s","AIRPCAP_DLL_OLD\n");
+ break;
+
+ case AIRPCAP_DLL_ERROR:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s","AIRPCAP_DLL_ERROR\n");
+ break;
+
+ case AIRPCAP_DLL_NOT_FOUND:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s","AIRPCAP_DDL_NOT_FOUND\n");
+ break;
+#endif
+ }
+#endif /* HAVE_AIRPCAP */
+
+ /* Start windows sockets */
+ WSAStartup( MAKEWORD( 1, 1 ), &wsaData );
+#endif /* _WIN32 */
+
+ profile_store_persconffiles (TRUE);
+
+ /* Assemble the compile-time version information string */
+ comp_info_str = g_string_new("Compiled ");
+
+ get_compiled_version_info(comp_info_str, get_gtk_compiled_info, get_gui_compiled_info);
+
+ /* Assemble the run-time version information string */
+ runtime_info_str = g_string_new("Running ");
+ get_runtime_version_info(runtime_info_str, get_gui_runtime_info);
+
+ /* Read the profile independent recent file. We have to do this here so we can */
+ /* set the profile before it can be set from the command line parameterts */
+ recent_read_static(&rf_path, &rf_open_errno);
+ if (rf_path != NULL && rf_open_errno != 0) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Could not open common recent file\n\"%s\": %s.",
+ rf_path, g_strerror(rf_open_errno));
+ }
+
+ /* "pre-scan" the command line parameters, if we have "console only"
+ parameters. We do this so we don't start GTK+ if we're only showing
+ command-line help or version information.
+
+ XXX - this pre-scan is done before we start GTK+, so we haven't
+ run gtk_init() on the arguments. That means that GTK+ arguments
+ have not been removed from the argument list; those arguments
+ begin with "--", and will be treated as an error by getopt().
+
+ We thus ignore errors - *and* set "opterr" to 0 to suppress the
+ error messages. */
+ opterr = 0;
+ optind_initial = optind;
+ while ((opt = getopt(argc, argv, optstring)) != -1) {
+ switch (opt) {
+ case 'C': /* Configuration Profile */
+ if (profile_exists (optarg, FALSE)) {
+ set_profile_name (optarg);
+ } else {
+ cmdarg_err("Configuration Profile \"%s\" does not exist", optarg);
+ exit(1);
+ }
+ break;
+ case 'D': /* Print a list of capture devices and exit */
+#ifdef HAVE_LIBPCAP
+ if_list = capture_interface_list(&err, &err_str);
+ if (if_list == NULL) {
+ switch (err) {
+ case CANT_GET_INTERFACE_LIST:
+ case DONT_HAVE_PCAP:
+ cmdarg_err("%s", err_str);
+ g_free(err_str);
+ break;
+
+ case NO_INTERFACES_FOUND:
+ cmdarg_err("There are no interfaces on which a capture can be done");
+ break;
+ }
+ exit(2);
+ }
+ capture_opts_print_interfaces(if_list);
+ free_interface_list(if_list);
+ exit(0);
+#else
+ capture_option_specified = TRUE;
+ arg_error = TRUE;
+#endif
+ break;
+ case 'h': /* Print help and exit */
+ print_usage(TRUE);
+ exit(0);
+ break;
+#ifdef _WIN32
+ case 'i':
+ if (strcmp(optarg, "-") == 0)
+ stdin_capture = TRUE;
+ break;
+#endif
+ case 'P': /* Path settings - change these before the Preferences and alike are processed */
+ status = filesystem_opt(opt, optarg);
+ if(status != 0) {
+ cmdarg_err("-P flag \"%s\" failed (hint: is it quoted and existing?)", optarg);
+ exit(status);
+ }
+ break;
+ case 'v': /* Show version and exit */
+ show_version();
+ exit(0);
+ break;
+ case 'X':
+ /*
+ * Extension command line options have to be processed before
+ * we call epan_init() as they are supposed to be used by dissectors
+ * or taps very early in the registration process.
+ */
+ ex_opt_add(optarg);
+ break;
+ case '?': /* Ignore errors - the "real" scan will catch them. */
+ break;
+ }
+ }
+
+ /* Init the "Open file" dialog directory */
+ /* (do this after the path settings are processed) */
+
+ /* Read the profile dependent (static part) of the recent file. */
+ /* Only the static part of it will be read, as we don't have the gui now to fill the */
+ /* recent lists which is done in the dynamic part. */
+ /* We have to do this already here, so command line parameters can overwrite these values. */
+ recent_read_profile_static(&rf_path, &rf_open_errno);
+ if (rf_path != NULL && rf_open_errno != 0) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Could not open recent file\n\"%s\": %s.",
+ rf_path, g_strerror(rf_open_errno));
+ }
+
+ if (recent.gui_fileopen_remembered_dir &&
+ test_for_directory(recent.gui_fileopen_remembered_dir) == EISDIR) {
+ set_last_open_dir(recent.gui_fileopen_remembered_dir);
+ } else {
+ set_last_open_dir(get_persdatafile_dir());
+ }
+
+ /* Set getopt index back to initial value, so it will start with the
+ first command line parameter again. Also reset opterr to 1, so that
+ error messages are printed by getopt().
+
+ XXX - this seems to work on most platforms, but time will tell.
+ The Single UNIX Specification says "The getopt() function need
+ not be reentrant", so this isn't guaranteed to work. The Mac
+ OS X 10.4[.x] getopt() man page says
+
+ In order to use getopt() to evaluate multiple sets of arguments, or to
+ evaluate a single set of arguments multiple times, the variable optreset
+ must be set to 1 before the second and each additional set of calls to
+ getopt(), and the variable optind must be reinitialized.
+
+ ...
+
+ The optreset variable was added to make it possible to call the getopt()
+ function multiple times. This is an extension to the IEEE Std 1003.2
+ (``POSIX.2'') specification.
+
+ which I think comes from one of the other BSDs.
+
+ XXX - if we want to control all the command-line option errors, so
+ that we can display them where we choose (e.g., in a window), we'd
+ want to leave opterr as 0, and produce our own messages using optopt.
+ We'd have to check the value of optopt to see if it's a valid option
+ letter, in which case *presumably* the error is "this option requires
+ an argument but none was specified", or not a valid option letter,
+ in which case *presumably* the error is "this option isn't valid".
+ Some versions of getopt() let you supply a option string beginning
+ with ':', which means that getopt() will return ':' rather than '?'
+ for "this option requires an argument but none was specified", but
+ not all do. */
+ optind = optind_initial;
+ opterr = 1;
+
+#if !GLIB_CHECK_VERSION(2,31,0)
+ g_thread_init(NULL);
+#endif
+
+ /* Set the current locale according to the program environment.
+ * We haven't localized anything, but some GTK widgets are localized
+ * (the file selection dialogue, for example).
+ * This also sets the C-language locale to the native environment. */
+ setlocale (LC_ALL, "");
+
+ /* Let GTK get its args (will need an X server, so do this after command line only commands handled) */
+ gtk_init (&argc, &argv);
+
+ cf_callback_add(main_cf_callback, NULL);
+#ifdef HAVE_LIBPCAP
+ capture_callback_add(main_capture_callback, NULL);
+#endif
+ cf_callback_add(statusbar_cf_callback, NULL);
+#ifdef HAVE_LIBPCAP
+ capture_callback_add(statusbar_capture_callback, NULL);
+#endif
+
+ /* Arrange that if we have no console window, and a GLib message logging
+ routine is called to log a message, we pop up a console window.
+
+ We do that by inserting our own handler for all messages logged
+ to the default domain; that handler pops up a console if necessary,
+ and then calls the default handler. */
+
+ /* We might want to have component specific log levels later ... */
+
+ log_flags =
+ G_LOG_LEVEL_ERROR|
+ G_LOG_LEVEL_CRITICAL|
+ G_LOG_LEVEL_WARNING|
+ G_LOG_LEVEL_MESSAGE|
+ G_LOG_LEVEL_INFO|
+ G_LOG_LEVEL_DEBUG|
+ G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION;
+
+ g_log_set_handler(NULL,
+ log_flags,
+ console_log_handler, NULL /* user_data */);
+ g_log_set_handler(LOG_DOMAIN_MAIN,
+ log_flags,
+ console_log_handler, NULL /* user_data */);
+
+#ifdef HAVE_LIBPCAP
+ g_log_set_handler(LOG_DOMAIN_CAPTURE,
+ log_flags,
+ console_log_handler, NULL /* user_data */);
+ g_log_set_handler(LOG_DOMAIN_CAPTURE_CHILD,
+ log_flags,
+ console_log_handler, NULL /* user_data */);
+
+ /* Set the initial values in the capture options. This might be overwritten
+ by preference settings and then again by the command line parameters. */
+ capture_opts_init(&global_capture_opts, &cfile);
+#endif
+
+ /* Initialize whatever we need to allocate colors for GTK+ */
+ colors_init();
+
+ /* Non-blank filter means we're remote. Throttle splash screen and resolution updates. */
+ filter = get_conn_cfilter();
+ if ( *filter != '\0' ) {
+ info_update_freq = 1000; /* Milliseconds */
+ }
+
+ /* We won't come till here, if we had a "console only" command line parameter. */
+ splash_win = splash_new("Loading Wireshark ...");
+ if (init_progfile_dir_error != NULL) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Can't get pathname of Wireshark: %s.\n"
+ "It won't be possible to capture traffic.\n"
+ "Report this to the Wireshark developers.",
+ init_progfile_dir_error);
+ g_free(init_progfile_dir_error);
+ }
+
+ splash_update(RA_DISSECTORS, NULL, (gpointer)splash_win);
+
+ /* Register all dissectors; we must do this before checking for the
+ "-G" flag, as the "-G" flag dumps information registered by the
+ dissectors, and we must do it before we read the preferences, in
+ case any dissectors register preferences. */
+ epan_init(register_all_protocols,register_all_protocol_handoffs,
+ splash_update, (gpointer) splash_win,
+ failure_alert_box,open_failure_alert_box,read_failure_alert_box,
+ write_failure_alert_box);
+
+ splash_update(RA_LISTENERS, NULL, (gpointer)splash_win);
+
+ /* Register all tap listeners; we do this before we parse the arguments,
+ as the "-z" argument can specify a registered tap. */
+
+ /* we register the plugin taps before the other taps because
+ stats_tree taps plugins will be registered as tap listeners
+ by stats_tree_stat.c and need to registered before that */
+
+#ifdef HAVE_PLUGINS
+ register_all_plugin_tap_listeners();
+#endif
+
+ register_all_tap_listeners();
+
+ splash_update(RA_PREFERENCES, NULL, (gpointer)splash_win);
+
+ /* Now register the preferences for any non-dissector modules.
+ We must do that before we read the preferences as well. */
+ prefs_register_modules();
+
+ prefs_p = read_configuration_files (&gdp_path, &dp_path);
+ /* Removed thread code:
+ * http://anonsvn.wireshark.org/viewvc/viewvc.cgi?view=rev&revision=35027
+ */
+
+ /* this is to keep tap extensions updating once every 3 seconds */
+ tap_update_timer_id = g_timeout_add(prefs_p->tap_update_interval, tap_update_cb, NULL);
+
+ splash_update(RA_CONFIGURATION, NULL, (gpointer)splash_win);
+ proto_help_init();
+ cap_file_init(&cfile);
+
+ /* Fill in capture options with values from the preferences */
+ prefs_to_capture_opts();
+
+ /* Now get our args */
+ while ((opt = getopt(argc, argv, optstring)) != -1) {
+ switch (opt) {
+ /*** capture option specific ***/
+ case 'a': /* autostop criteria */
+ case 'b': /* Ringbuffer option */
+ case 'c': /* Capture xxx packets */
+ case 'f': /* capture filter */
+ case 'k': /* Start capture immediately */
+ case 'H': /* Hide capture info dialog box */
+ case 'i': /* Use interface xxx */
+ case 'p': /* Don't capture in promiscuous mode */
+#ifdef HAVE_PCAP_CREATE
+ case 'I': /* Capture in monitor mode, if available */
+#endif
+ case 's': /* Set the snapshot (capture) length */
+ case 'S': /* "Sync" mode: used for following file ala tail -f */
+ case 'w': /* Write to capture file xxx */
+ case 'y': /* Set the pcap data link type */
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ case 'B': /* Buffer size */
+#endif /* _WIN32 or HAVE_PCAP_CREATE */
+#ifdef HAVE_LIBPCAP
+ status = capture_opts_add_opt(&global_capture_opts, opt, optarg,
+ &start_capture);
+ if(status != 0) {
+ exit(status);
+ }
+#else
+ capture_option_specified = TRUE;
+ arg_error = TRUE;
+#endif
+ break;
+
+#if defined(HAVE_HEIMDAL_KERBEROS) || defined(HAVE_MIT_KERBEROS)
+ case 'K': /* Kerberos keytab file */
+ read_keytab_file(optarg);
+ break;
+#endif
+
+ /*** all non capture option specific ***/
+ case 'C':
+ /* Configuration profile settings were already processed just ignore them this time*/
+ break;
+ case 'd':
+ dfilter = optarg;
+ break;
+ case 'j': /* Search backwards for a matching packet from filter in option J */
+ jump_backwards = TRUE;
+ break;
+ case 'g': /* Go to packet with the given packet number */
+ go_to_packet = get_positive_int(optarg, "go to packet");
+ break;
+ case 'J': /* Jump to the first packet which matches the filter criteria */
+ jfilter = optarg;
+ break;
+ case 'l': /* Automatic scrolling in live capture mode */
+#ifdef HAVE_LIBPCAP
+ auto_scroll_live = TRUE;
+#else
+ capture_option_specified = TRUE;
+ arg_error = TRUE;
+#endif
+ break;
+ case 'L': /* Print list of link-layer types and exit */
+#ifdef HAVE_LIBPCAP
+ list_link_layer_types = TRUE;
+#else
+ capture_option_specified = TRUE;
+ arg_error = TRUE;
+#endif
+ break;
+ case 'm': /* Fixed-width font for the display */
+ g_free(prefs_p->gui_font_name);
+ prefs_p->gui_font_name = g_strdup(optarg);
+ break;
+ case 'n': /* No name resolution */
+ gbl_resolv_flags = RESOLV_NONE;
+ break;
+ case 'N': /* Select what types of addresses/port #s to resolve */
+ if (gbl_resolv_flags == RESOLV_ALL)
+ gbl_resolv_flags = RESOLV_NONE;
+ badopt = string_to_name_resolve(optarg, &gbl_resolv_flags);
+ if (badopt != '\0') {
+ cmdarg_err("-N specifies unknown resolving option '%c'; valid options are 'm', 'n', and 't'",
+ badopt);
+ exit(1);
+ }
+ break;
+ case 'o': /* Override preference from command line */
+ switch (prefs_set_pref(optarg)) {
+ case PREFS_SET_OK:
+ break;
+ case PREFS_SET_SYNTAX_ERR:
+ cmdarg_err("Invalid -o flag \"%s\"", optarg);
+ exit(1);
+ break;
+ case PREFS_SET_NO_SUCH_PREF:
+ /* not a preference, might be a recent setting */
+ switch (recent_set_arg(optarg)) {
+ case PREFS_SET_OK:
+ break;
+ case PREFS_SET_SYNTAX_ERR:
+ /* shouldn't happen, checked already above */
+ cmdarg_err("Invalid -o flag \"%s\"", optarg);
+ exit(1);
+ break;
+ case PREFS_SET_NO_SUCH_PREF:
+ case PREFS_SET_OBSOLETE:
+ cmdarg_err("-o flag \"%s\" specifies unknown preference/recent value",
+ optarg);
+ exit(1);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ break;
+ case PREFS_SET_OBSOLETE:
+ cmdarg_err("-o flag \"%s\" specifies obsolete preference",
+ optarg);
+ exit(1);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ break;
+ case 'P':
+ /* Path settings were already processed just ignore them this time*/
+ break;
+ case 'r': /* Read capture file xxx */
+ /* We may set "last_open_dir" to "cf_name", and if we change
+ "last_open_dir" later, we free the old value, so we have to
+ set "cf_name" to something that's been allocated. */
+ cf_name = g_strdup(optarg);
+ break;
+ case 'R': /* Read file filter */
+ rfilter = optarg;
+ break;
+ case 't': /* Time stamp type */
+ if (strcmp(optarg, "r") == 0)
+ timestamp_set_type(TS_RELATIVE);
+ else if (strcmp(optarg, "a") == 0)
+ timestamp_set_type(TS_ABSOLUTE);
+ else if (strcmp(optarg, "ad") == 0)
+ timestamp_set_type(TS_ABSOLUTE_WITH_DATE);
+ else if (strcmp(optarg, "d") == 0)
+ timestamp_set_type(TS_DELTA);
+ else if (strcmp(optarg, "dd") == 0)
+ timestamp_set_type(TS_DELTA_DIS);
+ else if (strcmp(optarg, "e") == 0)
+ timestamp_set_type(TS_EPOCH);
+ else if (strcmp(optarg, "u") == 0)
+ timestamp_set_type(TS_UTC);
+ else if (strcmp(optarg, "ud") == 0)
+ timestamp_set_type(TS_UTC_WITH_DATE);
+ else {
+ cmdarg_err("Invalid time stamp type \"%s\"", optarg);
+ cmdarg_err_cont("It must be \"r\" for relative, \"a\" for absolute,");
+ cmdarg_err_cont("\"ad\" for absolute with date, or \"d\" for delta.");
+ exit(1);
+ }
+ break;
+ case 'u': /* Seconds type */
+ if (strcmp(optarg, "s") == 0)
+ timestamp_set_seconds_type(TS_SECONDS_DEFAULT);
+ else if (strcmp(optarg, "hms") == 0)
+ timestamp_set_seconds_type(TS_SECONDS_HOUR_MIN_SEC);
+ else {
+ cmdarg_err("Invalid seconds type \"%s\"", optarg);
+ cmdarg_err_cont("It must be \"s\" for seconds or \"hms\" for hours, minutes and seconds.");
+ exit(1);
+ }
+ break;
+ case 'X':
+ /* ext ops were already processed just ignore them this time*/
+ break;
+ case 'z':
+ /* We won't call the init function for the stat this soon
+ as it would disallow MATE's fields (which are registered
+ by the preferences set callback) from being used as
+ part of a tap filter. Instead, we just add the argument
+ to a list of stat arguments. */
+ if (!process_stat_cmd_arg(optarg)) {
+ cmdarg_err("Invalid -z argument.");
+ cmdarg_err_cont(" -z argument must be one of :");
+ list_stat_cmd_args();
+ exit(1);
+ }
+ break;
+ default:
+ case '?': /* Bad flag - print usage message */
+ arg_error = TRUE;
+ break;
+ }
+ }
+ if (!arg_error) {
+ argc -= optind;
+ argv += optind;
+ if (argc >= 1) {
+ if (cf_name != NULL) {
+ /*
+ * Input file name specified with "-r" *and* specified as a regular
+ * command-line argument.
+ */
+ cmdarg_err("File name specified both with -r and regular argument");
+ arg_error = TRUE;
+ } else {
+ /*
+ * Input file name not specified with "-r", and a command-line argument
+ * was specified; treat it as the input file name.
+ *
+ * Yes, this is different from tshark, where non-flag command-line
+ * arguments are a filter, but this works better on GUI desktops
+ * where a command can be specified to be run to open a particular
+ * file - yes, you could have "-r" as the last part of the command,
+ * but that's a bit ugly.
+ */
+ cf_name = g_strdup(argv[0]);
+ }
+ argc--;
+ argv++;
+ }
+
+ if (argc != 0) {
+ /*
+ * Extra command line arguments were specified; complain.
+ */
+ cmdarg_err("Invalid argument: %s", argv[0]);
+ arg_error = TRUE;
+ }
+ }
+ if (arg_error) {
+#ifndef HAVE_LIBPCAP
+ if (capture_option_specified) {
+ cmdarg_err("This version of Wireshark was not built with support for capturing packets.");
+ }
+#endif
+ print_usage(FALSE);
+ exit(1);
+ }
+
+#ifdef HAVE_LIBPCAP
+ if (start_capture && list_link_layer_types) {
+ /* Specifying *both* is bogus. */
+ cmdarg_err("You can't specify both -L and a live capture.");
+ exit(1);
+ }
+
+ if (list_link_layer_types) {
+ /* We're supposed to list the link-layer types for an interface;
+ did the user also specify a capture file to be read? */
+ if (cf_name) {
+ /* Yes - that's bogus. */
+ cmdarg_err("You can't specify -L and a capture file to be read.");
+ exit(1);
+ }
+ /* No - did they specify a ring buffer option? */
+ if (global_capture_opts.multi_files_on) {
+ cmdarg_err("Ring buffer requested, but a capture isn't being done.");
+ exit(1);
+ }
+ } else {
+ /* We're supposed to do a live capture; did the user also specify
+ a capture file to be read? */
+ if (start_capture && cf_name) {
+ /* Yes - that's bogus. */
+ cmdarg_err("You can't specify both a live capture and a capture file to be read.");
+ exit(1);
+ }
+
+ /* No - was the ring buffer option specified and, if so, does it make
+ sense? */
+ if (global_capture_opts.multi_files_on) {
+ /* Ring buffer works only under certain conditions:
+ a) ring buffer does not work with temporary files;
+ b) real_time_mode and multi_files_on are mutually exclusive -
+ real_time_mode takes precedence;
+ c) it makes no sense to enable the ring buffer if the maximum
+ file size is set to "infinite". */
+ if (global_capture_opts.save_file == NULL) {
+ cmdarg_err("Ring buffer requested, but capture isn't being saved to a permanent file.");
+ global_capture_opts.multi_files_on = FALSE;
+ }
+/* if (global_capture_opts.real_time_mode) {
+ cmdarg_err("Ring buffer requested, but an \"Update list of packets in real time\" capture is being done.");
+ global_capture_opts.multi_files_on = FALSE;
+ }*/
+ if (!global_capture_opts.has_autostop_filesize && !global_capture_opts.has_file_duration) {
+ cmdarg_err("Ring buffer requested, but no maximum capture file size or duration were specified.");
+/* XXX - this must be redesigned as the conditions changed */
+/* global_capture_opts.multi_files_on = FALSE;*/
+ }
+ }
+ }
+
+ if (start_capture || list_link_layer_types) {
+ /* Did the user specify an interface to use? */
+ if (!capture_opts_trim_iface(&global_capture_opts,
+ (prefs_p->capture_device) ? get_if_name(prefs_p->capture_device) : NULL)) {
+ exit(2);
+ }
+ }
+
+ if (list_link_layer_types) {
+ /* Get the list of link-layer types for the capture devices. */
+ if_capabilities_t *caps;
+ guint i;
+ interface_options interface_opts;
+
+ for (i = 0; i < global_capture_opts.ifaces->len; i++) {
+
+ interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, i);
+ caps = capture_get_if_capabilities(interface_opts.name, interface_opts.monitor_mode, &err_str);
+ if (caps == NULL) {
+ cmdarg_err("%s", err_str);
+ g_free(err_str);
+ exit(2);
+ }
+ if (caps->data_link_types == NULL) {
+ cmdarg_err("The capture device \"%s\" has no data link types.", interface_opts.name);
+ exit(2);
+ }
+ capture_opts_print_if_capabilities(caps, interface_opts.name, interface_opts.monitor_mode);
+ free_if_capabilities(caps);
+ }
+ exit(0);
+ }
+
+ capture_opts_trim_snaplen(&global_capture_opts, MIN_PACKET_SIZE);
+ capture_opts_trim_ring_num_files(&global_capture_opts);
+#endif /* HAVE_LIBPCAP */
+
+ /* Notify all registered modules that have had any of their preferences
+ changed either from one of the preferences file or from the command
+ line that their preferences have changed. */
+ prefs_apply_all();
+
+#ifdef HAVE_LIBPCAP
+ if ((global_capture_opts.ifaces->len == 0) &&
+ (prefs.capture_device != NULL)) {
+ GList *curr, *combo_list;
+ gboolean found = FALSE;
+
+ if_list = capture_interface_list(&err, NULL);
+ if (g_list_length(if_list) > 0) {
+ combo_list = build_capture_combo_list(if_list, FALSE);
+ free_interface_list(if_list);
+ for (curr = combo_list; curr; curr = g_list_next(curr)) {
+ if (strcmp(curr->data, prefs.capture_device) == 0) {
+ found = TRUE;
+ break;
+ }
+ }
+ }
+ if (found) {
+ interface_options interface_opts;
+
+ interface_opts.name = g_strdup(get_if_name(prefs.capture_device));
+ interface_opts.descr = get_interface_descriptive_name(interface_opts.name);
+ interface_opts.monitor_mode = prefs_capture_device_monitor_mode(interface_opts.name);
+ interface_opts.linktype = capture_dev_user_linktype_find(interface_opts.name);
+ interface_opts.cfilter = g_strdup(global_capture_opts.default_options.cfilter);
+ interface_opts.snaplen = global_capture_opts.default_options.snaplen;
+ interface_opts.has_snaplen = global_capture_opts.default_options.has_snaplen;
+ interface_opts.promisc_mode = global_capture_opts.default_options.promisc_mode;
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ interface_opts.buffer_size = global_capture_opts.default_options.buffer_size;
+#endif
+#ifdef HAVE_PCAP_REMOTE
+ interface_opts.src_type = global_capture_opts.default_options.src_type;
+ interface_opts.remote_host = g_strdup(global_capture_opts.default_options.remote_host);
+ interface_opts.remote_port = g_strdup(global_capture_opts.default_options.remote_port);
+ interface_opts.auth_type = global_capture_opts.default_options.auth_type;
+ interface_opts.auth_username = g_strdup(global_capture_opts.default_options.auth_username);
+ interface_opts.auth_password = g_strdup(global_capture_opts.default_options.auth_password);
+ interface_opts.datatx_udp = global_capture_opts.default_options.datatx_udp;
+ interface_opts.nocap_rpcap = global_capture_opts.default_options.nocap_rpcap;
+ interface_opts.nocap_local = global_capture_opts.default_options.nocap_local;
+ #endif
+ #ifdef HAVE_PCAP_SETSAMPLING
+ interface_opts.sampling_method = global_capture_opts.default_options.sampling_method;
+ interface_opts.sampling_param = global_capture_opts.default_options.sampling_param;
+ #endif
+ g_array_insert_val(global_capture_opts.ifaces, 0, interface_opts);
+ }
+ }
+#endif
+
+ /* disabled protocols as per configuration file */
+ if (gdp_path == NULL && dp_path == NULL) {
+ set_disabled_protos_list();
+ }
+
+ build_column_format_array(&cfile.cinfo, prefs_p->num_cols, TRUE);
+
+ /* read in rc file from global and personal configuration paths. */
+ rc_file = get_datafile_path(RC_FILE);
+#if GTK_CHECK_VERSION(3,0,0)
+ /* XXX resolve later */
+#else
+ gtk_rc_parse(rc_file);
+ g_free(rc_file);
+ rc_file = get_persconffile_path(RC_FILE, FALSE, FALSE);
+ gtk_rc_parse(rc_file);
+#endif
+ g_free(rc_file);
+
+ font_init();
+
+ macros_init();
+
+ stock_icons_init();
+
+ /* close the splash screen, as we are going to open the main window now */
+ splash_destroy(splash_win);
+
+ /************************************************************************/
+ /* Everything is prepared now, preferences and command line was read in */
+
+ /* Pop up the main window. */
+ create_main_window(pl_size, tv_size, bv_size, prefs_p);
+
+ /* Read the dynamic part of the recent file, as we have the gui now ready for it. */
+ recent_read_dynamic(&rf_path, &rf_open_errno);
+ if (rf_path != NULL && rf_open_errno != 0) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Could not open recent file\n\"%s\": %s.",
+ rf_path, g_strerror(rf_open_errno));
+ }
+
+ color_filters_enable(recent.packet_list_colorize);
+
+ /* rearrange all the widgets as we now have all recent settings ready for this */
+ main_widgets_rearrange();
+
+ /* Fill in column titles. This must be done after the top level window
+ is displayed.
+
+ XXX - is that still true, with fixed-width columns? */
+
+ menu_recent_read_finished();
+#ifdef HAVE_LIBPCAP
+ menu_auto_scroll_live_changed(auto_scroll_live);
+#endif
+
+ switch (user_font_apply()) {
+ case FA_SUCCESS:
+ break;
+ case FA_FONT_NOT_RESIZEABLE:
+ /* "user_font_apply()" popped up an alert box. */
+ /* turn off zooming - font can't be resized */
+ case FA_FONT_NOT_AVAILABLE:
+ /* XXX - did we successfully load the un-zoomed version earlier?
+ If so, this *probably* means the font is available, but not at
+ this particular zoom level, but perhaps some other failure
+ occurred; I'm not sure you can determine which is the case,
+ however. */
+ /* turn off zooming - zoom level is unavailable */
+ default:
+ /* in any other case than FA_SUCCESS, turn off zooming */
+ recent.gui_zoom_level = 0;
+ /* XXX: would it be a good idea to disable zooming (insensitive GUI)? */
+ }
+
+ dnd_init(top_level);
+
+ color_filters_init();
+ decode_as_init();
+#ifdef HAVE_LIBPCAP
+ capture_filter_init();
+#endif
+
+ /* the window can be sized only, if it's not already shown, so do it now! */
+ main_load_window_geometry(top_level);
+
+ g_timeout_add(info_update_freq, resolv_update_cb, NULL);
+
+ if (dfilter) {
+ GtkWidget *filter_te;
+ filter_te = gtk_bin_get_child(GTK_BIN(g_object_get_data(G_OBJECT(top_level), E_DFILTER_CM_KEY)));
+ gtk_entry_set_text(GTK_ENTRY(filter_te), dfilter);
+
+ /* Run the display filter so it goes in effect. */
+ main_filter_packets(&cfile, dfilter, FALSE);
+ }
+
+ /* If we were given the name of a capture file, read it in now;
+ we defer it until now, so that, if we can't open it, and pop
+ up an alert box, the alert box is more likely to come up on
+ top of the main window - but before the preference-file-error
+ alert box, so, if we get one of those, it's more likely to come
+ up on top of us. */
+ if (cf_name) {
+ show_main_window(TRUE);
+ check_and_warn_user_startup(cf_name);
+ if (rfilter != NULL) {
+ if (!dfilter_compile(rfilter, &rfcode)) {
+ bad_dfilter_alert_box(rfilter);
+ rfilter_parse_failed = TRUE;
+ }
+ }
+ if (!rfilter_parse_failed) {
+ if (cf_open(&cfile, cf_name, FALSE, &err) == CF_OK) {
+ /* "cf_open()" succeeded, so it closed the previous
+ capture file, and thus destroyed any previous read filter
+ attached to "cf". */
+
+ cfile.rfcode = rfcode;
+ /* Open stat windows; we do so after creating the main window,
+ to avoid GTK warnings, and after successfully opening the
+ capture file, so we know we have something to compute stats
+ on, and after registering all dissectors, so that MATE will
+ have registered its field array and we can have a tap filter
+ with one of MATE's late-registered fields as part of the
+ filter. */
+ start_requested_stats();
+
+ /* Read the capture file. */
+ switch (cf_read(&cfile, FALSE)) {
+
+ case CF_READ_OK:
+ case CF_READ_ERROR:
+ /* Just because we got an error, that doesn't mean we were unable
+ to read any of the file; we handle what we could get from the
+ file. */
+ /* if the user told us to jump to a specific packet, do it now */
+ if(go_to_packet != 0) {
+ /* Jump to the specified frame number, kept for backward
+ compatibility. */
+ cf_goto_frame(&cfile, go_to_packet);
+ } else if (jfilter != NULL) {
+ /* try to compile given filter */
+ if (!dfilter_compile(jfilter, &jump_to_filter)) {
+ bad_dfilter_alert_box(jfilter);
+ } else {
+ /* Filter ok, jump to the first packet matching the filter
+ conditions. Default search direction is forward, but if
+ option d was given, search backwards */
+ cf_find_packet_dfilter(&cfile, jump_to_filter, jump_backwards);
+ }
+ }
+ break;
+
+ case CF_READ_ABORTED:
+ /* Exit now. */
+ exit(0);
+ break;
+ }
+
+ /* If the filename is not the absolute path, prepend the current dir. This happens
+ when wireshark is invoked from a cmd shell (e.g.,'wireshark -r file.pcap'). */
+ if (!g_path_is_absolute(cf_name)) {
+ char *old_cf_name = cf_name;
+ char *pwd = g_get_current_dir();
+ cf_name = g_strdup_printf("%s%s%s", pwd, G_DIR_SEPARATOR_S, cf_name);
+ g_free(old_cf_name);
+ g_free(pwd);
+ }
+
+ /* Save the name of the containing directory specified in the
+ path name, if any; we can write over cf_name, which is a
+ good thing, given that "get_dirname()" does write over its
+ argument. */
+ s = get_dirname(cf_name);
+ set_last_open_dir(s);
+ g_free(cf_name);
+ cf_name = NULL;
+ } else {
+ if (rfcode != NULL)
+ dfilter_free(rfcode);
+ cfile.rfcode = NULL;
+ show_main_window(FALSE);
+ /* Don't call check_and_warn_user_startup(): we did it above */
+ set_menus_for_capture_in_progress(FALSE);
+ set_capture_if_dialog_for_capture_in_progress(FALSE);
+ }
+ }
+ } else {
+#ifdef HAVE_LIBPCAP
+ if (start_capture) {
+ if (global_capture_opts.save_file != NULL) {
+ /* Save the directory name for future file dialogs. */
+ /* (get_dirname overwrites filename) */
+ s = get_dirname(g_strdup(global_capture_opts.save_file));
+ set_last_open_dir(s);
+ g_free(s);
+ }
+ /* "-k" was specified; start a capture. */
+ show_main_window(TRUE);
+ check_and_warn_user_startup(cf_name);
+ if (capture_start(&global_capture_opts)) {
+ /* The capture started. Open stat windows; we do so after creating
+ the main window, to avoid GTK warnings, and after successfully
+ opening the capture file, so we know we have something to compute
+ stats on, and after registering all dissectors, so that MATE will
+ have registered its field array and we can have a tap filter with
+ one of MATE's late-registered fields as part of the filter. */
+ start_requested_stats();
+ }
+ } else {
+ show_main_window(FALSE);
+ check_and_warn_user_startup(cf_name);
+ set_menus_for_capture_in_progress(FALSE);
+ set_capture_if_dialog_for_capture_in_progress(FALSE);
+ }
+
+ /* if the user didn't supply a capture filter, use the one to filter out remote connections like SSH */
+ if (!start_capture && !global_capture_opts.default_options.cfilter) {
+ global_capture_opts.default_options.cfilter = g_strdup(get_conn_cfilter());
+ }
+#else /* HAVE_LIBPCAP */
+ show_main_window(FALSE);
+ check_and_warn_user_startup(cf_name);
+ set_menus_for_capture_in_progress(FALSE);
+ set_capture_if_dialog_for_capture_in_progress(FALSE);
+#endif /* HAVE_LIBPCAP */
+ }
+
+ /* register our pid if we are being run from a U3 device */
+ u3_register_pid();
+
+ profile_store_persconffiles (FALSE);
+
+#ifdef HAVE_GTKOSXAPPLICATION
+ theApp = g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
+ gtk_osxapplication_set_dock_icon_pixbuf(theApp,gdk_pixbuf_new_from_xpm_data(wsicon64_xpm));
+ gtk_osxapplication_ready(theApp);
+#endif
+
+ g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_INFO, "Wireshark is up and ready to go");
+
+ /* we'll enter the GTK loop now and hand the control over to GTK ... */
+ gtk_main();
+ /* ... back from GTK, we're going down now! */
+
+ /* deregister our pid */
+ u3_deregister_pid();
+
+ epan_cleanup();
+
+ AirPDcapDestroyContext(&airpdcap_ctx);
+
+#ifdef _WIN32
+ /* hide the (unresponsive) main window, while asking the user to close the console window */
+ gtk_widget_hide(top_level);
+
+#ifdef HAVE_GTKOSXAPPLICATION
+ g_object_unref(theApp);
+#endif
+
+ /* Shutdown windows sockets */
+ WSACleanup();
+
+ /* For some unknown reason, the "atexit()" call in "create_console()"
+ doesn't arrange that "destroy_console()" be called when we exit,
+ so we call it here if a console was created. */
+ destroy_console();
+#endif
+
+ exit(0);
+}
+
+#ifdef _WIN32
+
+/* We build this as a GUI subsystem application on Win32, so
+ "WinMain()", not "main()", gets called.
+
+ Hack shamelessly stolen from the Win32 port of the GIMP. */
+#ifdef __GNUC__
+#define _stdcall __attribute__((stdcall))
+#endif
+
+int _stdcall
+WinMain (struct HINSTANCE__ *hInstance,
+ struct HINSTANCE__ *hPrevInstance,
+ char *lpszCmdLine,
+ int nCmdShow)
+{
+ INITCOMMONCONTROLSEX comm_ctrl;
+
+ /*
+ * Initialize our DLL search path. MUST be called before LoadLibrary
+ * or g_module_open.
+ */
+ ws_init_dll_search_path();
+
+ /* Initialize our controls. Required for native Windows file dialogs. */
+ memset (&comm_ctrl, 0, sizeof(comm_ctrl));
+ comm_ctrl.dwSize = sizeof(comm_ctrl);
+ /* Includes the animate, header, hot key, list view, progress bar,
+ * status bar, tab, tooltip, toolbar, trackbar, tree view, and
+ * up-down controls
+ */
+ comm_ctrl.dwICC = ICC_WIN95_CLASSES;
+ InitCommonControlsEx(&comm_ctrl);
+
+ /* RichEd20.DLL is needed for filter entries. */
+ ws_load_library("riched20.dll");
+
+ has_console = FALSE;
+ console_wait = FALSE;
+ return main (__argc, __argv);
+}
+
+/* The code to create and desstroy console windows should not be necessary,
+ at least as I read the GLib source code, as it looks as if GLib is, on
+ Win32, *supposed* to create a console window into which to display its
+ output.
+
+ That doesn't happen, however. I suspect there's something completely
+ broken about that code in GLib-for-Win32, and that it may be related
+ to the breakage that forces us to just call "printf()" on the message
+ rather than passing the message on to "g_log_default_handler()"
+ (which is the routine that does the aforementioned non-functional
+ console window creation). */
+
+/*
+ * If this application has no console window to which its standard output
+ * would go, create one.
+ */
+void
+create_console(void)
+{
+ if (stdin_capture) {
+ /* We've been handed "-i -". Don't mess with stdio. */
+ return;
+ }
+
+ if (!has_console) {
+ /* We have no console to which to print the version string, so
+ create one and make it the standard input, output, and error. */
+
+ /*
+ * See if we have an existing console (i.e. we were run from a
+ * command prompt)
+ */
+ if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
+ if (AllocConsole()) {
+ console_wait = TRUE;
+ SetConsoleTitle(_T("Wireshark Debug Console"));
+ } else {
+ return; /* couldn't create console */
+ }
+ }
+
+ ws_freopen("CONIN$", "r", stdin);
+ ws_freopen("CONOUT$", "w", stdout);
+ ws_freopen("CONOUT$", "w", stderr);
+ fprintf(stdout, "\n");
+ fprintf(stderr, "\n");
+
+ /* Now register "destroy_console()" as a routine to be called just
+ before the application exits, so that we can destroy the console
+ after the user has typed a key (so that the console doesn't just
+ disappear out from under them, giving the user no chance to see
+ the message(s) we put in there). */
+ atexit(destroy_console);
+
+ /* Well, we have a console now. */
+ has_console = TRUE;
+ }
+}
+
+static void
+destroy_console(void)
+{
+ if (console_wait) {
+ printf("\n\nPress any key to exit\n");
+ _getch();
+ }
+ FreeConsole();
+}
+#endif /* _WIN32 */
+
+
+static void
+console_log_handler(const char *log_domain, GLogLevelFlags log_level,
+ const char *message, gpointer user_data _U_)
+{
+ time_t curr;
+ struct tm *today;
+ const char *level;
+
+
+ /* ignore log message, if log_level isn't interesting based
+ upon the console log preferences.
+ If the preferences haven't been loaded loaded yet, display the
+ message anyway.
+
+ The default console_log_level preference value is such that only
+ ERROR, CRITICAL and WARNING level messages are processed;
+ MESSAGE, INFO and DEBUG level messages are ignored. */
+ if((log_level & G_LOG_LEVEL_MASK & prefs.console_log_level) == 0 &&
+ prefs.console_log_level != 0) {
+ return;
+ }
+
+#ifdef _WIN32
+ if (prefs.gui_console_open != console_open_never || log_level & G_LOG_LEVEL_ERROR) {
+ /* the user wants a console or the application will terminate immediately */
+ create_console();
+ }
+ if (has_console) {
+ /* For some unknown reason, the above doesn't appear to actually cause
+ anything to be sent to the standard output, so we'll just splat the
+ message out directly, just to make sure it gets out. */
+#endif
+ switch(log_level & G_LOG_LEVEL_MASK) {
+ case G_LOG_LEVEL_ERROR:
+ level = "Err ";
+ break;
+ case G_LOG_LEVEL_CRITICAL:
+ level = "Crit";
+ break;
+ case G_LOG_LEVEL_WARNING:
+ level = "Warn";
+ break;
+ case G_LOG_LEVEL_MESSAGE:
+ level = "Msg ";
+ break;
+ case G_LOG_LEVEL_INFO:
+ level = "Info";
+ break;
+ case G_LOG_LEVEL_DEBUG:
+ level = "Dbg ";
+ break;
+ default:
+ fprintf(stderr, "unknown log_level %u\n", log_level);
+ level = NULL;
+ g_assert_not_reached();
+ }
+
+ /* create a "timestamp" */
+ time(&curr);
+ today = localtime(&curr);
+
+ fprintf(stderr, "%02u:%02u:%02u %8s %s %s\n",
+ today->tm_hour, today->tm_min, today->tm_sec,
+ log_domain != NULL ? log_domain : "",
+ level, message);
+#ifdef _WIN32
+ if(log_level & G_LOG_LEVEL_ERROR) {
+ /* wait for a key press before the following error handler will terminate the program
+ this way the user at least can read the error message */
+ printf("\n\nPress any key to exit\n");
+ _getch();
+ }
+ } else {
+ /* XXX - on UN*X, should we just use g_log_default_handler()?
+ We want the error messages to go to the standard output;
+ on Mac OS X, that will cause them to show up in various
+ per-user logs accessible through Console (details depend
+ on whether you're running 10.0 through 10.4 or running
+ 10.5 and later), and, on other UN*X desktop environments,
+ if they don't show up in some form of console log, that's
+ a deficiency in that desktop environment. (Too bad
+ Windows doesn't set the standard output and error for
+ GUI apps to something that shows up in such a log.) */
+ g_log_default_handler(log_domain, log_level, message, user_data);
+ }
+#endif
+}
+
+
+/*
+ * Helper for main_widgets_rearrange()
+ */
+static void foreach_remove_a_child(GtkWidget *widget, gpointer data) {
+ gtk_container_remove(GTK_CONTAINER(data), widget);
+}
+
+static GtkWidget *main_widget_layout(gint layout_content)
+{
+ switch(layout_content) {
+ case(layout_pane_content_none):
+ return NULL;
+ case(layout_pane_content_plist):
+ return pkt_scrollw;
+ case(layout_pane_content_pdetails):
+ return tv_scrollw;
+ case(layout_pane_content_pbytes):
+ return byte_nb_ptr_gbl;
+ default:
+ g_assert_not_reached();
+ return NULL;
+ }
+}
+
+
+/*
+ * Rearrange the main window widgets
+ */
+void main_widgets_rearrange(void) {
+ GtkWidget *first_pane_widget1, *first_pane_widget2;
+ GtkWidget *second_pane_widget1, *second_pane_widget2;
+ gboolean split_top_left;
+
+ /* be a bit faster */
+ gtk_widget_hide(main_vbox);
+
+ /* be sure we don't lose a widget while rearranging */
+ g_object_ref(G_OBJECT(menubar));
+ g_object_ref(G_OBJECT(main_tb));
+ g_object_ref(G_OBJECT(filter_tb));
+#ifdef HAVE_AIRPCAP
+ g_object_ref(G_OBJECT(airpcap_tb));
+#endif
+ g_object_ref(G_OBJECT(pkt_scrollw));
+ g_object_ref(G_OBJECT(tv_scrollw));
+ g_object_ref(G_OBJECT(byte_nb_ptr_gbl));
+ g_object_ref(G_OBJECT(statusbar));
+ g_object_ref(G_OBJECT(main_pane_v1));
+ g_object_ref(G_OBJECT(main_pane_v2));
+ g_object_ref(G_OBJECT(main_pane_h1));
+ g_object_ref(G_OBJECT(main_pane_h2));
+ g_object_ref(G_OBJECT(welcome_pane));
+
+ /* empty all containers participating */
+ gtk_container_foreach(GTK_CONTAINER(main_vbox), foreach_remove_a_child, main_vbox);
+ gtk_container_foreach(GTK_CONTAINER(main_pane_v1), foreach_remove_a_child, main_pane_v1);
+ gtk_container_foreach(GTK_CONTAINER(main_pane_v2), foreach_remove_a_child, main_pane_v2);
+ gtk_container_foreach(GTK_CONTAINER(main_pane_h1), foreach_remove_a_child, main_pane_h1);
+ gtk_container_foreach(GTK_CONTAINER(main_pane_h2), foreach_remove_a_child, main_pane_h2);
+
+ statusbar_widgets_emptying(statusbar);
+
+ /* add the menubar always at the top */
+ gtk_box_pack_start(GTK_BOX(main_vbox), menubar, FALSE, TRUE, 0);
+
+ /* main toolbar */
+ gtk_box_pack_start(GTK_BOX(main_vbox), main_tb, FALSE, TRUE, 0);
+
+ /* filter toolbar in toolbar area */
+ if (!prefs.filter_toolbar_show_in_statusbar) {
+ gtk_box_pack_start(GTK_BOX(main_vbox), filter_tb, FALSE, TRUE, 1);
+ }
+
+#ifdef HAVE_AIRPCAP
+ /* airpcap toolbar */
+ gtk_box_pack_start(GTK_BOX(main_vbox), airpcap_tb, FALSE, TRUE, 1);
+#endif
+
+ /* fill the main layout panes */
+ switch(prefs.gui_layout_type) {
+ case(layout_type_5):
+ main_first_pane = main_pane_v1;
+ main_second_pane = main_pane_v2;
+ split_top_left = FALSE;
+ break;
+ case(layout_type_2):
+ main_first_pane = main_pane_v1;
+ main_second_pane = main_pane_h1;
+ split_top_left = FALSE;
+ break;
+ case(layout_type_1):
+ main_first_pane = main_pane_v1;
+ main_second_pane = main_pane_h1;
+ split_top_left = TRUE;
+ break;
+ case(layout_type_4):
+ main_first_pane = main_pane_h1;
+ main_second_pane = main_pane_v1;
+ split_top_left = FALSE;
+ break;
+ case(layout_type_3):
+ main_first_pane = main_pane_h1;
+ main_second_pane = main_pane_v1;
+ split_top_left = TRUE;
+ break;
+ case(layout_type_6):
+ main_first_pane = main_pane_h1;
+ main_second_pane = main_pane_h2;
+ split_top_left = FALSE;
+ break;
+ default:
+ main_first_pane = NULL;
+ main_second_pane = NULL;
+ split_top_left = FALSE;
+ g_assert_not_reached();
+ }
+ if (split_top_left) {
+ first_pane_widget1 = main_second_pane;
+ second_pane_widget1 = main_widget_layout(prefs.gui_layout_content_1);
+ second_pane_widget2 = main_widget_layout(prefs.gui_layout_content_2);
+ first_pane_widget2 = main_widget_layout(prefs.gui_layout_content_3);
+ } else {
+ first_pane_widget1 = main_widget_layout(prefs.gui_layout_content_1);
+ first_pane_widget2 = main_second_pane;
+ second_pane_widget1 = main_widget_layout(prefs.gui_layout_content_2);
+ second_pane_widget2 = main_widget_layout(prefs.gui_layout_content_3);
+ }
+ if (first_pane_widget1 != NULL)
+ gtk_paned_add1(GTK_PANED(main_first_pane), first_pane_widget1);
+ if (first_pane_widget2 != NULL)
+ gtk_paned_add2(GTK_PANED(main_first_pane), first_pane_widget2);
+ if (second_pane_widget1 != NULL)
+ gtk_paned_pack1(GTK_PANED(main_second_pane), second_pane_widget1, TRUE, TRUE);
+ if (second_pane_widget2 != NULL)
+ gtk_paned_pack2(GTK_PANED(main_second_pane), second_pane_widget2, FALSE, FALSE);
+
+ gtk_container_add(GTK_CONTAINER(main_vbox), main_first_pane);
+
+ /* welcome pane */
+ gtk_box_pack_start(GTK_BOX(main_vbox), welcome_pane, TRUE, TRUE, 0);
+
+ /* statusbar */
+ gtk_box_pack_start(GTK_BOX(main_vbox), statusbar, FALSE, TRUE, 0);
+
+ /* filter toolbar in statusbar hbox */
+ if (prefs.filter_toolbar_show_in_statusbar) {
+ gtk_box_pack_start(GTK_BOX(statusbar), filter_tb, FALSE, TRUE, 1);
+ }
+
+ /* statusbar widgets */
+ statusbar_widgets_pack(statusbar);
+
+ /* hide widgets on users recent settings */
+ main_widgets_show_or_hide();
+
+ gtk_widget_show(main_vbox);
+}
+
+static void
+is_widget_visible(GtkWidget *widget, gpointer data)
+{
+ gboolean *is_visible = data;
+
+ if (!*is_visible) {
+ if (gtk_widget_get_visible(widget))
+ *is_visible = TRUE;
+ }
+}
+
+
+void
+main_widgets_show_or_hide(void)
+{
+ gboolean main_second_pane_show;
+
+ if (recent.main_toolbar_show) {
+ gtk_widget_show(main_tb);
+ } else {
+ gtk_widget_hide(main_tb);
+ }
+
+ statusbar_widgets_show_or_hide(statusbar);
+
+ if (recent.filter_toolbar_show) {
+ gtk_widget_show(filter_tb);
+ } else {
+ gtk_widget_hide(filter_tb);
+ }
+
+#ifdef HAVE_AIRPCAP
+ if (recent.airpcap_toolbar_show) {
+ gtk_widget_show(airpcap_tb);
+ } else {
+ gtk_widget_hide(airpcap_tb);
+ }
+#endif
+
+ if (recent.packet_list_show && have_capture_file) {
+ gtk_widget_show(pkt_scrollw);
+ } else {
+ gtk_widget_hide(pkt_scrollw);
+ }
+
+ if (recent.tree_view_show && have_capture_file) {
+ gtk_widget_show(tv_scrollw);
+ } else {
+ gtk_widget_hide(tv_scrollw);
+ }
+
+ if (recent.byte_view_show && have_capture_file) {
+ gtk_widget_show(byte_nb_ptr_gbl);
+ } else {
+ gtk_widget_hide(byte_nb_ptr_gbl);
+ }
+
+ if (have_capture_file) {
+ gtk_widget_show(main_first_pane);
+ } else {
+ gtk_widget_hide(main_first_pane);
+ }
+
+ /*
+ * Is anything in "main_second_pane" visible?
+ * If so, show it, otherwise hide it.
+ */
+ main_second_pane_show = FALSE;
+ gtk_container_foreach(GTK_CONTAINER(main_second_pane), is_widget_visible,
+ &main_second_pane_show);
+ if (main_second_pane_show) {
+ gtk_widget_show(main_second_pane);
+ } else {
+ gtk_widget_hide(main_second_pane);
+ }
+
+ if (!have_capture_file) {
+ if(welcome_pane) {
+ gtk_widget_show(welcome_pane);
+ select_ifaces();
+ }
+ } else {
+ gtk_widget_hide(welcome_pane);
+ }
+}
+
+
+/* called, when the window state changes (minimized, maximized, ...) */
+static gboolean
+window_state_event_cb (GtkWidget *widget _U_,
+ GdkEvent *event,
+ gpointer data _U_)
+{
+ GdkWindowState new_window_state = ((GdkEventWindowState*)event)->new_window_state;
+
+ if( (event->type) == (GDK_WINDOW_STATE)) {
+ if(!(new_window_state & GDK_WINDOW_STATE_ICONIFIED)) {
+ /* we might have dialogs popped up while we where iconified,
+ show em now */
+ display_queued_messages();
+ }
+ }
+ return FALSE;
+}
+
+
+
+#define NO_SHIFT_MOD_MASK (GDK_MODIFIER_MASK & ~(GDK_SHIFT_MASK|GDK_MOD2_MASK|GDK_LOCK_MASK))
+static gboolean
+top_level_key_pressed_cb(GtkWidget *w _U_, GdkEventKey *event, gpointer user_data _U_)
+{
+ if (event->keyval == GDK_F8) {
+ new_packet_list_next();
+ return TRUE;
+ } else if (event->keyval == GDK_F7) {
+ new_packet_list_prev();
+ return TRUE;
+ } else if (event->state & NO_SHIFT_MOD_MASK) {
+ return FALSE; /* Skip control, alt, and other modifiers */
+ /*
+ * A comment in gdkkeysyms.h says that it's autogenerated from
+ * freedesktop.org/x.org's keysymdef.h. Although the GDK docs
+ * don't explicitly say so, isprint() should work as expected
+ * for values < 127.
+ */
+ } else if (isascii(event->keyval) && isprint(event->keyval)) {
+ /* Forward the keypress on to the display filter entry */
+ if (main_display_filter_widget && !gtk_widget_is_focus(main_display_filter_widget)) {
+ gtk_window_set_focus(GTK_WINDOW(top_level), main_display_filter_widget);
+ gtk_editable_set_position(GTK_EDITABLE(main_display_filter_widget), -1);
+ }
+ return FALSE;
+ }
+ return FALSE;
+}
+
+static void
+create_main_window (gint pl_size, gint tv_size, gint bv_size, e_prefs *prefs_p)
+{
+ GtkAccelGroup *accel;
+
+ /* Main window */
+ top_level = window_new(GTK_WINDOW_TOPLEVEL, "");
+ set_main_window_name("The Wireshark Network Analyzer");
+
+ gtk_widget_set_name(top_level, "main window");
+ g_signal_connect(top_level, "delete_event", G_CALLBACK(main_window_delete_event_cb),
+ NULL);
+ g_signal_connect(G_OBJECT(top_level), "window_state_event",
+ G_CALLBACK(window_state_event_cb), NULL);
+ g_signal_connect(G_OBJECT(top_level), "key-press-event",
+ G_CALLBACK(top_level_key_pressed_cb), NULL );
+
+ /* Vertical container for menu bar, toolbar(s), paned windows and progress/info box */
+ main_vbox = gtk_vbox_new(FALSE, 1);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vbox), 0);
+ gtk_container_add(GTK_CONTAINER(top_level), main_vbox);
+ gtk_widget_show(main_vbox);
+
+ /* Menu bar */
+ menubar = main_menu_new(&accel);
+
+#if defined(HAVE_IGE_MAC_INTEGRATION) || defined (HAVE_GTKOSXAPPLICATION)
+ /* Mac OS X native menus are created and displayed by main_menu_new() */
+ if(!prefs_p->gui_macosx_style) {
+#endif
+ gtk_window_add_accel_group(GTK_WINDOW(top_level), accel);
+ gtk_widget_show(menubar);
+#if defined(HAVE_IGE_MAC_INTEGRATION) || defined(HAVE_GTKOSXAPPLICATION)
+ }
+#endif
+
+ /* Main Toolbar */
+ main_tb = toolbar_new();
+ gtk_widget_show (main_tb);
+
+ /* Filter toolbar */
+ filter_tb = filter_toolbar_new();
+
+ /* Packet list */
+ pkt_scrollw = new_packet_list_create();
+ gtk_widget_set_size_request(pkt_scrollw, -1, pl_size);
+ gtk_widget_show_all(pkt_scrollw);
+
+ /* Tree view */
+ tv_scrollw = main_tree_view_new(prefs_p, &tree_view_gbl);
+ gtk_widget_set_size_request(tv_scrollw, -1, tv_size);
+ gtk_widget_show(tv_scrollw);
+
+ g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view_gbl)),
+ "changed", G_CALLBACK(tree_view_selection_changed_cb), NULL);
+ g_signal_connect(tree_view_gbl, "button_press_event", G_CALLBACK(popup_menu_handler),
+ g_object_get_data(G_OBJECT(popup_menu_object), PM_TREE_VIEW_KEY));
+ gtk_widget_show(tree_view_gbl);
+
+ /* Byte view. */
+ byte_nb_ptr_gbl = byte_view_new();
+ gtk_widget_set_size_request(byte_nb_ptr_gbl, -1, bv_size);
+ gtk_widget_show(byte_nb_ptr_gbl);
+
+ g_signal_connect(byte_nb_ptr_gbl, "button_press_event", G_CALLBACK(popup_menu_handler),
+ g_object_get_data(G_OBJECT(popup_menu_object), PM_BYTES_VIEW_KEY));
+
+ /* Panes for the packet list, tree, and byte view */
+ main_pane_v1 = gtk_vpaned_new();
+ gtk_widget_show(main_pane_v1);
+ main_pane_v2 = gtk_vpaned_new();
+ gtk_widget_show(main_pane_v2);
+ main_pane_h1 = gtk_hpaned_new();
+ gtk_widget_show(main_pane_h1);
+ main_pane_h2 = gtk_hpaned_new();
+ gtk_widget_show(main_pane_h2);
+#ifdef HAVE_AIRPCAP
+ airpcap_tb = airpcap_toolbar_new();
+ gtk_widget_show(airpcap_tb);
+#endif
+ /* status bar */
+ statusbar = statusbar_new();
+ gtk_widget_show(statusbar);
+
+ /* Pane for the welcome screen */
+ welcome_pane = welcome_new();
+ gtk_widget_show(welcome_pane);
+}
+
+static void
+show_main_window(gboolean doing_work)
+{
+ main_set_for_capture_file(doing_work);
+
+ /*** we have finished all init things, show the main window ***/
+ gtk_widget_show(top_level);
+
+ /* the window can be maximized only, if it's visible, so do it after show! */
+ main_load_window_geometry(top_level);
+
+ /* process all pending GUI events before continue */
+ while (gtk_events_pending()) gtk_main_iteration();
+
+ /* Pop up any queued-up alert boxes. */
+ display_queued_messages();
+
+ /* Move the main window to the front, in case it isn't already there */
+ gdk_window_raise(gtk_widget_get_window(top_level));
+
+#ifdef HAVE_AIRPCAP
+ airpcap_toolbar_show(airpcap_tb);
+#endif /* HAVE_AIRPCAP */
+}
+
+/* Fill in capture options with values from the preferences */
+void
+prefs_to_capture_opts(void)
+{
+#ifdef HAVE_LIBPCAP
+ /* Set promiscuous mode from the preferences setting. */
+ /* the same applies to other preferences settings as well. */
+ global_capture_opts.default_options.promisc_mode = prefs.capture_prom_mode;
+ global_capture_opts.use_pcapng = prefs.capture_pcap_ng;
+ global_capture_opts.show_info = prefs.capture_show_info;
+ global_capture_opts.real_time_mode = prefs.capture_real_time;
+ auto_scroll_live = prefs.capture_auto_scroll;
+#endif /* HAVE_LIBPCAP */
+
+ /* Set the name resolution code's flags from the preferences. */
+ gbl_resolv_flags = prefs.name_resolve;
+}
+
+static void copy_global_profile (const gchar *profile_name)
+{
+ char *pf_dir_path, *pf_dir_path2, *pf_filename;
+
+ if (create_persconffile_profile(profile_name, &pf_dir_path) == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't create directory\n\"%s\":\n%s.",
+ pf_dir_path, g_strerror(errno));
+
+ g_free(pf_dir_path);
+ }
+
+ if (copy_persconffile_profile(profile_name, profile_name, TRUE, &pf_filename,
+ &pf_dir_path, &pf_dir_path2) == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't copy file \"%s\" in directory\n\"%s\" to\n\"%s\":\n%s.",
+ pf_filename, pf_dir_path2, pf_dir_path, g_strerror(errno));
+
+ g_free(pf_filename);
+ g_free(pf_dir_path);
+ g_free(pf_dir_path2);
+ }
+}
+
+/* Change configuration profile */
+void change_configuration_profile (const gchar *profile_name)
+{
+ char *gdp_path, *dp_path;
+ char *rf_path;
+ int rf_open_errno;
+
+ /* First check if profile exists */
+ if (!profile_exists(profile_name, FALSE)) {
+ if (profile_exists(profile_name, TRUE)) {
+ /* Copy from global profile */
+ copy_global_profile (profile_name);
+ } else {
+ /* No personal and no global profile exists */
+ return;
+ }
+ }
+
+ /* Then check if changing to another profile */
+ if (profile_name && strcmp (profile_name, get_profile_name()) == 0) {
+ return;
+ }
+
+ /* Get the current geometry, before writing it to disk */
+ main_save_window_geometry(top_level);
+
+ if (profile_exists(get_profile_name(), FALSE)) {
+ /* Write recent file for profile we are leaving, if it still exists */
+ write_profile_recent();
+ }
+
+ /* Set profile name and update the status bar */
+ set_profile_name (profile_name);
+ profile_bar_update ();
+ filter_expression_reinit(FILTER_EXPRESSION_REINIT_DESTROY);
+
+ /* Reset current preferences and apply the new */
+ prefs_reset();
+ menu_prefs_reset();
+
+ (void) read_configuration_files (&gdp_path, &dp_path);
+
+ recent_read_profile_static(&rf_path, &rf_open_errno);
+ if (rf_path != NULL && rf_open_errno != 0) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Could not open common recent file\n\"%s\": %s.",
+ rf_path, g_strerror(rf_open_errno));
+ }
+ if (recent.gui_fileopen_remembered_dir &&
+ test_for_directory(recent.gui_fileopen_remembered_dir) == EISDIR) {
+ set_last_open_dir(recent.gui_fileopen_remembered_dir);
+ }
+ timestamp_set_type (recent.gui_time_format);
+ timestamp_set_seconds_type (recent.gui_seconds_format);
+ color_filters_enable(recent.packet_list_colorize);
+
+ prefs_to_capture_opts();
+ prefs_apply_all();
+ macros_post_update();
+
+ /* Update window view and redraw the toolbar */
+ update_main_window_title();
+ filter_expression_reinit(FILTER_EXPRESSION_REINIT_CREATE);
+ toolbar_redraw_all();
+
+ /* Enable all protocols and disable from the disabled list */
+ proto_enable_all();
+ if (gdp_path == NULL && dp_path == NULL) {
+ set_disabled_protos_list();
+ }
+
+ /* Reload color filters */
+ color_filters_reload();
+
+ /* Reload list of interfaces on welcome page */
+ welcome_if_panel_reload();
+
+ /* Recreate the packet list according to new preferences */
+ new_packet_list_recreate ();
+ cfile.cinfo.columns_changed = FALSE; /* Reset value */
+ user_font_apply();
+
+ /* Update menus with new recent values */
+ menu_recent_read_finished();
+
+ /* Reload pane geometry, must be done after recreating the list */
+ main_pane_load_window_geometry();
+}
+
+/** redissect packets and update UI */
+void redissect_packets(void)
+{
+ cf_redissect_packets(&cfile);
+ status_expert_update();
+}
diff --git a/ui/gtk/main.h b/ui/gtk/main.h
new file mode 100644
index 0000000000..f944ab2bb6
--- /dev/null
+++ b/ui/gtk/main.h
@@ -0,0 +1,366 @@
+/* main.h
+ * Global defines, etc.
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MAIN_H__
+#define __MAIN_H__
+
+#include "globals.h"
+
+/** @defgroup main_window_group Main window
+ * The main window has the following submodules:
+ @dot
+ digraph main_dependencies {
+ node [shape=record, fontname=Helvetica, fontsize=10];
+ main [ label="main window" URL="\ref main.h"];
+ menu [ label="menubar" URL="\ref menus.h"];
+ toolbar [ label="toolbar" URL="\ref main_toolbar.h"];
+ packet_list [ label="packet list pane" URL="\ref new_packet_list.h"];
+ proto_draw [ label="packet details & bytes panes" URL="\ref main_proto_draw.h"];
+ recent [ label="recent user settings" URL="\ref recent.h"];
+ main -> menu [ arrowhead="open", style="solid" ];
+ main -> toolbar [ arrowhead="open", style="solid" ];
+ main -> packet_list [ arrowhead="open", style="solid" ];
+ main -> proto_draw [ arrowhead="open", style="solid" ];
+ main -> recent [ arrowhead="open", style="solid" ];
+ }
+ @enddot
+ */
+
+/** @file
+ * The main window, filter toolbar, program start/stop and a lot of other things
+ * @ingroup main_window_group
+ * @ingroup windows_group
+ */
+
+/** Global compile time version string */
+extern GString *comp_info_str;
+/** Global runtime version string */
+extern GString *runtime_info_str;
+
+extern GtkWidget* airpcap_tb;
+
+extern void protect_thread_critical_region(void);
+extern void unprotect_thread_critical_region(void);
+
+void
+airpcap_toolbar_encryption_cb(GtkWidget *entry, gpointer user_data);
+
+/** User requested "Zoom In" by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void view_zoom_in_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Zoom Out" by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void view_zoom_out_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Zoom 100%" by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void view_zoom_100_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Protocol Info" by ptree context menu.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void selected_ptree_info_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Filter Reference" by ptree context menu.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void selected_ptree_ref_cb(GtkWidget *widget, gpointer data);
+
+/** "Apply as Filter" / "Prepare a Filter" action type. */
+typedef enum {
+ MATCH_SELECTED_REPLACE, /**< "Selected" */
+ MATCH_SELECTED_AND, /**< "and Selected" */
+ MATCH_SELECTED_OR, /**< "or Selected" */
+ MATCH_SELECTED_NOT, /**< "Not Selected" */
+ MATCH_SELECTED_AND_NOT, /**< "and not Selected" */
+ MATCH_SELECTED_OR_NOT /**< "or not Selected" */
+} MATCH_SELECTED_E;
+
+/** mask MATCH_SELECTED_E values (internally used) */
+#define MATCH_SELECTED_MASK 0x0ff
+
+/** "bitwise or" this with MATCH_SELECTED_E value for instant apply instead of prepare only */
+#define MATCH_SELECTED_APPLY_NOW 0x100
+
+/** "bitwise or" this with MATCH_SELECTED_E value for copy to clipboard instead of prepare only */
+#define MATCH_SELECTED_COPY_ONLY 0x200
+
+/** User requested one of "Apply as Filter" or "Prepare a Filter" functions
+ * by menu or context menu of protocol tree.
+ *
+ * @param widget parent widget
+ * @param data parent widget
+ * @param action the function to use
+ */
+extern void match_selected_ptree_cb(GtkWidget *widget, gpointer data, MATCH_SELECTED_E action);
+
+/** "Copy ..." action type. */
+typedef enum {
+ COPY_SELECTED_DESCRIPTION, /**< "Copy Description" */
+ COPY_SELECTED_FIELDNAME, /**< "Copy Fieldname" */
+ COPY_SELECTED_VALUE /**< "Copy Value" */
+} COPY_SELECTED_E;
+
+/** User highlighted item in details window and then right clicked and selected the copy option
+ *
+ * @param w parent widget
+ * @param data parent widget
+ * @param action the function to use
+ */
+extern void copy_selected_plist_cb(GtkWidget *w _U_, gpointer data, COPY_SELECTED_E action);
+
+/** Set or remove a time reference on this frame
+ *
+ * @param set TRUE = set time ref, FALSE=unset time ref
+ * @param frame pointer to frame
+ * @param row row number
+ */
+extern void set_frame_reftime(gboolean set, frame_data *frame, gint row);
+
+/** User requested the colorize function
+ * by menu or context menu of protocol tree.
+ *
+ * @param w parent widget
+ * @param data parent widget
+ * @param filt_nr 1-10: use filter for color 1-10
+ * 0: open new colorization rule dialog
+ * 255: clear filters for color 1-10
+ */
+extern void colorize_selected_ptree_cb(GtkWidget *w, gpointer data, guint8 filt_nr);
+
+/** User requested one of "Apply as Filter" or "Prepare a Filter" functions
+ * by context menu of packet list.
+ *
+ * @param widget parent widget (unused)
+ * @param data parent widget
+ * @param action the function to use
+ */
+extern void match_selected_plist_cb(GtkWidget *widget, gpointer data, MATCH_SELECTED_E action);
+
+/** User requested "Quit" by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void file_quit_cmd_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Print" by menu or toolbar.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void file_print_cmd_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Print" by packet list context menu.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void file_print_selected_cmd_cb(GtkWidget *widget _U_, gpointer data _U_);
+
+/** User requested "Export as Plain Text" by menu.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void export_text_cmd_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Export as Postscript" by menu.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void export_ps_cmd_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Export as PSML" by menu.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void export_psml_cmd_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Export as PDML" by menu.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void export_pdml_cmd_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Export as CSV" by menu.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void export_csv_cmd_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Export as C Arrays" by menu.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void export_carrays_cmd_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Expand Tree" by menu.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void expand_tree_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Expand All" by menu.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void expand_all_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Apply as a custom column" by menu.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void apply_as_custom_column_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Collapse All" by menu.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void collapse_all_cb(GtkWidget *widget, gpointer data);
+
+/** User requested "Resolve Name" by menu.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void resolve_name_cb(GtkWidget *widget, gpointer data);
+
+/** Action to take for reftime_frame_cb() */
+typedef enum {
+ REFTIME_TOGGLE, /**< toggle ref frame */
+ REFTIME_FIND_NEXT, /**< find next ref frame */
+ REFTIME_FIND_PREV /**< find previous ref frame */
+} REFTIME_ACTION_E;
+
+/** User requested one of the "Time Reference" functions by menu.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ * @param action the function to use
+ */
+extern void reftime_frame_cb(GtkWidget *widget, gpointer data, REFTIME_ACTION_E action);
+
+/** User requested the "Find Next Mark" function by menu.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ * @param action unused
+ */
+extern void find_next_mark_cb(GtkWidget *widget, gpointer data, int action);
+
+/** User requested the "Find Previous Mark" function by menu.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ * @param action unused
+ */
+extern void find_prev_mark_cb(GtkWidget *widget, gpointer data, int action);
+
+/** Add a display filter coming from the user's recent file to the dfilter combo box.
+ *
+ * @param dftext the filter string
+ */
+extern gboolean dfilter_combo_add_recent(gchar *dftext);
+
+#if 0
+/** Empty out the combobox entry field */
+extern void dfilter_combo_add_empty(void);
+#endif
+/** Write all non empty display filters (until maximum count)
+ * of the combo box GList to the user's recent file.
+ *
+ * @param rf the recent file
+ */
+extern void dfilter_recent_combo_write_all(FILE *rf);
+
+/** Quit the program.
+ *
+ * @return TRUE, if a file read is in progress
+ */
+extern gboolean main_do_quit(void);
+
+/** Rearrange the main window widgets, user changed it's preferences. */
+extern void main_widgets_rearrange(void);
+
+/** Show or hide the main window widgets, user changed it's preferences. */
+extern void main_widgets_show_or_hide(void);
+
+/** Apply a new filter string.
+ * Call cf_filter_packets() and add this filter string to the recent filter list.
+ *
+ * @param cf the capture file
+ * @param dftext the new filter string
+ * @param force force the refiltering, even if filter string doesn't changed
+ * @return TRUE, if the filtering succeeded
+ */
+extern gboolean main_filter_packets(capture_file *cf, const gchar *dftext,
+ gboolean force);
+
+#ifdef _WIN32
+/** Win32 only: Create a console. Beware: cannot be closed again. */
+extern void create_console(void);
+#endif
+
+/** Restart the tap update display timer with new configured interval */
+extern void reset_tap_update_timer(void);
+
+/** Fill in capture options with values from the preferences */
+extern void prefs_to_capture_opts(void);
+
+/** Change configuration profile */
+extern void change_configuration_profile(const gchar *profile_name);
+
+/** redissect packets and update UI */
+extern void redissect_packets(void);
+
+/** Fetch all IP addresses from selected row */
+extern GList *get_ip_address_list_from_packet_list_row(gpointer data);
+
+extern GtkWidget *pkt_scrollw;
+
+#endif /* __MAIN_H__ */
diff --git a/ui/gtk/main_airpcap_toolbar.c b/ui/gtk/main_airpcap_toolbar.c
new file mode 100644
index 0000000000..fb016d6b10
--- /dev/null
+++ b/ui/gtk/main_airpcap_toolbar.c
@@ -0,0 +1,430 @@
+/* main_airpcap_toolbar.c
+ * The airpcap toolbar
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * This file implements the wireless toolbar for Wireshark.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_AIRPCAP
+
+#include <gtk/gtk.h>
+#include "ui/gtk/old-gtk-compat.h"
+
+#include <epan/epan.h>
+#include <epan/frequency-utils.h>
+
+#include "simple_dialog.h"
+#include "main.h"
+#include "main_airpcap_toolbar.h"
+
+#include "recent.h"
+#include "keys.h"
+
+#include <airpcap.h>
+#include "airpcap_loader.h"
+#include "airpcap_dlg.h"
+#include "airpcap_gui_utils.h"
+
+#include <epan/crypt/airpdcap_ws.h>
+
+
+gboolean block_toolbar_signals = FALSE;
+static GtkWidget *driver_warning_dialog;
+
+
+/*
+ * Callback for the wrong crc combo
+ */
+static void
+airpcap_toolbar_fcs_filter_combo_cb(GtkWidget *fcs_filter_cb, gpointer user_data _U_)
+{
+ PAirpcapHandle ad;
+ gchar ebuf[AIRPCAP_ERRBUF_SIZE];
+ gchar *fcs_filter_str;
+
+ if (fcs_filter_cb != NULL && !block_toolbar_signals && (airpcap_if_active != NULL)) {
+ fcs_filter_str = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(fcs_filter_cb));
+ ad = airpcap_if_open(airpcap_if_active->name, ebuf);
+
+ if (fcs_filter_str && (g_ascii_strcasecmp("", fcs_filter_str)) && ad) {
+ airpcap_if_selected->CrcValidationOn = airpcap_get_validation_type(fcs_filter_str);
+ airpcap_if_selected->saved = FALSE;
+ airpcap_if_set_fcs_validation(ad,airpcap_if_active->CrcValidationOn);
+ /* Save configuration */
+ airpcap_if_store_cur_config_as_adapter_default(ad);
+ airpcap_if_close(ad);
+ }
+ g_free(fcs_filter_str);
+ }
+}
+
+void
+airpcap_toolbar_encryption_cb(GtkWidget *entry _U_, gpointer user_data _U_)
+{
+ /* We need to directly access the .dll functions here... */
+ gchar ebuf[AIRPCAP_ERRBUF_SIZE];
+ PAirpcapHandle ad;
+
+ gint n = 0;
+ gint i = 0;
+ airpcap_if_info_t* curr_if = NULL;
+
+ /* Apply changes to the current adapter */
+ if( (airpcap_if_active != NULL)) {
+ ad = airpcap_if_open(airpcap_if_active->name, ebuf);
+
+ if(ad) {
+ if(airpcap_if_active->DecryptionOn == AIRPCAP_DECRYPTION_ON) {
+ airpcap_if_active->DecryptionOn = AIRPCAP_DECRYPTION_OFF;
+ airpcap_if_set_decryption_state(ad,airpcap_if_active->DecryptionOn);
+ /* Save configuration */
+ if(!airpcap_if_store_cur_config_as_adapter_default(ad)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Cannot save configuration!!!\nRemember that in order to store the configuration in the registry you have to:\n\n- Close all the airpcap-based applications.\n- Be sure to have administrative privileges.");
+ }
+ airpcap_if_close(ad);
+ } else {
+ airpcap_if_active->DecryptionOn = AIRPCAP_DECRYPTION_ON;
+ airpcap_if_set_decryption_state(ad,airpcap_if_active->DecryptionOn);
+ /* Save configuration */
+ if(!airpcap_if_store_cur_config_as_adapter_default(ad)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Cannot save configuration!!!\nRemember that in order to store the configuration in the registry you have to:\n\n- Close all the airpcap-based applications.\n- Be sure to have administrative privileges.");
+ }
+ airpcap_if_close(ad);
+ }
+ }
+ } else {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No active AirPcap Adapter selected!");
+ return;
+ }
+
+ if (!(airpcap_if_list == NULL)) {
+ n = g_list_length(airpcap_if_list);
+
+ /* The same kind of settings should be propagated to all the adapters */
+ /* Apply this change to all the adapters !!! */
+ for(i = 0; i < n; i++) {
+ curr_if = (airpcap_if_info_t*)g_list_nth_data(airpcap_if_list,i);
+
+ if( (curr_if != NULL) && (curr_if != airpcap_if_selected) ) {
+ ad = airpcap_if_open(curr_if->name, ebuf);
+ if(ad) {
+ curr_if->DecryptionOn = airpcap_if_selected->DecryptionOn;
+ airpcap_if_set_decryption_state(ad,curr_if->DecryptionOn);
+ /* Save configuration for the curr_if */
+ if(!airpcap_if_store_cur_config_as_adapter_default(ad)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Cannot save configuration!!!\nRemember that in order to store the configuration in the registry you have to:\n\n- Close all the airpcap-based applications.\n- Be sure to have administrative privileges.");
+ }
+ airpcap_if_close(ad);
+ }
+ }
+ }
+ } else {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "AirPcap Adapter Error!");
+ return;
+ }
+}
+
+/*
+ * Callback for the Advanced Wireless Settings button
+ */
+static void
+toolbar_display_airpcap_advanced_cb(GtkWidget *w, gpointer data)
+{
+ int *from_widget;
+
+ from_widget = (gint*)g_malloc(sizeof(gint));
+ *from_widget = AIRPCAP_ADVANCED_FROM_TOOLBAR;
+ g_object_set_data(G_OBJECT(airpcap_tb),AIRPCAP_ADVANCED_FROM_KEY,from_widget);
+
+ display_airpcap_advanced_cb(w,data);
+}
+
+/*
+ * Callback for the Decryption Key Management button
+ */
+static void
+toolbar_display_airpcap_key_management_cb(GtkWidget *w, gpointer data)
+{
+ int *from_widget;
+
+ from_widget = (gint*)g_malloc(sizeof(gint));
+ *from_widget = AIRPCAP_ADVANCED_FROM_TOOLBAR;
+ g_object_set_data(G_OBJECT(airpcap_tb), AIRPCAP_ADVANCED_FROM_KEY, from_widget);
+
+ display_airpcap_key_management_cb(w,data);
+}
+
+GtkWidget *airpcap_toolbar_new(void)
+{
+ GtkWidget *channel_lb = NULL,
+ *channel_cb = NULL,
+ *channel_offset_lb = NULL,
+ *channel_offset_cb = NULL,
+ *fcs_filter_lb = NULL,
+ *fcs_filter_cb = NULL;
+ GtkWidget *airpcap_tb;
+
+ GtkWidget *decryption_mode_lb;
+ GtkWidget *decryption_mode_cb;
+
+ GtkToolItem *key_management_bt = NULL,
+ *advanced_bt = NULL,
+ *tool_item;
+
+ /* airpcap toolbar */
+ airpcap_tb = gtk_toolbar_new();
+ gtk_orientable_set_orientation(GTK_ORIENTABLE(airpcap_tb),
+ GTK_ORIENTATION_HORIZONTAL);
+
+ /* Create the "802.11 Channel:" label */
+ channel_lb = gtk_label_new("802.11 Channel: ");
+ g_object_set_data(G_OBJECT(airpcap_tb), AIRPCAP_TOOLBAR_CHANNEL_LABEL_KEY, channel_lb);
+ gtk_widget_show(channel_lb);
+
+ gtk_widget_set_size_request(channel_lb, 85, -1);
+
+ tool_item = gtk_tool_item_new ();
+ gtk_container_add (GTK_CONTAINER (tool_item), channel_lb);
+ gtk_widget_show (GTK_WIDGET (tool_item));
+ gtk_toolbar_insert(GTK_TOOLBAR(airpcap_tb), tool_item, -1);
+
+ gtk_widget_set_tooltip_text(GTK_WIDGET(tool_item), "Current 802.11 Channel");
+
+ /* Create the channel combo box */
+ channel_cb = gtk_combo_box_text_new();
+ g_object_set_data(G_OBJECT(airpcap_tb), AIRPCAP_TOOLBAR_CHANNEL_KEY, channel_cb);
+
+ /* Select the current channel */
+ airpcap_update_channel_combo(GTK_WIDGET(channel_cb), airpcap_if_selected);
+
+ gtk_widget_set_size_request(channel_cb, 120, -1);
+
+ gtk_widget_show(channel_cb);
+
+ tool_item = gtk_tool_item_new ();
+ gtk_container_add (GTK_CONTAINER (tool_item), channel_cb);
+ gtk_widget_show (GTK_WIDGET (tool_item));
+ gtk_toolbar_insert(GTK_TOOLBAR(airpcap_tb), tool_item, -1);
+
+ gtk_widget_set_tooltip_text(GTK_WIDGET(tool_item), "802.11 Channel");
+
+ /* Create the "Channel Offset:" label */
+ channel_offset_lb = gtk_label_new("Channel Offset: ");
+ g_object_set_data(G_OBJECT(airpcap_tb), AIRPCAP_TOOLBAR_CHANNEL_OFFSET_LABEL_KEY, channel_offset_lb);
+ gtk_widget_show(channel_offset_lb);
+
+ gtk_widget_set_size_request(channel_offset_lb, 90, -1);
+ tool_item = gtk_tool_item_new ();
+ gtk_container_add (GTK_CONTAINER (tool_item), channel_offset_lb);
+ gtk_widget_show (GTK_WIDGET (tool_item));
+ gtk_toolbar_insert(GTK_TOOLBAR(airpcap_tb), tool_item, -1);
+
+ gtk_widget_set_tooltip_text(GTK_WIDGET(tool_item), "Current 802.11 Channel Offset");
+
+ /* Start: Channel offset combo box */
+ channel_offset_cb = gtk_combo_box_text_new();
+ g_object_set_data(G_OBJECT(airpcap_tb), AIRPCAP_TOOLBAR_CHANNEL_OFFSET_KEY, channel_offset_cb);
+
+ if(airpcap_if_active != NULL){
+ airpcap_update_channel_offset_combo(airpcap_if_active, airpcap_if_active->channelInfo.Frequency, channel_offset_cb, FALSE);
+ } else {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(channel_offset_cb), -1);
+ }
+
+ gtk_widget_set_tooltip_text(channel_offset_cb, "Current 802.11 Channel Offset");
+
+ gtk_widget_set_size_request(channel_offset_cb, 50, -1);
+
+ gtk_widget_show(channel_offset_cb);
+
+ tool_item = gtk_tool_item_new ();
+ gtk_container_add (GTK_CONTAINER (tool_item), channel_offset_cb);
+ gtk_widget_show (GTK_WIDGET (tool_item));
+ gtk_toolbar_insert(GTK_TOOLBAR(airpcap_tb), tool_item, -1);
+
+ /* callback for channel combo box */
+ g_signal_connect(channel_cb,"changed", G_CALLBACK(airpcap_channel_changed_set_cb), channel_offset_cb);
+ /* callback for channel offset combo box */
+ g_signal_connect(channel_offset_cb, "changed", G_CALLBACK(airpcap_channel_offset_changed_cb), NULL);
+ /* End: Channel offset combo box */
+
+ /* Wrong CRC Label */
+ fcs_filter_lb = gtk_label_new(" FCS Filter: ");
+ g_object_set_data(G_OBJECT(airpcap_tb), AIRPCAP_TOOLBAR_FCS_FILTER_LABEL_KEY, fcs_filter_lb);
+ gtk_widget_show(fcs_filter_lb);
+ tool_item = gtk_tool_item_new ();
+ gtk_container_add (GTK_CONTAINER (tool_item), fcs_filter_lb);
+ gtk_widget_show (GTK_WIDGET (tool_item));
+ gtk_toolbar_insert(GTK_TOOLBAR(airpcap_tb), tool_item, -1);
+
+ /* FCS filter combo box */
+ fcs_filter_cb = gtk_combo_box_text_new();
+ g_object_set_data(G_OBJECT(airpcap_tb), AIRPCAP_TOOLBAR_FCS_FILTER_KEY, fcs_filter_cb);
+
+ gtk_widget_set_size_request(fcs_filter_cb, 100, -1);
+
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(fcs_filter_cb), airpcap_get_validation_name(AIRPCAP_VT_ACCEPT_EVERYTHING));
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(fcs_filter_cb), airpcap_get_validation_name(AIRPCAP_VT_ACCEPT_CORRECT_FRAMES));
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(fcs_filter_cb), airpcap_get_validation_name(AIRPCAP_VT_ACCEPT_CORRUPT_FRAMES));
+ gtk_combo_box_set_active(GTK_COMBO_BOX(fcs_filter_cb), 0);
+
+ gtk_widget_set_tooltip_text(fcs_filter_cb, "Select the 802.11 FCS filter that the wireless adapter will apply.");
+
+ if (airpcap_if_selected != NULL) {
+ airpcap_validation_type_combo_set_by_type(fcs_filter_cb, airpcap_if_selected->CrcValidationOn);
+ }
+
+ g_signal_connect (fcs_filter_cb, "changed", G_CALLBACK(airpcap_toolbar_fcs_filter_combo_cb), NULL);
+ gtk_widget_show(fcs_filter_cb);
+
+ tool_item = gtk_tool_item_new ();
+ gtk_container_add (GTK_CONTAINER (tool_item), fcs_filter_cb);
+ gtk_widget_show (GTK_WIDGET (tool_item));
+ gtk_toolbar_insert(GTK_TOOLBAR(airpcap_tb), tool_item, -1);
+
+ /* Decryption mode combo box */
+ decryption_mode_lb = gtk_label_new ("Decryption Mode: ");
+ g_object_set_data(G_OBJECT(airpcap_tb), AIRPCAP_TOOLBAR_DECRYPTION_LABEL_KEY, decryption_mode_lb);
+ gtk_widget_set_name (decryption_mode_lb, "decryption_mode_lb");
+ gtk_widget_show (decryption_mode_lb);
+
+ decryption_mode_cb = gtk_combo_box_text_new();
+ gtk_widget_set_name (decryption_mode_cb, "decryption_mode_cb");
+ gtk_widget_show (decryption_mode_cb);
+ gtk_widget_set_size_request(decryption_mode_cb, 83, -1);
+ update_decryption_mode_list(decryption_mode_cb);
+
+ tool_item = gtk_tool_item_new ();
+ gtk_container_add (GTK_CONTAINER (tool_item), decryption_mode_cb);
+ gtk_widget_show (GTK_WIDGET (tool_item));
+ gtk_toolbar_insert(GTK_TOOLBAR(airpcap_tb), tool_item, -1);
+
+ gtk_widget_set_tooltip_text(fcs_filter_lb, "Choose a Decryption Mode");
+ /* Set current decryption mode!!!! */
+ update_decryption_mode(decryption_mode_cb);
+ g_signal_connect(decryption_mode_cb, "changed", G_CALLBACK(on_decryption_mode_cb_changed), NULL);
+ g_object_set_data(G_OBJECT(airpcap_tb), AIRPCAP_TOOLBAR_DECRYPTION_KEY, decryption_mode_cb);
+
+ /* Advanced button */
+ advanced_bt = gtk_tool_button_new(NULL, /* a widget that will be used as icon widget, or NULL */
+ "Wireless Settings...");
+ g_object_set_data(G_OBJECT(airpcap_tb), AIRPCAP_TOOLBAR_ADVANCED_KEY, advanced_bt);
+
+ g_signal_connect(advanced_bt, "clicked", G_CALLBACK(toolbar_display_airpcap_advanced_cb), airpcap_tb);
+ gtk_widget_show(GTK_WIDGET(advanced_bt));
+ gtk_toolbar_insert(GTK_TOOLBAR(airpcap_tb), advanced_bt, -1);
+
+ gtk_widget_set_tooltip_text(GTK_WIDGET(advanced_bt), "Set Advanced Wireless Settings");
+ /* Key Management button */
+ key_management_bt = gtk_tool_button_new(NULL, /* a widget that will be used as icon widget, or NULL */
+ "Decryption Keys...");
+
+ g_object_set_data(G_OBJECT(airpcap_tb), AIRPCAP_TOOLBAR_KEY_MANAGEMENT_KEY, key_management_bt);
+
+ g_signal_connect(key_management_bt, "clicked", G_CALLBACK(toolbar_display_airpcap_key_management_cb), airpcap_tb);
+ gtk_widget_show(GTK_WIDGET(key_management_bt));
+ gtk_toolbar_insert(GTK_TOOLBAR(airpcap_tb), key_management_bt, -1);
+ gtk_widget_set_tooltip_text(GTK_WIDGET(key_management_bt), "Manage Decryption Keys");
+
+ /* If no airpcap interface is present, gray everything */
+ if(airpcap_if_active == NULL) {
+ if(airpcap_if_list == NULL || g_list_length(airpcap_if_list) == 0) {
+ /* No airpcap device found */
+ airpcap_enable_toolbar_widgets(airpcap_tb, FALSE);
+ /* recent.airpcap_toolbar_show = TRUE; */
+ } else {
+ /* default adapter is not airpcap... or is airpcap but is not found*/
+ airpcap_set_toolbar_stop_capture(airpcap_if_active);
+ airpcap_enable_toolbar_widgets(airpcap_tb, FALSE);
+ /* recent.airpcap_toolbar_show = TRUE; */
+ }
+ } else {
+ airpcap_set_toolbar_stop_capture(airpcap_if_active);
+ /* recent.airpcap_toolbar_show = TRUE; */
+ }
+
+ return airpcap_tb;
+}
+
+static void
+driver_warning_dialog_cb(gpointer dialog, gint btn _U_, gpointer data _U_)
+{
+ gboolean r;
+
+ r = simple_dialog_check_get(dialog);
+ recent.airpcap_driver_check_show = !r;
+}
+
+void airpcap_toolbar_show(GtkWidget *airpcap_tb _U_)
+{
+ /*
+ * This will read the decryption keys from the preferences file, and will
+ * store them into the registry...
+ */
+ if(airpcap_if_list != NULL && g_list_length(airpcap_if_list) > 0){
+ if (!airpcap_check_decryption_keys(airpcap_if_list)) {
+ /* Ask the user what to do ...*/
+ airpcap_keys_check_w(NULL,NULL);
+ } else {
+ /* Keys from lists are equals, or Wireshark has got no keys */
+ airpcap_load_decryption_keys(airpcap_if_list);
+ }
+ }
+
+ switch (airpcap_dll_ret_val) {
+
+ case AIRPCAP_DLL_OK:
+ break;
+
+ case AIRPCAP_DLL_OLD:
+ if(recent.airpcap_driver_check_show) {
+ driver_warning_dialog = simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s",
+ "WARNING: The version of AirPcap on this system\n"
+ "does not support driver-level decryption. Please\n"
+ "download a more recent version from\n" "http://www.cacetech.com/support/downloads.htm \n");
+ simple_dialog_check_set(driver_warning_dialog,"Don't show this message again.");
+ simple_dialog_set_cb(driver_warning_dialog, driver_warning_dialog_cb, NULL);
+ }
+ break;
+
+#if 0
+ /*
+ * XXX - Maybe we need to warn the user if one of the following happens???
+ */
+ case AIRPCAP_DLL_ERROR:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s","AIRPCAP_DLL_ERROR\n");
+ break;
+
+ case AIRPCAP_DLL_NOT_FOUND:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s","AIRPCAP_DDL_NOT_FOUND\n");
+ break;
+#endif
+ }
+}
+
+#endif /* HAVE_AIRPCAP */
diff --git a/ui/gtk/main_airpcap_toolbar.h b/ui/gtk/main_airpcap_toolbar.h
new file mode 100644
index 0000000000..80ccbed85d
--- /dev/null
+++ b/ui/gtk/main_airpcap_toolbar.h
@@ -0,0 +1,33 @@
+/* main_airpcap_toolbar.h
+ * Definitions for the airpcap toolbar routines
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MAIN_AIRPCAP_TOOLBAR_H__
+#define __MAIN_AIRPCAP_TOOLBAR_H__
+
+extern int airpcap_dll_ret_val;
+
+GtkWidget *airpcap_toolbar_new(void);
+void airpcap_toolbar_show(GtkWidget *airpcap_tb);
+
+#endif /* __MAIN_AIRPCAP_TOOLBAR_H__ */
diff --git a/ui/gtk/main_filter_toolbar.c b/ui/gtk/main_filter_toolbar.c
new file mode 100644
index 0000000000..43054bdf71
--- /dev/null
+++ b/ui/gtk/main_filter_toolbar.c
@@ -0,0 +1,414 @@
+/* main_filter_toolbar.c
+ * The filter toolbar
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * This file implements the "filter" toolbar for Wireshark.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include "ui/gtk/old-gtk-compat.h"
+
+#include "filter_dlg.h"
+#include "filter_autocomplete.h"
+
+#include "epan/prefs.h"
+
+#include "keys.h"
+#include "gtkglobals.h"
+#include "stock_icons.h"
+#include "recent.h"
+
+#include "main.h"
+#include "menus.h"
+#include "main_toolbar.h"
+#include "main_filter_toolbar.h"
+#include "filter_expression_save_dlg.h"
+
+#define MENU_BAR_PATH_FILE_OPEN "/Menubar/FileMenu/Open"
+#define MENU_BAR_PATH_EDIT_COPY_AS_FLT "/Menubar/EditMenu/Copy/AsFilter"
+#define MENU_BAR_PATH_ANALYZE_DISPLAY_FLT "/Menubar/AnalyzeMenu/DisplayFilters"
+#define MENU_BAR_PATH_ANALYZE_FOLLOW_TCP_STREAM "/Menubar/AnalyzeMenu/FollowTCPStream"
+#define MENU_BAR_PATH_ANALYZE_FOLLOW_UDP_STREAM "/Menubar/AnalyzeMenu/FollowUDPStream"
+#define MENU_BAR_PATH_ANALYZE_FOLLOW_SSL_STREAM "/Menubar/AnalyzeMenu/FollowSSLStream"
+#define MENU_BAR_PATH_ANALYZE_APL_AS_FLT_SEL "/Menubar/AnalyzeMenu/ApplyAsFilter/Selected"
+#define MENU_BAR_PATH_ANALYZE_APL_AS_FLT_NOT_SEL "/Menubar/AnalyzeMenu/ApplyAsFilter/NotSelected"
+#define MENU_BAR_PATH_ANALYZE_APL_AS_FLT_AND_SEL "/Menubar/AnalyzeMenu/ApplyAsFilter/AndSelected"
+#define MENU_BAR_PATH_ANALYZE_APL_AS_FLT_OR_SEL "/Menubar/AnalyzeMenu/ApplyAsFilter/OrSelected"
+#define MENU_BAR_PATH_ANALYZE_APL_AS_FLT_AND_NOT_SEL "/Menubar/AnalyzeMenu/ApplyAsFilter/AndNotSelected"
+#define MENU_BAR_PATH_ANALYZE_APL_AS_FLT_OR_NOT_SEL "/Menubar/AnalyzeMenu/ApplyAsFilter/OrNotSelected"
+#define MENU_BAR_PATH_ANALYZE_PREP_A_FLT_SEL "/Menubar/AnalyzeMenu/PrepareaFilter/Selected"
+#define MENU_BAR_PATH_ANALYZE_PREP_A_FLT_NOT_SEL "/Menubar/AnalyzeMenu/PrepareaFilter/NotSelected"
+#define MENU_BAR_PATH_ANALYZE_PREP_A_FLT_AND_SEL "/Menubar/AnalyzeMenu/PrepareaFilter/AndSelected"
+#define MENU_BAR_PATH_ANALYZE_PREP_A_FLT_OR_SEL "/Menubar/AnalyzeMenu/PrepareaFilter/OrSelected"
+#define MENU_BAR_PATH_ANALYZE_PREP_A_FLT_AND_NOT_SEL "/Menubar/AnalyzeMenu/PrepareaFilter/AndNotSelected"
+#define MENU_BAR_PATH_ANALYZE_PREP_A_FLT_OR_NOT_SEL "/Menubar/AnalyzeMenu/PrepareaFilter/OrNotSelected"
+
+GtkWidget *main_display_filter_widget=NULL;
+
+/* Run the current display filter on the current packet set, and
+ redisplay. */
+static void
+filter_activate_cb(GtkWidget *w _U_, gpointer data)
+{
+ const char *s;
+
+ s = gtk_entry_get_text(GTK_ENTRY(data));
+ main_filter_packets(&cfile, s, FALSE);
+}
+
+/* Enable both Clear and Apply button when filter is changed */
+static void
+filter_changed_cb(GtkWidget *w _U_, gpointer data)
+{
+ gtk_widget_set_sensitive (g_object_get_data (G_OBJECT(data), E_DFILTER_APPLY_KEY), TRUE);
+ gtk_widget_set_sensitive (g_object_get_data (G_OBJECT(data), E_DFILTER_CLEAR_KEY), TRUE);
+ gtk_widget_set_sensitive (g_object_get_data (G_OBJECT(data), E_DFILTER_SAVE_KEY), TRUE);
+}
+
+/* redisplay with no display filter */
+static void
+filter_reset_cb(GtkWidget *w, gpointer data _U_)
+{
+ GtkWidget *filter_te = NULL;
+
+ if ((filter_te = g_object_get_data(G_OBJECT(w), E_DFILTER_TE_KEY))) {
+ gtk_entry_set_text(GTK_ENTRY(filter_te), "");
+ }
+ main_filter_packets(&cfile, NULL, FALSE);
+}
+
+static void
+filter_save_cb(GtkWidget *w _U_, GtkWindow *parent_w)
+{
+ filter_expression_save_dlg(parent_w);
+}
+
+
+GtkWidget *filter_toolbar_new(void)
+{
+ GtkWidget *filter_cm;
+ GtkWidget *filter_te;
+ GtkWidget *filter_tb;
+ GtkToolItem *filter_bt, *filter_add_expr_bt, *filter_reset;
+ GtkToolItem *filter_apply, *filter_save, *item;
+
+
+ /* Display filter construct dialog has an Apply button, and "OK" not
+ only sets our text widget, it activates it (i.e., it causes us to
+ filter the capture). */
+ static construct_args_t args = {
+ "Wireshark: Display Filter",
+ TRUE,
+ TRUE,
+ FALSE
+ };
+
+ /* filter toolbar */
+ filter_tb = gtk_toolbar_new();
+ gtk_orientable_set_orientation(GTK_ORIENTABLE(filter_tb),
+ GTK_ORIENTATION_HORIZONTAL);
+
+ g_object_set_data(G_OBJECT(top_level), E_TB_FILTER_KEY, filter_tb);
+ gtk_widget_show(filter_tb);
+
+ /* Create the "Filter:" button */
+ filter_bt = gtk_tool_button_new_from_stock (WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
+ g_signal_connect(filter_bt, "clicked", G_CALLBACK(display_filter_construct_cb), &args);
+ gtk_widget_show(GTK_WIDGET (filter_bt));
+ g_object_set_data(G_OBJECT(top_level), E_FILT_BT_PTR_KEY, filter_bt);
+
+ gtk_toolbar_insert(GTK_TOOLBAR(filter_tb),
+ filter_bt,
+ -1);
+ gtk_widget_set_tooltip_text( GTK_WIDGET(filter_bt), "Open the \"Display Filter\" dialog, to edit/apply filters");
+
+ /* Create the filter combobox */
+ filter_cm = gtk_combo_box_text_new_with_entry ();
+ filter_te = gtk_bin_get_child(GTK_BIN(filter_cm));
+ main_display_filter_widget=filter_te;
+ g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_te);
+ g_object_set_data(G_OBJECT(filter_te), E_DFILTER_CM_KEY, filter_cm);
+ g_object_set_data(G_OBJECT(top_level), E_DFILTER_CM_KEY, filter_cm);
+ g_signal_connect(filter_te, "activate", G_CALLBACK(filter_activate_cb), filter_te);
+ g_signal_connect(filter_te, "changed", G_CALLBACK(filter_changed_cb), filter_cm);
+ g_signal_connect(filter_te, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
+ g_object_set_data(G_OBJECT(filter_tb), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+ g_object_set_data(G_OBJECT(filter_te), E_FILT_FIELD_USE_STATUSBAR_KEY, "");
+ g_signal_connect(filter_te, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
+ g_signal_connect(filter_tb, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
+
+ gtk_widget_set_size_request(filter_cm, 400, -1);
+ gtk_widget_show(filter_cm);
+ item = gtk_tool_item_new ();
+ gtk_container_add (GTK_CONTAINER (item), filter_cm);
+ gtk_widget_show (GTK_WIDGET (item));
+
+ gtk_toolbar_insert(GTK_TOOLBAR(filter_tb),
+ item,
+ -1);
+
+ /* setting a tooltip for a combobox will do nothing, so add it to the corresponding text entry */
+ gtk_widget_set_tooltip_text(filter_cm,
+ "Enter a display filter, or choose one of your recently used filters. "
+ "The background color of this field is changed by a continuous syntax check "
+ "(green is valid, red is invalid, yellow may have unexpected results).");
+
+ /* Create the "Add Expression..." button, to pop up a dialog
+ for constructing filter comparison expressions. */
+ filter_add_expr_bt = gtk_tool_button_new_from_stock(WIRESHARK_STOCK_ADD_EXPRESSION);
+ g_object_set_data(G_OBJECT(filter_tb), E_FILT_FILTER_TE_KEY, filter_te);
+ g_signal_connect(filter_add_expr_bt, "clicked", G_CALLBACK(filter_add_expr_bt_cb), filter_tb);
+ gtk_widget_show(GTK_WIDGET(filter_add_expr_bt));
+
+ gtk_toolbar_insert(GTK_TOOLBAR(filter_tb),
+ filter_add_expr_bt,
+ -1);
+
+ gtk_widget_set_tooltip_text(GTK_WIDGET(filter_add_expr_bt), "Add an expression to this filter string");
+
+ /* Create the "Clear" button */
+ filter_reset = gtk_tool_button_new_from_stock(WIRESHARK_STOCK_CLEAR_EXPRESSION);
+ g_object_set_data(G_OBJECT(filter_reset), E_DFILTER_TE_KEY, filter_te);
+ g_object_set_data (G_OBJECT(filter_cm), E_DFILTER_CLEAR_KEY, filter_reset);
+ g_signal_connect(filter_reset, "clicked", G_CALLBACK(filter_reset_cb), NULL);
+ gtk_widget_set_sensitive (GTK_WIDGET(filter_reset), FALSE);
+ gtk_widget_show(GTK_WIDGET(filter_reset));
+ gtk_toolbar_insert(GTK_TOOLBAR(filter_tb),
+ filter_reset,
+ -1);
+
+ gtk_widget_set_tooltip_text(GTK_WIDGET(filter_reset), "Clear this filter string and update the display");
+
+ /* Create the "Apply" button */
+ filter_apply = gtk_tool_button_new_from_stock(WIRESHARK_STOCK_APPLY_EXPRESSION);
+ g_object_set_data(G_OBJECT(filter_apply), E_DFILTER_CM_KEY, filter_cm);
+ g_object_set_data (G_OBJECT(filter_cm), E_DFILTER_APPLY_KEY, filter_apply);
+ g_signal_connect(filter_apply, "clicked", G_CALLBACK(filter_activate_cb), filter_te);
+ gtk_widget_set_sensitive (GTK_WIDGET(filter_apply), FALSE);
+ gtk_widget_show(GTK_WIDGET(filter_apply));
+
+ gtk_toolbar_insert(GTK_TOOLBAR(filter_tb),
+ filter_apply,
+ -1);
+
+ gtk_widget_set_tooltip_text(GTK_WIDGET(filter_apply), "Apply this filter string to the display");
+
+ /* Create the "Save" button */
+ filter_save = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE);
+ g_object_set_data(G_OBJECT(filter_save), E_DFILTER_CM_KEY, filter_cm);
+ g_object_set_data(G_OBJECT(filter_cm), E_DFILTER_SAVE_KEY, filter_save);
+ g_signal_connect(filter_save, "clicked", G_CALLBACK(filter_save_cb), filter_te);
+ gtk_widget_set_sensitive (GTK_WIDGET(filter_save), FALSE);
+ gtk_widget_show(GTK_WIDGET(filter_save));
+
+ gtk_toolbar_insert(GTK_TOOLBAR(filter_tb),
+ filter_save,
+ -1);
+
+ gtk_widget_set_tooltip_text(GTK_WIDGET(filter_save), "Save this filter string");
+
+ /* Sets the text entry widget pointer as the E_DILTER_TE_KEY data
+ * of any widget that ends up calling a callback which needs
+ * that text entry pointer */
+ set_menu_object_data(MENU_BAR_PATH_FILE_OPEN, E_DFILTER_TE_KEY, filter_te);
+ set_menu_object_data(MENU_BAR_PATH_EDIT_COPY_AS_FLT, E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data(MENU_BAR_PATH_ANALYZE_DISPLAY_FLT, E_FILT_TE_PTR_KEY,
+ filter_te);
+ set_menu_object_data(MENU_BAR_PATH_ANALYZE_FOLLOW_TCP_STREAM, E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data(MENU_BAR_PATH_ANALYZE_FOLLOW_UDP_STREAM, E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data(MENU_BAR_PATH_ANALYZE_FOLLOW_SSL_STREAM, E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data(MENU_BAR_PATH_ANALYZE_APL_AS_FLT_SEL, E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data(MENU_BAR_PATH_ANALYZE_APL_AS_FLT_NOT_SEL, E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data(MENU_BAR_PATH_ANALYZE_APL_AS_FLT_AND_SEL, E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data(MENU_BAR_PATH_ANALYZE_APL_AS_FLT_OR_SEL, E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data(MENU_BAR_PATH_ANALYZE_APL_AS_FLT_AND_NOT_SEL, E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data(MENU_BAR_PATH_ANALYZE_APL_AS_FLT_OR_NOT_SEL, E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data(MENU_BAR_PATH_ANALYZE_PREP_A_FLT_SEL, E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data(MENU_BAR_PATH_ANALYZE_PREP_A_FLT_NOT_SEL, E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data(MENU_BAR_PATH_ANALYZE_PREP_A_FLT_AND_SEL, E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data(MENU_BAR_PATH_ANALYZE_PREP_A_FLT_OR_SEL, E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data(MENU_BAR_PATH_ANALYZE_PREP_A_FLT_AND_NOT_SEL, E_DFILTER_TE_KEY,
+ filter_te);
+ set_menu_object_data(MENU_BAR_PATH_ANALYZE_PREP_A_FLT_OR_NOT_SEL, E_DFILTER_TE_KEY,
+ filter_te);
+
+ set_toolbar_object_data(E_DFILTER_TE_KEY, filter_te);
+ g_object_set_data(G_OBJECT(popup_menu_object), E_DFILTER_TE_KEY, filter_te);
+
+ filter_expression_save_dlg_init(filter_tb, filter_te);
+
+ /* make current preferences effective */
+ toolbar_redraw_all();
+
+ return filter_tb;
+}
+
+static gboolean
+dfilter_entry_match(GtkWidget *filter_cm, char *s, int *index)
+{
+ GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX(filter_cm));
+ GtkTreeIter iter;
+ GValue value = { 0, {{0}}};
+ const char *filter_str;
+ int i;
+
+ i = -1;
+ if (!gtk_tree_model_get_iter_first (model, &iter)){
+ *index = i;
+ return FALSE;
+ }
+ do{
+ i++;
+ gtk_tree_model_get_value (model, &iter, 0, &value);
+ filter_str = g_value_get_string (&value);
+ if(filter_str){
+ if(strcmp(s, filter_str) == 0){
+ g_value_unset (&value);
+ *index = i;
+ return TRUE;
+ }
+ }
+ g_value_unset (&value);
+ }while (gtk_tree_model_iter_next (model, &iter));
+
+ *index = i;
+ return FALSE;
+}
+
+/* add a display filter to the combo box */
+/* Note: a new filter string will not replace an old identical one */
+static gboolean
+dfilter_combo_add(GtkWidget *filter_cm, char *s) {
+ int index;
+
+ if(!dfilter_entry_match(filter_cm,s, &index))
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(filter_cm), s);
+ g_free(s);
+
+ return TRUE;
+}
+
+
+/* write all non empty display filters (until maximum count)
+ * of the combo box GList to the user's recent file */
+void
+dfilter_recent_combo_write_all(FILE *rf) {
+ GtkWidget *filter_cm = g_object_get_data(G_OBJECT(top_level), E_DFILTER_CM_KEY);
+ GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX(filter_cm));
+ GtkTreeIter iter;
+ GValue value = { 0, {{0}}};
+ const char *filter_str;
+ guint max_count = 0;
+
+ if (!gtk_tree_model_get_iter_first (model, &iter))
+ return;
+ do{
+ gtk_tree_model_get_value (model, &iter, 0, &value);
+ filter_str = g_value_get_string (&value);
+ if(filter_str)
+ fprintf (rf, RECENT_KEY_DISPLAY_FILTER ": %s\n", filter_str);
+ g_value_unset (&value);
+
+ }while (gtk_tree_model_iter_next (model, &iter)&& (max_count++ < prefs.gui_recent_df_entries_max));
+
+}
+
+/* add a display filter coming from the user's recent file to the dfilter combo box */
+gboolean
+dfilter_combo_add_recent(gchar *s) {
+ GtkWidget *filter_cm = g_object_get_data(G_OBJECT(top_level), E_DFILTER_CM_KEY);
+ char *dup;
+
+ dup = g_strdup(s);
+
+ return dfilter_combo_add(filter_cm, dup);
+}
+
+/* call cf_filter_packets() and add this filter string to the recent filter list */
+gboolean
+main_filter_packets(capture_file *cf, const gchar *dftext, gboolean force)
+{
+ GtkWidget *filter_cm = g_object_get_data(G_OBJECT(top_level), E_DFILTER_CM_KEY);
+ gboolean free_filter = TRUE;
+ char *s;
+ cf_status_t cf_status;
+
+ s = g_strdup(dftext);
+
+ cf_status = cf_filter_packets(cf, s, force);
+
+ if (cf_status == CF_OK) {
+ gtk_widget_set_sensitive (g_object_get_data (G_OBJECT(filter_cm), E_DFILTER_APPLY_KEY), FALSE);
+ if (!s || strlen (s) == 0) {
+ gtk_widget_set_sensitive (g_object_get_data (G_OBJECT(filter_cm), E_DFILTER_CLEAR_KEY), FALSE);
+ gtk_widget_set_sensitive (g_object_get_data (G_OBJECT(filter_cm), E_DFILTER_SAVE_KEY), FALSE);
+ }
+ }
+
+ if (!s)
+ return (cf_status == CF_OK);
+
+ /* GtkCombos don't let us get at their list contents easily, so we maintain
+ our own filter list, and feed it to gtk_combo_set_popdown_strings when
+ a new filter is added. */
+ if (cf_status == CF_OK && strlen(s) > 0) {
+ int index;
+
+ if(!dfilter_entry_match(filter_cm,s, &index)){
+ gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(filter_cm), s);
+ index++;
+ }
+ while ((guint)index >= prefs.gui_recent_df_entries_max){
+ gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(filter_cm), index);
+ index--;
+ }
+ }
+ if (free_filter)
+ g_free(s);
+
+ return (cf_status == CF_OK);
+}
+
+
diff --git a/ui/gtk/main_filter_toolbar.h b/ui/gtk/main_filter_toolbar.h
new file mode 100644
index 0000000000..e90b6d2271
--- /dev/null
+++ b/ui/gtk/main_filter_toolbar.h
@@ -0,0 +1,35 @@
+/* main_filter_toolbar.h
+ * Definitions for filter toolbar routines
+ * Copyright 2003, Ulf Lamping <ulf.lamping@web.de>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MAIN_FILTER_TOOLBAR_H__
+#define __MAIN_FILTER_TOOLBAR_H__
+
+#define E_DFILTER_APPLY_KEY "display_filter_apply"
+#define E_DFILTER_CLEAR_KEY "display_filter_clear"
+#define E_DFILTER_SAVE_KEY "display_filter_save"
+
+extern GtkWidget *filter_toolbar_new(void);
+
+#endif /* __MAIN_FILTER_TOOLBAR_H__ */
diff --git a/ui/gtk/main_menubar.c b/ui/gtk/main_menubar.c
new file mode 100644
index 0000000000..e10072f966
--- /dev/null
+++ b/ui/gtk/main_menubar.c
@@ -0,0 +1,5495 @@
+/* main_menubar.c
+ * Menu routines
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <epan/packet.h>
+#include <epan/addr_resolv.h>
+#include <epan/prefs.h>
+#include <epan/prefs-int.h>
+#include <epan/tap.h>
+#include <epan/timestamp.h>
+#include <epan/etypes.h>
+#include <epan/ipproto.h>
+#include <epan/dissector_filters.h>
+#include <epan/strutil.h>
+#include <epan/plugins.h>
+#include <epan/epan_dissect.h>
+#include <epan/column.h>
+
+#include <epan/filesystem.h>
+
+#include "../print.h"
+#include "../ui_util.h"
+#include "../simple_dialog.h"
+#include "../main_statusbar.h"
+#include "../color_filters.h"
+#include "../stat_menu.h"
+#include "../u3.h"
+
+#include "ui/gtk/about_dlg.h"
+#include "ui/gtk/capture_dlg.h"
+#include "ui/gtk/capture_if_dlg.h"
+#include "ui/gtk/color_dlg.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/profile_dlg.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/capture_file_dlg.h"
+#include "ui/gtk/fileset_dlg.h"
+#include "ui/gtk/file_import_dlg.h"
+#include "ui/gtk/find_dlg.h"
+#include "ui/gtk/goto_dlg.h"
+#include "ui/gtk/summary_dlg.h"
+#include "ui/gtk/prefs_dlg.h"
+#include "ui/gtk/packet_win.h"
+#include "ui/gtk/follow_tcp.h"
+#include "ui/gtk/follow_udp.h"
+#include "ui/gtk/follow_ssl.h"
+#include "ui/gtk/decode_as_dlg.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/supported_protos_dlg.h"
+#include "ui/gtk/proto_dlg.h"
+#include "ui/gtk/proto_hier_stats_dlg.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/recent.h"
+#include "ui/gtk/main_proto_draw.h"
+#include "ui/gtk/conversations_table.h"
+#include "ui/gtk/hostlist_table.h"
+#include "ui/gtk/packet_history.h"
+#include "ui/gtk/sctp_stat.h"
+#include "ui/gtk/firewall_dlg.h"
+#include "ui/gtk/macros_dlg.h"
+#include "ui/gtk/export_object.h"
+#include "epan/dissectors/packet-ssl-utils.h"
+#include "ui/gtk/export_sslkeys.h"
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/menus.h"
+#include "ui/gtk/main_toolbar.h"
+#include "ui/gtk/main_welcome.h"
+#include "ui/gtk/uat_gui.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/manual_addr_resolv.h"
+#include "ui/gtk/proto_help.h"
+#include "ui/gtk/dissector_tables_dlg.h"
+#include "ui/gtk/utf8_entities.h"
+#include "ui/gtk/expert_comp_dlg.h"
+#include "ui/gtk/time_shift_dlg.h"
+
+#include "ui/gtk/new_packet_list.h"
+
+#ifdef HAVE_LIBPCAP
+#include "capture_opts.h"
+#include "ui/gtk/capture_globals.h"
+#endif
+#ifdef HAVE_IGE_MAC_INTEGRATION
+#include <ige-mac-menu.h>
+#endif
+
+#ifdef HAVE_GTKOSXAPPLICATION
+#include <igemacintegration/gtkosxapplication.h>
+#endif
+
+static int initialize = TRUE;
+GtkActionGroup *main_menu_bar_action_group;
+static GtkUIManager *ui_manager_main_menubar = NULL;
+static GtkUIManager *ui_manager_packet_list_heading = NULL;
+static GtkUIManager *ui_manager_packet_list_menu = NULL;
+static GtkUIManager *ui_manager_tree_view_menu = NULL;
+static GtkUIManager *ui_manager_bytes_menu = NULL;
+static GtkUIManager *ui_manager_statusbar_profiles_menu = NULL;
+static GSList *popup_menu_list = NULL;
+
+static GtkAccelGroup *grp;
+
+static GList *merge_lua_menu_items_list = NULL;
+static GList *build_menubar_items_callback_list = NULL;
+
+GtkWidget *popup_menu_object;
+
+static void menu_open_recent_file_cmd_cb(GtkAction *action, gpointer data _U_ );
+static void add_recent_items (guint merge_id, GtkUIManager *ui_manager);
+
+static void menus_init(void);
+static void merge_lua_menu_items(GList *node);
+static void ws_menubar_build_external_menus(void);
+static void set_menu_sensitivity (GtkUIManager *ui_manager, const gchar *, gint);
+static void set_menu_visible(GtkUIManager *ui_manager, const gchar *path, gint val);
+static void name_resolution_cb(GtkWidget *w, gpointer d, gint action);
+static void colorize_cb(GtkWidget *w, gpointer d);
+
+
+/* As a general GUI guideline, we try to follow the Gnome Human Interface Guidelines, which can be found at:
+ http://developer.gnome.org/projects/gup/hig/1.0/index.html
+
+Please note: there are some differences between the Gnome HIG menu suggestions and our implementation:
+
+File/Open Recent: the Gnome HIG suggests putting the list of recently used files as elements into the File menuitem.
+ As this is ok for only a few items, this will become unhandy for 10 or even more list entries.
+ For this reason, we use a submenu for this.
+
+File/Close: the Gnome HIG suggests putting this item just above the Quit item.
+ This results in unintuitive behaviour as both Close and Quit items are very near together.
+ By putting the Close item near the open item(s), it better suggests that it will close the
+ currently opened/captured file only.
+*/
+
+#ifdef NEW_MENU_CODE
+static gchar *
+get_ui_file_path(const char *filename)
+{
+ gchar *gui_desc_file_name;
+
+ gui_desc_file_name = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s" G_DIR_SEPARATOR_S "%s", get_datafile_dir(),
+ running_in_build_directory() ? "ui/gtk/ui" : "ui", filename);
+ return gui_desc_file_name;
+}
+#endif
+
+typedef enum {
+ CONV_ETHER = 1,
+ CONV_IP,
+ CONV_TCP,
+ CONV_UDP,
+ CONV_CBA
+} conv_values_e;
+
+static char *
+build_conversation_filter(int action, gboolean show_dialog)
+{
+ packet_info *pi = &cfile.edt->pi;
+ char *buf;
+
+
+ switch(action) {
+ case(CONV_CBA):
+ if (pi->profinet_type == 0) {
+ if (show_dialog) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Error filtering conversation. Please make\n"
+ "sure you have a PROFINET CBA packet selected.");
+ }
+ return NULL;
+ }
+
+ if( pi->net_src.type == AT_IPv4 && pi->net_dst.type == AT_IPv4
+ && pi->ipproto == IP_PROTO_TCP ) {
+ /* IPv4 */
+ switch(pi->profinet_type) {
+ case(1):
+ buf = g_strdup_printf("(ip.src eq %s and ip.dst eq %s and cba.acco.dcom == 1) || (ip.src eq %s and ip.dst eq %s and cba.acco.dcom == 0)",
+ ip_to_str( pi->net_dst.data),
+ ip_to_str( pi->net_src.data),
+ ip_to_str( pi->net_src.data),
+ ip_to_str( pi->net_dst.data));
+ break;
+ case(2):
+ buf = g_strdup_printf("(ip.src eq %s and ip.dst eq %s and cba.acco.dcom == 1) || (ip.src eq %s and ip.dst eq %s and cba.acco.dcom == 0)",
+ ip_to_str( pi->net_src.data),
+ ip_to_str( pi->net_dst.data),
+ ip_to_str( pi->net_dst.data),
+ ip_to_str( pi->net_src.data));
+ break;
+ case(3):
+ buf = g_strdup_printf("(ip.src eq %s and ip.dst eq %s and cba.acco.srt == 1) || (ip.src eq %s and ip.dst eq %s and cba.acco.srt == 0)",
+ ip_to_str( pi->net_dst.data),
+ ip_to_str( pi->net_src.data),
+ ip_to_str( pi->net_src.data),
+ ip_to_str( pi->net_dst.data));
+ break;
+ case(4):
+ buf = g_strdup_printf("(ip.src eq %s and ip.dst eq %s and cba.acco.srt == 1) || (ip.src eq %s and ip.dst eq %s and cba.acco.srt == 0)",
+ ip_to_str( pi->net_src.data),
+ ip_to_str( pi->net_dst.data),
+ ip_to_str( pi->net_dst.data),
+ ip_to_str( pi->net_src.data));
+ break;
+ default:
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
+ break;
+ case(CONV_TCP):
+ if (pi->ipproto != IP_PROTO_TCP) {
+ if (show_dialog) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Error filtering conversation. Please make\n"
+ "sure you have a TCP packet selected.");
+ }
+ return NULL;
+ }
+
+ if( pi->net_src.type == AT_IPv4 && pi->net_dst.type == AT_IPv4 ) {
+ /* TCP over IPv4 */
+ buf = g_strdup_printf("(ip.addr eq %s and ip.addr eq %s) and (tcp.port eq %d and tcp.port eq %d)",
+ ip_to_str( pi->net_src.data),
+ ip_to_str( pi->net_dst.data),
+ pi->srcport, pi->destport );
+ } else if( pi->net_src.type == AT_IPv6 && pi->net_dst.type == AT_IPv6 ) {
+ /* TCP over IPv6 */
+ buf = g_strdup_printf("(ipv6.addr eq %s and ipv6.addr eq %s) and (tcp.port eq %d and tcp.port eq %d)",
+ ip6_to_str((const struct e_in6_addr *)pi->net_src.data),
+ ip6_to_str((const struct e_in6_addr *)pi->net_dst.data),
+ pi->srcport, pi->destport );
+ } else {
+ return NULL;
+ }
+ break;
+ case(CONV_UDP):
+ if (pi->ipproto != IP_PROTO_UDP) {
+ if (show_dialog) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Error filtering conversation. Please make\n"
+ "sure you have a UDP packet selected.");
+ }
+ return NULL;
+ }
+
+ if( pi->net_src.type == AT_IPv4 && pi->net_dst.type == AT_IPv4 ) {
+ /* UDP over IPv4 */
+ buf = g_strdup_printf("(ip.addr eq %s and ip.addr eq %s) and (udp.port eq %d and udp.port eq %d)",
+ ip_to_str( pi->net_src.data),
+ ip_to_str( pi->net_dst.data),
+ pi->srcport, pi->destport );
+ } else if( pi->net_src.type == AT_IPv6 && pi->net_dst.type == AT_IPv6 ) {
+ /* UDP over IPv6 */
+ buf = g_strdup_printf("(ipv6.addr eq %s and ipv6.addr eq %s) and (udp.port eq %d and udp.port eq %d)",
+ ip6_to_str((const struct e_in6_addr *)pi->net_src.data),
+ ip6_to_str((const struct e_in6_addr *)pi->net_dst.data),
+ pi->srcport, pi->destport );
+ } else {
+ return NULL;
+ }
+ break;
+ case(CONV_IP):
+ if ((pi->ethertype != ETHERTYPE_IP) && (pi->ethertype != ETHERTYPE_IPv6)) {
+ if (show_dialog) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Error filtering conversation. Please make\n"
+ "sure you have a IP packet selected.");
+ }
+ return NULL;
+ }
+
+ if( pi->net_src.type == AT_IPv4 && pi->net_dst.type == AT_IPv4 ) {
+ /* IPv4 */
+ buf = g_strdup_printf("ip.addr eq %s and ip.addr eq %s",
+ ip_to_str( pi->net_src.data),
+ ip_to_str( pi->net_dst.data));
+ } else if( pi->net_src.type == AT_IPv6 && pi->net_dst.type == AT_IPv6 ) {
+ /* IPv6 */
+ buf = g_strdup_printf("ipv6.addr eq %s and ipv6.addr eq %s",
+ ip6_to_str((const struct e_in6_addr *)pi->net_src.data),
+ ip6_to_str((const struct e_in6_addr *)pi->net_dst.data));
+ } else {
+ return NULL;
+ }
+ break;
+ case(CONV_ETHER):
+ /* XXX - is this the right way to check for Ethernet? */
+ /* check for the data link address type */
+ /* (ethertype will be 0 when used as length field) */
+ if (pi->dl_src.type != AT_ETHER) {
+ if (show_dialog) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Error filtering conversation. Please make\n"
+ "sure you have a Ethernet packet selected.");
+ }
+ return NULL;
+ }
+
+ if( pi->dl_src.type == AT_ETHER && pi->dl_dst.type == AT_ETHER ) {
+ /* Ethernet */
+ buf = g_strdup_printf("eth.addr eq %s and eth.addr eq %s",
+ ether_to_str( pi->dl_src.data),
+ ether_to_str( pi->dl_dst.data));
+ } else {
+ return NULL;
+ }
+ break;
+ default:
+ return NULL;
+ }
+
+ return buf;
+}
+
+static void
+new_window_cb(GtkWidget *widget)
+{
+ new_packet_window(widget, FALSE);
+}
+
+static void
+edit_window_cb(GtkWidget *widget _U_)
+{
+#ifdef WANT_PACKET_EDITOR
+ new_packet_window(widget, TRUE);
+#endif
+}
+
+static void
+conversation_cb(GtkAction *a _U_, gpointer data _U_, int action)
+{
+ gchar *filter;
+ GtkWidget *filter_te;
+
+ if (cfile.current_frame) {
+ /* create a filter-string based on the selected packet and action */
+ filter = build_conversation_filter(action, TRUE);
+
+ /* Run the display filter so it goes in effect - even if it's the
+ same as the previous display filter. */
+ filter_te = gtk_bin_get_child(GTK_BIN(g_object_get_data(G_OBJECT(top_level), E_DFILTER_CM_KEY)));
+
+ gtk_entry_set_text(GTK_ENTRY(filter_te), filter);
+ main_filter_packets(&cfile, filter, TRUE);
+
+ g_free(filter);
+ }
+}
+
+static void
+colorize_conversation_cb(GtkAction *action _U_, gpointer data _U_, int action_num)
+{
+ gchar *filter = NULL;
+
+ if( (action_num>>8) == 255 ) {
+ color_filters_reset_tmp();
+ new_packet_list_colorize_packets();
+ } else if (cfile.current_frame) {
+ if( (action_num&0xff) == 0 ) {
+ /* colorize_conversation_cb was called from the window-menu
+ * or through an accelerator key. Try to build a conversation
+ * filter in the order TCP, UDP, IP, Ethernet and apply the
+ * coloring */
+ filter = build_conversation_filter(CONV_TCP,FALSE);
+ if( filter == NULL )
+ filter = build_conversation_filter(CONV_UDP,FALSE);
+ if( filter == NULL )
+ filter = build_conversation_filter(CONV_IP,FALSE);
+ if( filter == NULL )
+ filter = build_conversation_filter(CONV_ETHER,FALSE);
+ if( filter == NULL ) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Unable to build conversation filter.");
+ return;
+ }
+ } else {
+ /* create a filter-string based on the selected packet and action_num */
+ filter = build_conversation_filter(action_num&0xff, TRUE);
+ }
+
+ if( (action_num>>8) == 0) {
+ /* Open the "new coloring filter" dialog with the filter */
+ color_display_with_filter(filter);
+ } else {
+ /* Set one of the temporary coloring filters */
+ color_filters_set_tmp((guint8)(action_num>>8),filter,FALSE);
+ new_packet_list_colorize_packets();
+ }
+
+ g_free(filter);
+ }
+}
+
+static void
+goto_conversation_frame(gboolean dir)
+{
+ gchar *filter;
+ dfilter_t *dfcode = NULL;
+ gboolean found_packet=FALSE;
+
+ filter = build_conversation_filter(CONV_TCP,FALSE);
+ if( filter == NULL )
+ filter = build_conversation_filter(CONV_UDP,FALSE);
+ if( filter == NULL )
+ filter = build_conversation_filter(CONV_IP,FALSE);
+ if( filter == NULL ) {
+ statusbar_push_temporary_msg("Unable to build conversation filter.");
+ g_free(filter);
+ return;
+ }
+
+ if (!dfilter_compile(filter, &dfcode)) {
+ /* The attempt failed; report an error. */
+ statusbar_push_temporary_msg("Error compiling filter for this conversation.");
+ g_free(filter);
+ return;
+ }
+
+ found_packet = cf_find_packet_dfilter(&cfile, dfcode, dir);
+
+ if (!found_packet) {
+ /* We didn't find a packet */
+ statusbar_push_temporary_msg("No previous/next packet in conversation.");
+ }
+
+ dfilter_free(dfcode);
+ g_free(filter);
+}
+
+static void
+goto_next_frame_conversation_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ goto_conversation_frame(FALSE);
+}
+
+static void
+goto_previous_frame_conversation_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ goto_conversation_frame(TRUE);
+}
+
+
+/*Apply a filter */
+
+static void
+tree_view_menu_apply_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_tree_view_menu, "/TreeViewPopup/ApplyAsFilter/Selected");
+ match_selected_ptree_cb( widget , user_data, MATCH_SELECTED_REPLACE|MATCH_SELECTED_APPLY_NOW);
+}
+
+static void
+tree_view_menu_apply_not_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_tree_view_menu, "/TreeViewPopup/ApplyAsFilter/NotSelected");
+ match_selected_ptree_cb( widget , user_data, MATCH_SELECTED_NOT|MATCH_SELECTED_APPLY_NOW);
+}
+
+static void
+tree_view_menu_apply_and_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_tree_view_menu, "/TreeViewPopup/ApplyAsFilter/AndSelected");
+ match_selected_ptree_cb( widget , user_data, MATCH_SELECTED_AND|MATCH_SELECTED_APPLY_NOW);
+}
+
+static void
+tree_view_menu_apply_or_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_tree_view_menu, "/TreeViewPopup/ApplyAsFilter/OrSelected");
+ match_selected_ptree_cb( widget , user_data, MATCH_SELECTED_OR|MATCH_SELECTED_APPLY_NOW);
+}
+
+static void
+tree_view_menu_apply_and_not_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_tree_view_menu, "/TreeViewPopup/ApplyAsFilter/AndNotSelected");
+ match_selected_ptree_cb( widget , user_data, MATCH_SELECTED_AND_NOT|MATCH_SELECTED_APPLY_NOW);
+}
+
+static void
+tree_view_menu_apply_or_not_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_tree_view_menu, "/TreeViewPopup/ApplyAsFilter/OrNotSelected");
+ match_selected_ptree_cb( widget , user_data,MATCH_SELECTED_OR_NOT|MATCH_SELECTED_APPLY_NOW);
+}
+/* Prepare a filter */
+static void
+tree_view_menu_prepare_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_tree_view_menu, "/TreeViewPopup/PrepareaFilter/Selected");
+ match_selected_ptree_cb( widget , user_data, MATCH_SELECTED_REPLACE);
+}
+
+static void
+tree_view_menu_prepare_not_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_tree_view_menu, "/TreeViewPopup/PrepareaFilter/NotSelected");
+ match_selected_ptree_cb( widget , user_data, MATCH_SELECTED_NOT);
+}
+
+static void
+tree_view_menu_prepare_and_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_tree_view_menu, "/TreeViewPopup/PrepareaFilter/AndSelected");
+ match_selected_ptree_cb( widget , user_data, MATCH_SELECTED_AND);
+}
+
+static void
+tree_view_menu_prepare_or_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_tree_view_menu, "/TreeViewPopup/PrepareaFilter/OrSelected");
+ match_selected_ptree_cb( widget , user_data, MATCH_SELECTED_OR);
+}
+
+static void
+tree_view_menu_prepare_and_not_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_tree_view_menu, "/TreeViewPopup/PrepareaFilter/AndNotSelected");
+ match_selected_ptree_cb( widget , user_data, MATCH_SELECTED_AND_NOT);
+}
+
+static void
+tree_view_menu_prepare_or_not_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_tree_view_menu, "/TreeViewPopup/PrepareaFilter/OrNotSelected");
+ match_selected_ptree_cb( widget , user_data, MATCH_SELECTED_OR_NOT);
+}
+
+static void
+copy_description_cb(GtkAction *action _U_, gpointer user_data)
+{
+ copy_selected_plist_cb( NULL /* widget _U_ */ , user_data, COPY_SELECTED_DESCRIPTION);
+}
+
+static void
+copy_fieldname_cb(GtkAction *action _U_, gpointer user_data)
+{
+ copy_selected_plist_cb( NULL /* widget _U_ */ , user_data, COPY_SELECTED_FIELDNAME);
+}
+
+static void
+copy_value_cb(GtkAction *action _U_, gpointer user_data)
+{
+ copy_selected_plist_cb( NULL /* widget _U_ */ , user_data, COPY_SELECTED_VALUE);
+}
+
+static void
+copy_as_filter_cb(GtkAction *action _U_, gpointer user_data)
+{
+ match_selected_ptree_cb( NULL /* widget _U_ */ , user_data, MATCH_SELECTED_REPLACE|MATCH_SELECTED_COPY_ONLY);
+}
+
+static void
+set_reftime_cb(GtkAction *action _U_, gpointer user_data)
+{
+ reftime_frame_cb( NULL /* widget _U_ */ , user_data, REFTIME_TOGGLE);
+}
+
+static void
+find_next_ref_time_cb(GtkAction *action _U_, gpointer user_data)
+{
+ reftime_frame_cb( NULL /* widget _U_ */ , user_data, REFTIME_FIND_NEXT);
+}
+
+static void
+find_previous_ref_time_cb(GtkAction *action _U_, gpointer user_data)
+{
+ reftime_frame_cb( NULL /* widget _U_ */ , user_data, REFTIME_FIND_PREV);
+}
+
+static void
+menus_prefs_cb(GtkAction *action _U_, gpointer user_data)
+{
+ prefs_page_cb( NULL /* widget _U_ */ , user_data, PREFS_PAGE_USER_INTERFACE);
+}
+
+static void
+main_toolbar_show_hide_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/MainToolbar");
+
+ recent.main_toolbar_show = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
+ main_widgets_show_or_hide();
+
+}
+
+static void
+filter_toolbar_show_hide_cb(GtkAction * action _U_, gpointer user_data _U_)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/FilterToolbar");
+
+ recent.filter_toolbar_show = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
+ main_widgets_show_or_hide();
+}
+
+static void
+wireless_toolbar_show_hide_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+#ifdef HAVE_AIRPCAP
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/WirelessToolbar");
+
+ recent.airpcap_toolbar_show = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
+ main_widgets_show_or_hide();
+#endif /* HAVE_AIRPCAP */
+}
+
+static void
+status_bar_show_hide_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/Statusbar");
+
+ recent.statusbar_show = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
+ main_widgets_show_or_hide();
+}
+static void
+packet_list_show_hide_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/PacketList");
+
+ recent.packet_list_show = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
+ main_widgets_show_or_hide();
+}
+static void
+packet_details_show_hide_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/PacketDetails");
+
+ recent.tree_view_show = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
+ main_widgets_show_or_hide();
+}
+static void
+packet_bytes_show_hide_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/PacketBytes");
+
+ recent.byte_view_show = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
+ main_widgets_show_or_hide();
+}
+
+static void
+timestamp_seconds_time_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/TimeDisplayFormat/DisplaySecondsWithHoursAndMinutes");
+
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
+ recent.gui_seconds_format = TS_SECONDS_HOUR_MIN_SEC;
+ } else {
+ recent.gui_seconds_format = TS_SECONDS_DEFAULT;
+ }
+ timestamp_set_seconds_type (recent.gui_seconds_format);
+
+ /* This call adjusts column width */
+ cf_timestamp_auto_precision(&cfile);
+ new_packet_list_queue_draw();
+}
+
+static void
+timestamp_format_new_cb (GtkRadioAction *action, GtkRadioAction *current _U_, gpointer user_data _U_)
+{
+ gint value;
+
+ value = gtk_radio_action_get_current_value (action);
+ if (recent.gui_time_format != value) {
+ timestamp_set_type(value);
+ recent.gui_time_format = value;
+ /* This call adjusts column width */
+ cf_timestamp_auto_precision(&cfile);
+ new_packet_list_queue_draw();
+ }
+
+}
+
+static void
+timestamp_precision_new_cb (GtkRadioAction *action, GtkRadioAction *current _U_, gpointer user_data _U_)
+{
+ gint value;
+
+ value = gtk_radio_action_get_current_value (action);
+ if (recent.gui_time_precision != value) {
+ /* the actual precision will be set in new_packet_list_queue_draw() below */
+ if (value == TS_PREC_AUTO) {
+ timestamp_set_precision(TS_PREC_AUTO_SEC);
+ } else {
+ timestamp_set_precision(value);
+ }
+ recent.gui_time_precision = value;
+ /* This call adjusts column width */
+ cf_timestamp_auto_precision(&cfile);
+ new_packet_list_queue_draw();
+ }
+}
+
+static void
+view_menu_en_for_MAC_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/NameResolution/EnableforMACLayer");
+ if (!widget){
+ g_warning("view_menu_en_for_MAC_cb: No widget found");
+ }else{
+ name_resolution_cb( widget , user_data, RESOLV_MAC);
+ }
+}
+
+static void
+view_menu_en_for_network_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/NameResolution/EnableforNetworkLayer");
+ if (!widget){
+ g_warning("view_menu_en_for_network_cb: No widget found");
+ }else{
+ name_resolution_cb( widget , user_data, RESOLV_NETWORK);
+ }
+}
+
+static void
+view_menu_en_for_transport_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/NameResolution/EnableforTransportLayer");
+ if (!widget){
+ g_warning("view_menu_en_for_transport_cb: No widget found");
+ }else{
+ name_resolution_cb( widget , user_data, RESOLV_TRANSPORT);
+ }
+}
+
+static void
+view_menu_colorize_pkt_lst_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/ColorizePacketList");
+ if (!widget){
+ g_warning("view_menu_colorize_pkt_lst_cb: No widget found");
+ }else{
+ colorize_cb( widget , user_data);
+ }
+
+}
+
+static void
+view_menu_auto_scroll_live_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+#ifdef HAVE_LIBPCAP
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/AutoScrollinLiveCapture");
+
+ if (!widget){
+ g_warning("view_menu_auto_scroll_live_cb: No widget found");
+ }else{
+ menu_auto_scroll_live_changed(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)));
+ }
+#endif
+}
+
+static void
+view_menu_color_conv_color1_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, 1*256);
+}
+
+static void
+view_menu_color_conv_color2_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, 2*256);
+}
+
+static void
+view_menu_color_conv_color3_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, 3*256);
+}
+
+static void
+view_menu_color_conv_color4_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, 4*256);
+}
+
+static void
+view_menu_color_conv_color5_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, 5*256);
+}
+
+static void
+view_menu_color_conv_color6_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, 6*256);
+}
+
+static void
+view_menu_color_conv_color7_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, 7*256);
+}
+
+static void
+view_menu_color_conv_color8_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, 8*256);
+}
+
+static void
+view_menu_color_conv_color9_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, 9*256);
+}
+
+static void
+view_menu_color_conv_color10_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, 10*256);
+}
+
+static void
+view_menu_color_conv_new_rule_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, 0);
+}
+
+static void
+view_menu_reset_coloring_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, 255*256);
+}
+/*
+ * TODO Move this menu to capture_if_dlg.c ?
+ */
+static void
+capture_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+#ifdef HAVE_LIBPCAP
+ const gchar *action_name;
+ gchar *name;
+
+ action_name = gtk_action_get_name (action);
+ name = strrchr(action_name,'/');
+ if(name){
+ name = name+1;
+ }else{
+ name = g_strdup_printf("%s",action_name);
+ }
+ if(strcmp(name, "Interfaces") == 0){
+ capture_if_cb(NULL /* GtkWidget *w _U_ */, user_data);
+ return;
+ }else if(strcmp(name, "Options") == 0){
+ capture_prep_cb(NULL /* GtkWidget *w _U_ */, user_data);
+ return;
+ }else if(strcmp(name, "Start") == 0){
+ capture_start_cb(NULL /* GtkWidget *w _U_ */, user_data);
+ return;
+ }else if(strcmp(name, "Stop") == 0){
+ capture_stop_cb(NULL /* GtkWidget *w _U_ */, user_data);
+ return;
+ }else if(strcmp(name, "Restart") == 0){
+ capture_restart_cb(NULL /* GtkWidget *w _U_ */, user_data);
+ return;
+ }else if(strcmp(name, "CaptureFilters") == 0){
+ cfilter_dialog_cb(NULL /* GtkWidget *w _U_ */);
+ return;
+ }
+
+ fprintf (stderr, "Warning capture_cb unknown action: %s/n",action_name);
+#endif /* HAVE_LIBPCAP */
+}
+
+static void
+help_menu_cont_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ topic_menu_cb(NULL/*widget _U_ */, NULL /*GdkEventButton *event _U_*/, GINT_TO_POINTER(HELP_CONTENT));
+}
+
+static void
+help_menu_faq_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ topic_menu_cb(NULL/*widget _U_ */, NULL /*GdkEventButton *event _U_*/, GINT_TO_POINTER(ONLINEPAGE_FAQ));
+}
+
+static void
+help_menu_wireshark_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ topic_menu_cb(NULL/*widget _U_ */, NULL /*GdkEventButton *event _U_*/, GINT_TO_POINTER(LOCALPAGE_MAN_WIRESHARK));
+}
+
+static void
+help_menu_wireshark_flt_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ topic_menu_cb(NULL/*widget _U_ */, NULL /*GdkEventButton *event _U_*/, GINT_TO_POINTER(LOCALPAGE_MAN_WIRESHARK_FILTER));
+}
+
+static void
+help_menu_Tshark_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ topic_menu_cb(NULL/*widget _U_ */, NULL /*GdkEventButton *event _U_*/, GINT_TO_POINTER(LOCALPAGE_MAN_TSHARK));
+}
+
+static void
+help_menu_RawShark_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ topic_menu_cb(NULL/*widget _U_ */, NULL /*GdkEventButton *event _U_*/, GINT_TO_POINTER(LOCALPAGE_MAN_RAWSHARK));
+}
+
+static void
+help_menu_Dumpcap_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ topic_menu_cb(NULL/*widget _U_ */, NULL /*GdkEventButton *event _U_*/, GINT_TO_POINTER(LOCALPAGE_MAN_DUMPCAP));
+}
+
+static void
+help_menu_Mergecap_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ topic_menu_cb(NULL/*widget _U_ */, NULL /*GdkEventButton *event _U_*/, GINT_TO_POINTER(LOCALPAGE_MAN_MERGECAP));
+}
+
+static void
+help_menu_Editcap_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ topic_menu_cb(NULL/* widget _U_ */, NULL /*GdkEventButton *event _U_*/, GINT_TO_POINTER(LOCALPAGE_MAN_EDITCAP));
+}
+
+static void
+help_menu_Text2pcap_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ topic_menu_cb(NULL/* widget _U_ */, NULL /*GdkEventButton *event _U_*/, GINT_TO_POINTER(LOCALPAGE_MAN_TEXT2PCAP));
+}
+
+static void
+help_menu_Website_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ topic_menu_cb(NULL/* widget _U_ */, NULL /*GdkEventButton *event _U_*/, GINT_TO_POINTER(ONLINEPAGE_HOME));
+}
+
+static void
+help_menu_Wiki_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ topic_menu_cb(NULL/* widget _U_ */, NULL /*GdkEventButton *event _U_*/, GINT_TO_POINTER(ONLINEPAGE_WIKI));
+}
+
+static void
+help_menu_Downloads_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ topic_menu_cb(NULL/* widget _U_ */, NULL /*GdkEventButton *event _U_*/, GINT_TO_POINTER(ONLINEPAGE_DOWNLOAD));
+}
+
+static void
+help_menu_SampleCaptures_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ topic_menu_cb( NULL/* widget_U_ */, NULL /*GdkEventButton *event _U_*/, GINT_TO_POINTER(ONLINEPAGE_SAMPLE_FILES));
+}
+
+#ifndef NEW_MENU_CODE
+static const char *ui_desc_menubar =
+"<ui>\n"
+" <menubar name ='Menubar'>\n"
+" <menu name= 'FileMenu' action='/File'>\n"
+" <menuitem name='Open' action='/File/Open'/>\n"
+" <menu name='OpenRecent' action='/File/OpenRecent'>\n"
+" <placeholder name='RecentFiles'/>\n"
+" </menu>\n"
+" <menuitem name='Merge' action='/File/Merge'/>\n"
+" <menuitem name='Import' action='/File/Import'/>\n"
+" <menuitem name='Close' action='/File/Close'/>\n"
+" <separator/>\n"
+" <menuitem name='Save' action='/File/Save'/>\n"
+" <menuitem name='SaveAs' action='/File/SaveAs'/>\n"
+" <separator/>\n"
+" <menu name= 'Set' action='/File/Set'>\n"
+" <menuitem name='ListFiles' action='/File/Set/ListFiles'/>\n"
+" <menuitem name='NextFile' action='/File/Set/NextFile'/>\n"
+" <menuitem name='PreviousFile' action='/File/Set/PreviousFile'/>\n"
+" </menu>\n"
+" <separator/>\n"
+" <menu name= 'Export' action='/File/Export'>\n"
+" <menu name= 'File' action='/File/Export/File'>\n"
+" <menuitem name='AsTxt' action='/File/Export/File/Text'/>\n"
+" <menuitem name='AsPostScript' action='/File/Export/File/PostScript'/>\n"
+" <menuitem name='AsCSV' action='/File/Export/File/CSV'/>\n"
+" <menuitem name='AsCArrays' action='/File/Export/File/CArrays'/>\n"
+" <separator/>\n"
+" <menuitem name='AsPSML' action='/File/Export/File/PSML'/>\n"
+" <menuitem name='AsPDML' action='/File/Export/File/PDML'/>\n"
+" <separator/>\n"
+" </menu>\n"
+" <menuitem name='SelectedPacketBytes' action='/File/Export/SelectedPacketBytes'/>\n"
+" <menu name= 'Objects' action='/File/Export/Objects'>\n"
+" <menuitem name='HTTP' action='/File/Export/Objects/HTTP'/>\n"
+" <menuitem name='DICOM' action='/File/Export/Objects/DICOM'/>\n"
+" <menuitem name='SMB' action='/File/Export/Objects/SMB'/>\n"
+" </menu>\n"
+" </menu>\n"
+" <separator/>\n"
+" <menuitem name='Print' action='/File/Print'/>\n"
+" <separator/>\n"
+" <menuitem name='Quit' action='/File/Quit'/>\n"
+" </menu>\n"
+" <menu name= 'EditMenu' action='/Edit'>\n"
+" <menu name= 'Copy' action='/Edit/Copy'>\n"
+" <menuitem name='Description' action='/Edit/Copy/Description'/>\n"
+" <menuitem name='Fieldname' action='/Edit/Copy/Fieldname'/>\n"
+" <menuitem name='Value' action='/Edit/Copy/Value'/>\n"
+" <separator/>\n"
+" <menuitem name='AsFilter' action='/Edit/Copy/AsFilter'/>\n"
+" </menu>\n"
+" <menuitem name='FindPacket' action='/Edit/FindPacket'/>\n"
+" <menuitem name='FindNext' action='/Edit/FindNext'/>\n"
+" <menuitem name='FindPrevious' action='/Edit/FindPrevious'/>\n"
+" <separator/>\n"
+" <menuitem name='MarkPacket' action='/Edit/MarkPacket'/>\n"
+" <menuitem name='MarkAllDisplayedPackets' action='/Edit/MarkAllDisplayedPackets'/>\n"
+" <menuitem name='UnmarkAllDisplayedPackets' action='/Edit/UnmarkAllDisplayedPackets'/>\n"
+" <menuitem name='FindNextMark' action='/Edit/FindNextMark'/>\n"
+" <menuitem name='FindPreviousMark' action='/Edit/FindPreviousMark'/>\n"
+" <separator/>\n"
+" <menuitem name='IgnorePacket' action='/Edit/IgnorePacket'/>\n"
+" <menuitem name='IgnoreAllDisplayedPackets' action='/Edit/IgnoreAllDisplayedPackets'/>\n"
+" <menuitem name='Un-IgnoreAllPackets' action='/Edit/Un-IgnoreAllPackets'/>\n"
+" <separator/>\n"
+" <menuitem name='SetTimeReference' action='/Edit/SetTimeReference'/>\n"
+" <menuitem name='Un-TimeReferenceAllPackets' action='/Edit/Un-TimeReferenceAllPackets'/>\n"
+" <menuitem name='FindNextTimeReference' action='/Edit/FindNextTimeReference'/>\n"
+" <menuitem name='FindPreviousTimeReference' action='/Edit/FindPreviousTimeReference'/>\n"
+" <menuitem name='TimeShift' action='/Edit/TimeShift'/>\n"
+" <separator/>\n"
+" <menuitem name='EditPacket' action='/Edit/EditPacket'/>\n"
+" <separator/>\n"
+" <menuitem name='ConfigurationProfiles' action='/Edit/ConfigurationProfiles'/>\n"
+" <menuitem name='Preferences' action='/Edit/Preferences'/>\n"
+" </menu>\n"
+" <menu name= 'ViewMenu' action='/View'>\n"
+" <menuitem name='MainToolbar' action='/View/MainToolbar'/>\n"
+" <menuitem name='FilterToolbar' action='/View/FilterToolbar'/>\n"
+" <menuitem name='WirelessToolbar' action='/View/WirelessToolbar'/>\n"
+" <menuitem name='Statusbar' action='/View/Statusbar'/>\n"
+" <separator/>\n"
+" <menuitem name='PacketList' action='/View/PacketList'/>\n"
+" <menuitem name='PacketDetails' action='/View/PacketDetails'/>\n"
+" <menuitem name='PacketBytes' action='/View/PacketBytes'/>\n"
+" <separator/>\n"
+" <menu name= 'TimeDisplayFormat' action='/View/TimeDisplayFormat'>\n"
+" <menuitem name='DateandTimeofDay' action='/View/TimeDisplayFormat/DateandTimeofDay'/>\n"
+" <menuitem name='TimeofDay' action='/View/TimeDisplayFormat/TimeofDay'/>\n"
+" <menuitem name='SecondsSinceEpoch' action='/View/TimeDisplayFormat/SecondsSinceEpoch'/>\n"
+" <menuitem name='SecondsSinceBeginningofCapture' action='/View/TimeDisplayFormat/SecondsSinceBeginningofCapture'/>\n"
+" <menuitem name='SecondsSincePreviousCapturedPacket' action='/View/TimeDisplayFormat/SecondsSincePreviousCapturedPacket'/>\n"
+" <menuitem name='SecondsSincePreviousDisplayedPacket' action='/View/TimeDisplayFormat/SecondsSincePreviousDisplayedPacket'/>\n"
+" <menuitem name='UTCDateandTimeofDay' action='/View/TimeDisplayFormat/UTCDateandTimeofDay'/>\n"
+" <menuitem name='UTCTimeofDay' action='/View/TimeDisplayFormat/UTCTimeofDay'/>\n"
+" <separator/>\n"
+" <menuitem name='FileFormatPrecision-Automatic' action='/View/TimeDisplayFormat/FileFormatPrecision-Automatic'/>\n"
+" <menuitem name='FileFormatPrecision-Seconds' action='/View/TimeDisplayFormat/FileFormatPrecision-Seconds'/>\n"
+" <menuitem name='FileFormatPrecision-Deciseconds' action='/View/TimeDisplayFormat/FileFormatPrecision-Deciseconds'/>\n"
+" <menuitem name='FileFormatPrecision-Centiseconds' action='/View/TimeDisplayFormat/FileFormatPrecision-Centiseconds'/>\n"
+" <menuitem name='FileFormatPrecision-Milliseconds' action='/View/TimeDisplayFormat/FileFormatPrecision-Milliseconds'/>\n"
+" <menuitem name='FileFormatPrecision-Microseconds' action='/View/TimeDisplayFormat/FileFormatPrecision-Microseconds'/>\n"
+" <menuitem name='FileFormatPrecision-Nanoseconds' action='/View/TimeDisplayFormat/FileFormatPrecision-Nanoseconds'/>\n"
+" <separator/>\n"
+" <menuitem name='DisplaySecondsWithHoursAndMinutes' action='/View/TimeDisplayFormat/DisplaySecondsWithHoursAndMinutes'/>\n"
+" </menu>\n"
+" <menu name= 'NameResolution' action='/View/NameResolution'>\n"
+" <menuitem name='ResolveName' action='/View/NameResolution/ResolveName'/>\n"
+" <separator/>\n"
+" <menuitem name='EnableforMACLayer' action='/View/NameResolution/EnableforMACLayer'/>\n"
+" <menuitem name='EnableforNetworkLayer' action='/View/NameResolution/EnableforNetworkLayer'/>\n"
+" <menuitem name='EnableforTransportLayer' action='/View/NameResolution/EnableforTransportLayer'/>\n"
+" </menu>\n"
+" <menuitem name='ColorizePacketList' action='/View/ColorizePacketList'/>\n"
+" <menuitem name='AutoScrollinLiveCapture' action='/View/AutoScrollinLiveCapture'/>\n"
+" <separator/>\n"
+" <menuitem name='ZoomIn' action='/View/ZoomIn'/>\n"
+" <menuitem name='ZoomOut' action='/View/ZoomOut'/>\n"
+" <menuitem name='NormalSize' action='/View/NormalSize'/>\n"
+" <separator/>\n"
+" <menuitem name='ResizeAllColumns' action='/View/ResizeAllColumns'/>\n"
+" <menuitem name='DisplayedColumns' action='/View/DisplayedColumns'/>\n"
+" <separator/>\n"
+" <menuitem name='ExpandSubtrees' action='/View/ExpandSubtrees'/>\n"
+" <menuitem name='ExpandAll' action='/View/ExpandAll'/>\n"
+" <menuitem name='CollapseAll' action='/View/CollapseAll'/>\n"
+" <separator/>\n"
+" <menu name= 'ColorizeConversation' action='/View/ColorizeConversation'>\n"
+" <menuitem name='Color1' action='/View/ColorizeConversation/Color 1'/>\n"
+" <menuitem name='Color2' action='/View/ColorizeConversation/Color 2'/>\n"
+" <menuitem name='Color3' action='/View/ColorizeConversation/Color 3'/>\n"
+" <menuitem name='Color4' action='/View/ColorizeConversation/Color 4'/>\n"
+" <menuitem name='Color5' action='/View/ColorizeConversation/Color 5'/>\n"
+" <menuitem name='Color6' action='/View/ColorizeConversation/Color 6'/>\n"
+" <menuitem name='Color7' action='/View/ColorizeConversation/Color 7'/>\n"
+" <menuitem name='Color8' action='/View/ColorizeConversation/Color 8'/>\n"
+" <menuitem name='Color9' action='/View/ColorizeConversation/Color 9'/>\n"
+" <menuitem name='Color10' action='/View/ColorizeConversation/Color 10'/>\n"
+" <menuitem name='NewColoringRule' action='/View/ColorizeConversation/NewColoringRule'/>\n"
+" </menu>\n"
+" <separator/>\n"
+" <menuitem name='ResetColoring1-10' action='/View/ResetColoring1-10'/>\n"
+" <menuitem name='ColoringRules' action='/View/ColoringRules'/>\n"
+" <separator/>\n"
+" <menuitem name='ShowPacketinNewWindow' action='/View/ShowPacketinNewWindow'/>\n"
+" <menuitem name='Reload' action='/View/Reload'/>\n"
+" </menu>\n"
+" <menu name= 'GoMenu' action='/Go'>\n"
+" <menuitem name='Back' action='/Go/Back'/>\n"
+" <menuitem name='Forward' action='/Go/Forward'/>\n"
+" <menuitem name='Goto' action='/Go/Goto'/>\n"
+" <menuitem name='GotoCorrespondingPacket' action='/Go/GotoCorrespondingPacket'/>\n"
+" <separator/>\n"
+" <menuitem name='PreviousPacket' action='/Go/PreviousPacket'/>\n"
+" <menuitem name='NextPacket' action='/Go/NextPacket'/>\n"
+" <menuitem name='FirstPacket' action='/Go/FirstPacket'/>\n"
+" <menuitem name='LastPacket' action='/Go/LastPacket'/>\n"
+" <menuitem name='PreviousPacketInConversation' action='/Go/PreviousPacketInConversation'/>\n"
+" <menuitem name='NextPacketInConversation' action='/Go/NextPacketInConversation'/>\n"
+" </menu>\n"
+" <menu name= 'CaptureMenu' action='/Capture'>\n"
+" <menuitem name='Interfaces' action='/Capture/Interfaces'/>\n"
+" <menuitem name='Options' action='/Capture/Options'/>\n"
+" <menuitem name='Start' action='/Capture/Start'/>\n"
+" <menuitem name='Stop' action='/Capture/Stop'/>\n"
+" <menuitem name='Restart' action='/Capture/Restart'/>\n"
+" <menuitem name='CaptureFilters' action='/Capture/CaptureFilters'/>\n"
+" </menu>\n"
+" <menu name= 'AnalyzeMenu' action='/Analyze'>\n"
+" <menuitem name='DisplayFilters' action='/Analyze/DisplayFilters'/>\n"
+" <menuitem name='DisplayFilterMacros' action='/Analyze/DisplayFilterMacros'/>\n"
+" <separator/>\n"
+" <menuitem name='ApplyasColumn' action='/Analyze/ApplyasColumn'/>\n"
+" <menu name= 'ApplyAsFilter' action='/Analyze/ApplyasFilter'>\n"
+" <menuitem name='Selected' action='/Analyze/ApplyasFilter/Selected'/>\n"
+" <menuitem name='NotSelected' action='/Analyze/ApplyasFilter/NotSelected'/>\n"
+" <menuitem name='AndSelected' action='/Analyze/ApplyasFilter/AndSelected'/>\n"
+" <menuitem name='OrSelected' action='/Analyze/ApplyasFilter/OrSelected'/>\n"
+" <menuitem name='AndNotSelected' action='/Analyze/ApplyasFilter/AndNotSelected'/>\n"
+" <menuitem name='OrNotSelected' action='/Analyze/ApplyasFilter/OrNotSelected'/>\n"
+" </menu>\n"
+" <menu name= 'PrepareaFilter' action='/Analyze/PrepareaFilter'>\n"
+" <menuitem name='Selected' action='/Analyze/PrepareaFilter/Selected'/>\n"
+" <menuitem name='NotSelected' action='/Analyze/PrepareaFilter/NotSelected'/>\n"
+" <menuitem name='AndSelected' action='/Analyze/PrepareaFilter/AndSelected'/>\n"
+" <menuitem name='OrSelected' action='/Analyze/PrepareaFilter/OrSelected'/>\n"
+" <menuitem name='AndNotSelected' action='/Analyze/PrepareaFilter/AndNotSelected'/>\n"
+" <menuitem name='OrNotSelected' action='/Analyze/PrepareaFilter/OrNotSelected'/>\n"
+" </menu>\n"
+" <separator/>\n"
+" <menuitem name='EnabledProtocols' action='/Analyze/EnabledProtocols'/>\n"
+" <menuitem name='DecodeAs' action='/Analyze/DecodeAs'/>\n"
+" <menuitem name='UserSpecifiedDecodes' action='/Analyze/UserSpecifiedDecodes'/>\n"
+" <separator/>\n"
+" <menuitem name='FollowTCPStream' action='/Analyze/FollowTCPStream'/>\n"
+" <menuitem name='FollowUDPStream' action='/Analyze/FollowUDPStream'/>\n"
+" <menuitem name='FollowSSLStream' action='/Analyze/FollowSSLStream'/>\n"
+" <menuitem name='ExpertInfo' action='/Analyze/ExpertInfo'/>\n"
+" <menu name= 'ConversationFilterMenu' action='/Analyze/ConversationFilter'>\n"
+" <placeholder name='Filters'/>\n"
+" </menu>\n"
+" </menu>\n"
+" <menu name= 'StatisticsMenu' action='/Statistics'>\n"
+" <menuitem name='Summary' action='/Statistics/Summary'/>\n"
+" <menuitem name='ProtocolHierarchy' action='/Statistics/ProtocolHierarchy'/>\n"
+" <menuitem name='Conversations' action='/Statistics/Conversations'/>\n"
+" <menuitem name='Endpoints' action='/Statistics/Endpoints'/>\n"
+" <menuitem name='PacketLengths' action='/Statistics/plen'/>\n"
+" <menuitem name='IOGraphs' action='/Statistics/IOGraphs'/>\n"
+" <separator/>\n"
+" <menu name= 'ConversationListMenu' action='/Stataistics/ConversationList'>\n"
+" <menuitem name='Ethernet' action='/Stataistics/ConversationList/Ethernet'/>\n"
+" <menuitem name='FibreChannel' action='/Stataistics/ConversationList/FibreChannel'/>\n"
+" <menuitem name='FDDI' action='/Stataistics/ConversationList/FDDI'/>\n"
+" <menuitem name='IP' action='/Stataistics/ConversationList/IP'/>\n"
+" <menuitem name='IPv6' action='/Stataistics/ConversationList/IPv6'/>\n"
+" <menuitem name='JXTA' action='/Stataistics/ConversationList/JXTA'/>\n"
+" <menuitem name='NCP' action='/Stataistics/ConversationList/NCP'/>\n"
+" <menuitem name='RSVP' action='/Stataistics/ConversationList/RSVP'/>\n"
+" <menuitem name='SCTP' action='/Stataistics/ConversationList/SCTP'/>\n"
+" <menuitem name='TCPIP' action='/Stataistics/ConversationList/TCPIP'/>\n"
+" <menuitem name='TR' action='/Stataistics/ConversationList/TR'/>\n"
+" <menuitem name='UDPIP' action='/Stataistics/ConversationList/UDPIP'/>\n"
+" <menuitem name='USB' action='/Stataistics/ConversationList/USB'/>\n"
+" <menuitem name='WLAN' action='/Stataistics/ConversationList/WLAN'/>\n"
+" </menu>\n"
+" <menu name= 'EndpointListMenu' action='/Statistics/EndpointList'>\n"
+" <menuitem name='Ethernet' action='/Statistics/EndpointList/Ethernet'/>\n"
+" <menuitem name='FibreChannel' action='/Statistics/EndpointList/FibreChannel'/>\n"
+" <menuitem name='FDDI' action='/Statistics/EndpointList/FDDI'/>\n"
+" <menuitem name='IP' action='/Statistics/EndpointList/IP'/>\n"
+" <menuitem name='IPv6' action='/Statistics/EndpointList/IPv6'/>\n"
+" <menuitem name='JXTA' action='/Statistics/EndpointList/JXTA'/>\n"
+" <menuitem name='RSVP' action='/Statistics/EndpointList/RSVP'/>\n"
+" <menuitem name='SCTP' action='/Statistics/EndpointList/SCTP'/>\n"
+" <menuitem name='TCPIP' action='/Statistics/EndpointList/TCPIP'/>\n"
+" <menuitem name='TR' action='/Statistics/EndpointList/TR'/>\n"
+" <menuitem name='UDPIP' action='/Statistics/EndpointList/UDPIP'/>\n"
+" <menuitem name='USB' action='/Statistics/EndpointList/USB'/>\n"
+" <menuitem name='WLAN' action='/Statistics/EndpointList/WLAN'/>\n"
+" </menu>\n"
+" <menu name='ServiceResponseTimeMenu' action='/Statistics/ServiceResponseTime'>\n"
+" <menuitem name='AFP' action='/Statistics/ServiceResponseTime/AFP'/>\n"
+" <menuitem name='ONC-RPC' action='/Statistics/ServiceResponseTime/ONC-RPC'/>\n"
+" <menuitem name='Camel' action='/Statistics/ServiceResponseTime/Camel'/>\n"
+" <menuitem name='DCE-RPC' action='/Statistics/ServiceResponseTime/DCE-RPC'/>\n"
+" <menuitem name='Diameter' action='/Statistics/ServiceResponseTime/Diameter'/>\n"
+" <menuitem name='FibreChannel' action='/Statistics/ServiceResponseTime/FibreChannel'/>\n"
+" <menuitem name='GTP' action='/Statistics/ServiceResponseTime/GTP'/>\n"
+" <menuitem name='H225' action='/Statistics/ServiceResponseTime/H225'/>\n"
+" <menuitem name='LDAP' action='/Statistics/ServiceResponseTime/LDAP'/>\n"
+" <menuitem name='MEGACO' action='/Statistics/ServiceResponseTime/MEGACO'/>\n"
+" <menuitem name='MGCP' action='/Statistics/ServiceResponseTime/MGCP'/>\n"
+" <menuitem name='NCP' action='/Statistics/ServiceResponseTime/NCP'/>\n"
+" <menuitem name='RADIUS' action='/Statistics/ServiceResponseTime/RADIUS'/>\n"
+" <menuitem name='SCSI' action='/Statistics/ServiceResponseTime/SCSI'/>\n"
+" <menuitem name='SMB2' action='/Statistics/ServiceResponseTime/SMB2'/>\n"
+" </menu>\n"
+" <separator/>\n"
+" <menuitem name='ANCP' action='/StatisticsMenu/ancp'/>\n"
+" <menu name= 'BACnetMenu' action='/StatisticsMenu/BACnet'>\n"
+" <menuitem name='bacapp_instanceid' action='/StatisticsMenu/BACnet/bacapp_instanceid'/>\n"
+" <menuitem name='bacapp_ip' action='/StatisticsMenu/BACnet/bacapp_ip'/>\n"
+" <menuitem name='bacapp_objectid' action='/StatisticsMenu/BACnet/bacapp_objectid'/>\n"
+" <menuitem name='bacapp_service' action='/StatisticsMenu/BACnet/bacapp_service'/>\n"
+" </menu>\n"
+" <menuitem name='BOOTP-DHCP' action='/StatisticsMenu/BOOTP-DHCP'/>\n"
+" <menuitem name='Collectd' action='/StatisticsMenu/collectd'/>\n"
+" <menuitem name='Compare' action='/StatisticsMenu/compare'/>\n"
+" <menuitem name='FlowGraph' action='/StatisticsMenu/FlowGraph'/>\n"
+" <menu name= 'HTTPMenu' action='/StatisticsMenu/HTTP'>\n"
+" <menuitem name='http' action='/StatisticsMenu/HTTP/http'/>\n"
+" <menuitem name='http_req' action='/StatisticsMenu/HTTP/http_req'/>\n"
+" <menuitem name='http_srv' action='/StatisticsMenu/HTTP/http_srv'/>\n"
+" </menu>\n"
+" <menuitem name='IPAddresses' action='/StatisticsMenu/ip_hosts'/>\n"
+" <menuitem name='IPDestinations' action='/StatisticsMenu/dests'/>\n"
+" <menuitem name='IPptype' action='/StatisticsMenu/ptype'/>\n"
+" <menuitem name='ONC-RPC-Programs' action='/StatisticsMenu/ONC-RPC-Programs'/>\n"
+" <menu name= 'SametimeMenu' action='/StatisticsMenu/Sametime'>\n"
+" <menuitem name='sametime' action='/StatisticsMenu/Sametime/sametime'/>\n"
+" </menu>\n"
+" <menu name= 'TCPStreamGraphMenu' action='/StatisticsMenu/TCPStreamGraphMenu'>\n"
+" <menuitem name='Sequence-Graph-Stevens' action='/StatisticsMenu/TCPStreamGraphMenu/Time-Sequence-Graph-Stevens'/>\n"
+" <menuitem name='Sequence-Graph-tcptrace' action='/StatisticsMenu/TCPStreamGraphMenu/Time-Sequence-Graph-tcptrace'/>\n"
+" <menuitem name='Throughput-Graph' action='/StatisticsMenu/TCPStreamGraphMenu/Throughput-Graph'/>\n"
+" <menuitem name='RTT-Graph' action='/StatisticsMenu/TCPStreamGraphMenu/RTT-Graph'/>\n"
+" <menuitem name='Window-Scaling-Graph' action='/StatisticsMenu/TCPStreamGraphMenu/Window-Scaling-Graph'/>\n"
+" </menu>\n"
+" <menuitem name='UDPMulticastStreams' action='/StatisticsMenu/UDPMulticastStreams'/>\n"
+" <menuitem name='WLANTraffic' action='/StatisticsMenu/WLANTraffic'/>\n"
+" </menu>\n"
+" <menu name= 'TelephonyMenu' action='/Telephony'>\n"
+" <menu name= 'ANSI' action='/Telephony/ANSI'>\n"
+" <menuitem name='BSMAP' action='/Telephony/ANSI/BSMAP'/>\n"
+" <menuitem name='DTAP' action='/Telephony/ANSI/DTAP'/>\n"
+" <menuitem name='MAP-OP' action='/Telephony/ANSI/MAP-OP'/>\n"
+" </menu>\n"
+" <menu name= 'GSM' action='/Telephony/GSM'>\n"
+" <menuitem name='BSSMAP' action='/Telephony/GSM/BSSMAP'/>\n"
+" <menu name='GSM-DTAP' action='/Telephony/GSM/DTAP'>\n"
+" <menuitem name='CallControl' action='/Telephony/GSM/DTAP/CC'/>\n"
+" <menuitem name='GPRS-MM' action='/Telephony/GSM/DTAP/GMM'/>\n"
+" <menuitem name='GPRS-SM' action='/Telephony/GSM/DTAP/SM'/>\n"
+" <menuitem name='MM' action='/Telephony/GSM/DTAP/MM'/>\n"
+" <menuitem name='RR' action='/Telephony/GSM/DTAP/RR'/>\n"
+" <menuitem name='SMS' action='/Telephony/GSM/DTAP/SMS'/>\n"
+" <menuitem name='TP' action='/Telephony/GSM/DTAP/TP'/>\n"
+" <menuitem name='SS' action='/Telephony/GSM/DTAP/SS'/>\n"
+" </menu>\n"
+" <menuitem name='SACCH' action='/Telephony/GSM/SACCH'/>\n"
+" <menuitem name='MAP-OP' action='/Telephony/GSM/MAP-OP'/>\n"
+" <menuitem name='MAP-Summary' action='/Telephony/GSM/MAPSummary'/>\n"
+" </menu>\n"
+" <menuitem name='H225' action='/Telephony/H225'/>\n"
+" <menu name= 'IAX2menu' action='/Telephony/IAX2'>\n"
+" <menuitem name='StreamAnalysis' action='/Telephony/IAX2/StreamAnalysis'/>\n"
+" </menu>\n"
+" <menuitem name='ISUP' action='/Telephony/isup_msg'/>\n"
+" <menu name= 'LTEmenu' action='/Telephony/LTE'>\n"
+" <menuitem name='LTE_MAC' action='/Telephony/LTE/MAC'/>\n"
+" <menuitem name='LTE_RLC' action='/Telephony/LTE/RLC'/>\n"
+" </menu>\n"
+" <menu name= 'MTP3menu' action='/Telephony/MTP3'>\n"
+" <menuitem name='MSUs' action='/Telephony/MTP3/MSUs'/>\n"
+" <menuitem name='MSUSummary' action='/Telephony/MTP3/MSUSummary'/>\n"
+" </menu>\n"
+" <menu name= 'RTPmenu' action='/Telephony/RTP'>\n"
+" <menuitem name='ShowAllStreams' action='/Telephony/RTP/ShowAllStreams'/>\n"
+" <menuitem name='StreamAnalysis' action='/Telephony/RTP/StreamAnalysis'/>\n"
+" </menu>\n"
+" <menu name= 'RTSPmenu' action='/Telephony/RTSP'>\n"
+" <menuitem name='rtsp' action='/Telephony/RTSP/rtsp'/>\n"
+" </menu>\n"
+" <menu name= 'SCTPmenu' action='/Telephony/SCTP'>\n"
+" <menuitem name='AnalysethisAssociation' action='/Telephony/SCTP/AnalysethisAssociation'/>\n"
+" <menuitem name='ShowAllAssociations' action='/Telephony/SCTP/ShowAllAssociations'/>\n"
+" <menuitem name='ChunkCounter' action='/Telephony/SCTP/ChunkCounter'/>\n"
+" </menu>\n"
+" <menuitem name='SIP' action='/Telephony/SIP'/>\n"
+" <menuitem name='SMPP' action='/Telephony/smpp_commands'/>\n"
+" <menuitem name='UCP' action='/Telephony/ucp_messages'/>\n"
+" <menuitem name='VoIPCalls' action='/Telephony/VoIPCalls'/>\n"
+" <menuitem name='WSP' action='/Telephony/WSP'/>\n"
+" </menu>\n"
+" <menu name= 'ToolsMenu' action='/Tools'>\n"
+" <menuitem name='FirewallACLRules' action='/Tools/FirewallACLRules'/>\n"
+" </menu>\n"
+" <menu name= 'InternalsMenu' action='/Internals'>\n"
+" <menuitem name='Dissectortables' action='/Internals/Dissectortables'/>\n"
+" <menuitem name='SupportedProtocols' action='/Internals/SupportedProtocols'/>\n"
+" </menu>\n"
+" <menu name= 'HelpMenu' action='/Help'>\n"
+" <menuitem name='Contents' action='/Help/Contents'/>\n"
+" <menu name= 'ManualPages' action='/Help/ManualPages'>\n"
+" <menuitem name='Wireshark' action='/Help/ManualPages/Wireshark'/>\n"
+" <menuitem name='WiresharkFilter' action='/Help/ManualPages/WiresharkFilter'/>\n"
+" <separator/>\n"
+" <menuitem name='TShark' action='/Help/ManualPages/TShark'/>\n"
+" <menuitem name='RawShark' action='/Help/ManualPages/RawShark'/>\n"
+" <menuitem name='Dumpcap' action='/Help/ManualPages/Dumpcap'/>\n"
+" <menuitem name='Mergecap' action='/Help/ManualPages/Mergecap'/>\n"
+" <menuitem name='Editcap' action='/Help/ManualPages/Editcap'/>\n"
+" <menuitem name='Text2pcap' action='/Help/ManualPages/Text2pcap'/>\n"
+" </menu>\n"
+" <separator/>\n"
+" <menuitem name='Website' action='/Help/Website'/>\n"
+" <menuitem name='FAQs' action='/Help/FAQs'/>\n"
+" <menuitem name='Downloads' action='/Help/Downloads'/>\n"
+" <separator/>\n"
+" <menuitem name='Wiki' action='/Help/Wiki'/>\n"
+" <menuitem name='SampleCaptures' action='/Help/SampleCaptures'/>\n"
+" <separator/>\n"
+" <menuitem name='AboutWireshark' action='/Help/AboutWireshark'/>\n"
+" </menu>\n"
+" </menubar>\n"
+"</ui>\n";
+#endif
+
+
+/*
+ * Main menu.
+ *
+ * Please do not use keystrokes that are used as "universal" shortcuts in
+ * various desktop environments:
+ *
+ * Windows:
+ * http://support.microsoft.com/kb/126449
+ *
+ * GNOME:
+ * http://library.gnome.org/users/user-guide/nightly/keyboard-skills.html.en
+ *
+ * KDE:
+ * http://developer.kde.org/documentation/standards/kde/style/keys/shortcuts.html
+ *
+ * In particular, do not use the following <control> sequences for anything
+ * other than their standard purposes:
+ *
+ * <control>O File->Open
+ * <control>S File->Save
+ * <control>P File->Print
+ * <control>W File->Close
+ * <control>Q File->Quit
+ * <control>Z Edit->Undo (which we don't currently have)
+ * <control>X Edit->Cut (which we don't currently have)
+ * <control>C Edit->Copy (which we don't currently have)
+ * <control>V Edit->Paste (which we don't currently have)
+ * <control>A Edit->Select All (which we don't currently have)
+ *
+ * Note that some if not all of the Edit keys above already perform those
+ * functions in text boxes, such as the Filter box. Do no, under any
+ * circumstances, make a change that keeps them from doing so.
+ */
+
+/*
+ * GtkActionEntry
+ * typedef struct {
+ * const gchar *name;
+ * const gchar *stock_id;
+ * const gchar *label;
+ * const gchar *accelerator;
+ * const gchar *tooltip;
+ * GCallback callback;
+ * } GtkActionEntry;
+ * const gchar *name; The name of the action.
+ * const gchar *stock_id; The stock id for the action, or the name of an icon from the icon theme.
+ * const gchar *label; The label for the action. This field should typically be marked for translation,
+ * see gtk_action_group_set_translation_domain().
+ * If label is NULL, the label of the stock item with id stock_id is used.
+ * const gchar *accelerator; The accelerator for the action, in the format understood by gtk_accelerator_parse().
+ * const gchar *tooltip; The tooltip for the action. This field should typically be marked for translation,
+ * see gtk_action_group_set_translation_domain().
+ * GCallback callback; The function to call when the action is activated.
+ *
+ */
+static const GtkActionEntry main_menu_bar_entries[] = {
+ /* Top level */
+ { "/File", NULL, "_File", NULL, NULL, NULL },
+ { "/Edit", NULL, "_Edit", NULL, NULL, NULL },
+ { "/View", NULL, "_View", NULL, NULL, NULL },
+ { "/Go", NULL, "_Go", NULL, NULL, NULL },
+ { "/Capture", NULL, "_Capture", NULL, NULL, NULL },
+ { "/Analyze", NULL, "_Analyze", NULL, NULL, NULL },
+ { "/Statistics", NULL, "_Statistics", NULL, NULL, NULL },
+ { "/Telephony", NULL, "Telephon_y", NULL, NULL, NULL },
+ { "/Tools", NULL, "_Tools", NULL, NULL, NULL },
+ { "/Internals", NULL, "_Internals", NULL, NULL, NULL },
+ { "/Help", NULL, "_Help", NULL, NULL, NULL },
+
+ { "/File/Open", GTK_STOCK_OPEN, "_Open...", "<control>O", "Open a file", G_CALLBACK(file_open_cmd_cb) },
+ { "/File/OpenRecent", NULL, "Open _Recent", NULL, NULL, NULL },
+ { "/File/Merge", NULL, "_Merge...", NULL, NULL, G_CALLBACK(file_merge_cmd_cb) },
+ { "/File/Import", NULL, "_Import...", NULL, NULL, G_CALLBACK(file_import_cmd_cb) },
+ { "/File/Close", GTK_STOCK_CLOSE, "_Close", "<control>W", NULL, G_CALLBACK(file_close_cmd_cb) },
+
+ { "/File/Save", GTK_STOCK_SAVE, "_Save", "<control>S", NULL, G_CALLBACK(file_save_cmd_cb) },
+ { "/File/SaveAs", GTK_STOCK_SAVE_AS, "Save _As...", "<shift><control>S", NULL, G_CALLBACK(file_save_as_cmd_cb) },
+
+ { "/File/Set", NULL, "File Set", NULL, NULL, NULL },
+ { "/File/Export", NULL, "Export", NULL, NULL, NULL },
+ { "/File/Print", GTK_STOCK_PRINT, "_Print...", "<control>P", NULL, G_CALLBACK(file_print_cmd_cb) },
+ { "/File/Quit", GTK_STOCK_QUIT, "_Quit", "<control>Q", NULL, G_CALLBACK(file_quit_cmd_cb) },
+
+ { "/File/Set/ListFiles", WIRESHARK_STOCK_FILE_SET_LIST, "List Files", NULL, NULL, G_CALLBACK(fileset_cb) },
+ { "/File/Set/NextFile", WIRESHARK_STOCK_FILE_SET_NEXT, "Next File", NULL, NULL, G_CALLBACK(fileset_next_cb) },
+ { "/File/Set/PreviousFile",WIRESHARK_STOCK_FILE_SET_PREVIOUS, "Previous File", NULL, NULL, G_CALLBACK(fileset_previous_cb) },
+
+ { "/File/Export/File", NULL, "File", NULL, NULL, NULL },
+ { "/File/Export/File/Text", NULL, "as \"Plain _Text\" file...", NULL, NULL, G_CALLBACK(export_text_cmd_cb) },
+ { "/File/Export/File/PostScript", NULL, "as \"_PostScript\" file...", NULL, NULL, G_CALLBACK(export_ps_cmd_cb) },
+ { "/File/Export/File/CSV", NULL, "as \"_CSV\" (Comma Separated Values packet summary) file...",
+ NULL, NULL, G_CALLBACK(export_csv_cmd_cb) },
+ { "/File/Export/File/CArrays", NULL, "as \"C _Arrays\" (packet bytes) file...",
+ NULL, NULL, G_CALLBACK(export_carrays_cmd_cb) },
+ { "/File/Export/File/PSML", NULL, "as XML - \"P_SML\" (packet summary) file...",
+ NULL, NULL, G_CALLBACK(export_psml_cmd_cb) },
+ { "/File/Export/File/PDML", NULL, "as XML - \"P_DML\" (packet details) file...",
+ NULL, NULL, G_CALLBACK(export_pdml_cmd_cb) },
+ { "/File/Export/SelectedPacketBytes", NULL, "Selected Packet _Bytes...", "<control>H", NULL, G_CALLBACK(savehex_cb) },
+ { "/File/Export/SslSessionKeys", NULL, "SSL Session Keys...", NULL, NULL, G_CALLBACK(savesslkeys_cb) },
+ { "/File/Export/Objects", NULL, "Objects", NULL, NULL, NULL },
+ { "/File/Export/Objects/HTTP", NULL, "_HTTP", NULL, NULL, G_CALLBACK(eo_http_cb) },
+ { "/File/Export/Objects/DICOM", NULL, "_DICOM", NULL, NULL, G_CALLBACK(eo_dicom_cb) },
+ { "/File/Export/Objects/SMB", NULL, "_SMB", NULL, NULL, G_CALLBACK(eo_smb_cb) },
+
+
+ { "/Edit/Copy", NULL, "Copy", NULL, NULL, NULL },
+
+ { "/Edit/Copy/Description", NULL, "Description", "<shift><control>D", NULL, G_CALLBACK(copy_description_cb) },
+ { "/Edit/Copy/Fieldname", NULL, "Fieldname", "<shift><control>F", NULL, G_CALLBACK(copy_fieldname_cb) },
+ { "/Edit/Copy/Value", NULL, "Value", "<shift><control>V", NULL, G_CALLBACK(copy_value_cb) },
+ { "/Edit/Copy/AsFilter", NULL, "As Filter", "<shift><control>C", NULL, G_CALLBACK(copy_as_filter_cb) },
+
+#if 0
+ /*
+ * Un-#if this when we actually implement Cut/Copy/Paste for the
+ * packet list and packet detail windows.
+ *
+ * Note: when we implement Cut/Copy/Paste in those windows, we
+ * will almost certainly want to allow multiple packets to be
+ * selected in the packet list pane and multiple packet detail
+ * items to be selected in the packet detail pane, so that
+ * the user can, for example, copy the summaries of multiple
+ * packets to the clipboard from the packet list pane and multiple
+ * packet detail items - perhaps *all* packet detail items - from
+ * the packet detail pane. Given that, we'll also want to
+ * implement Select All.
+ *
+ * If multiple packets are selected, we would probably display nothing
+ * in the packet detail pane, just as we do if no packet is selected,
+ * and any menu items etc. that would pertain only to a single packet
+ * would be disabled.
+ *
+ * If multiple packet detail items are selected, we would probably
+ * disable all items that pertain only to a single packet detail
+ * item, such as some items in the status bar.
+ *
+ * XXX - the actions for these will be different depending on what
+ * widget we're in; ^C should copy from the filter text widget if
+ * we're in that widget, the packet list if we're in that widget
+ * (presumably copying the summaries of selected packets to the
+ * clipboard, e.g. the text copy would be the text of the columns),
+ * the packet detail if we're in that widget (presumably copying
+ * the contents of selected protocol tree items to the clipboard,
+ * e.g. the text copy would be the text displayed for those items),
+ * etc..
+ *
+ * Given that those menu items should also affect text widgets
+ * such as the filter box, we would again want Select All, and,
+ * at least for the filter box, we would also want Undo and Redo.
+ * We would only want Cut, Paste, Undo, and Redo for the packet
+ * list and packet detail panes if we support modifying them.
+ */
+ {"/Edit/_Undo", "<control>Z", NULL,
+ 0, "<StockItem>", GTK_STOCK_UNDO,},
+ {"/Edit/_Redo", "<shift><control>Z", NULL,
+ 0, "<StockItem>", GTK_STOCK_REDO,},
+ {"/Edit/<separator>", NULL, NULL, 0, "<Separator>", NULL,},
+ {"/Edit/Cu_t", "<control>X", NULL,
+ 0, "<StockItem>", GTK_STOCK_CUT,},
+ {"/Edit/_Copy", "<control>C", NULL,
+ 0, "<StockItem>", GTK_STOCK_COPY,},
+ {"/Edit/_Paste", "<control>V", NULL,
+ 0, "<StockItem>", GTK_STOCK_PASTE,},
+ {"/Edit/<separator>", NULL, NULL, 0, "<Separator>", NULL,},
+ {"/Edit/Select _All", "<control>A", NULL, 0,
+ "<StockItem>", GTK_STOCK_SELECT_ALL,},
+#endif /* 0 */
+ { "/Edit/FindPacket", GTK_STOCK_FIND, "_Find Packet...", "<control>F", NULL, G_CALLBACK(find_frame_cb) },
+ { "/Edit/FindNext", NULL, "Find Ne_xt", "<control>N", NULL, G_CALLBACK(find_next_cb) },
+ { "/Edit/FindPrevious", NULL, "Find Pre_vious", "<control>B", NULL, G_CALLBACK(find_previous_cb) },
+
+ { "/Edit/MarkPacket", NULL, "_Mark Packet (toggle)", "<control>M", NULL, G_CALLBACK(new_packet_list_mark_frame_cb) },
+ { "/Edit/ToggleMarkingOfAllDisplayedPackets", NULL, "Toggle Marking Of All Displayed Packets", "<shift><alt><control>M", NULL, G_CALLBACK(new_packet_list_toggle_mark_all_displayed_frames_cb) },
+ { "/Edit/MarkAllDisplayedPackets", NULL, "Mark All Displayed Packets", "<shift><control>M", NULL, G_CALLBACK(new_packet_list_mark_all_displayed_frames_cb) },
+ { "/Edit/UnmarkAllDisplayedPackets", NULL, "_Unmark All Displayed Packets", "<alt><control>M", NULL, G_CALLBACK(new_packet_list_unmark_all_displayed_frames_cb) },
+ { "/Edit/FindNextMark", NULL, "Find Next Mark", "<shift><control>N", NULL, G_CALLBACK(find_next_mark_cb) },
+ { "/Edit/FindPreviousMark", NULL, "Find Next Mark", "<shift><control>B", NULL, G_CALLBACK(find_prev_mark_cb) },
+
+ { "/Edit/IgnorePacket", NULL, "_Ignore Packet (toggle)", "<control>X", NULL, G_CALLBACK(new_packet_list_ignore_frame_cb) },
+ /*
+ * XXX - this next one overrides /Edit/Copy/Description
+ */
+ { "/Edit/IgnoreAllDisplayedPackets", NULL, "_Ignore All Displayed Packets (toggle)","<alt><shift><control>X", NULL, G_CALLBACK(new_packet_list_ignore_all_displayed_frames_cb) },
+ { "/Edit/Un-IgnoreAllPackets", NULL, "U_n-Ignore All Packets", "<shift><control>X", NULL, G_CALLBACK(new_packet_list_unignore_all_frames_cb) },
+ { "/Edit/SetTimeReference", WIRESHARK_STOCK_TIME, "Set Time Reference (toggle)", "<control>T", NULL, G_CALLBACK(set_reftime_cb) },
+ { "/Edit/Un-TimeReferenceAllPackets",NULL, "Un-Time Reference All Packets", "<alt><control>T", NULL, G_CALLBACK(new_packet_list_untime_reference_all_frames_cb) },
+ { "/Edit/FindNextTimeReference", NULL, "Find Next Time Reference", "<alt><control>N", NULL, G_CALLBACK(find_next_ref_time_cb) },
+ { "/Edit/FindPreviousTimeReference", NULL, "Find Previous Time Reference", "<alt><control>B", NULL, G_CALLBACK(find_previous_ref_time_cb) },
+ { "/Edit/TimeShift", WIRESHARK_STOCK_TIME, "Time Shift...", "<shift><control>T", NULL, G_CALLBACK(time_shift_cb) },
+
+ { "/Edit/ConfigurationProfiles", NULL, "_Configuration Profiles...", "<shift><control>A", NULL, G_CALLBACK(profile_dialog_cb) },
+ { "/Edit/Preferences", GTK_STOCK_PREFERENCES, "_Preferences...", "<shift><control>P", NULL, G_CALLBACK(menus_prefs_cb) },
+ { "/Edit/EditPacket", NULL, "_Edit Packet", NULL, NULL, G_CALLBACK(edit_window_cb) },
+
+ { "/View/TimeDisplayFormat", NULL, "_Time Display Format", NULL, NULL, NULL },
+
+ { "/View/NameResolution", NULL, "Name Resol_ution", NULL, NULL, NULL },
+ { "/View/ZoomIn", GTK_STOCK_ZOOM_IN, "_Zoom In", "<control>plus", NULL, G_CALLBACK(view_zoom_in_cb) },
+ { "/View/ZoomOut", GTK_STOCK_ZOOM_OUT, "Zoom _Out", "<control>minus", NULL, G_CALLBACK(view_zoom_out_cb) },
+ { "/View/NormalSize", GTK_STOCK_ZOOM_100, "_Normal Size", "<control>equal", NULL, G_CALLBACK(view_zoom_100_cb) },
+ { "/View/ResizeAllColumns", WIRESHARK_STOCK_RESIZE_COLUMNS, "Resize All Columns", "<shift><control>R", NULL, G_CALLBACK(new_packet_list_resize_columns_cb) },
+ { "/View/DisplayedColumns", NULL, "Displayed Columns", NULL, NULL, NULL },
+ { "/View/ExpandSubtrees", NULL, "Expand Subtrees", NULL, NULL, G_CALLBACK(expand_tree_cb) },
+ { "/View/ExpandAll", NULL, "Expand All", NULL, NULL, G_CALLBACK(expand_all_cb) },
+ { "/View/CollapseAll", NULL, "Collapse All", NULL, NULL, G_CALLBACK(collapse_all_cb) },
+ { "/View/ColorizeConversation", NULL, "Colorize Conversation",NULL, NULL, NULL },
+
+ { "/View/ColorizeConversation/Color 1", WIRESHARK_STOCK_COLOR1, "Color 1", NULL, NULL, G_CALLBACK(view_menu_color_conv_color1_cb) },
+ { "/View/ColorizeConversation/Color 2", WIRESHARK_STOCK_COLOR2, "Color 2", NULL, NULL, G_CALLBACK(view_menu_color_conv_color2_cb) },
+ { "/View/ColorizeConversation/Color 3", WIRESHARK_STOCK_COLOR3, "Color 3", NULL, NULL, G_CALLBACK(view_menu_color_conv_color3_cb) },
+ { "/View/ColorizeConversation/Color 4", WIRESHARK_STOCK_COLOR4, "Color 4", NULL, NULL, G_CALLBACK(view_menu_color_conv_color4_cb) },
+ { "/View/ColorizeConversation/Color 5", WIRESHARK_STOCK_COLOR5, "Color 5", NULL, NULL, G_CALLBACK(view_menu_color_conv_color5_cb) },
+ { "/View/ColorizeConversation/Color 6", WIRESHARK_STOCK_COLOR6, "Color 6", NULL, NULL, G_CALLBACK(view_menu_color_conv_color6_cb) },
+ { "/View/ColorizeConversation/Color 7", WIRESHARK_STOCK_COLOR7, "Color 7", NULL, NULL, G_CALLBACK(view_menu_color_conv_color7_cb) },
+ { "/View/ColorizeConversation/Color 8", WIRESHARK_STOCK_COLOR8, "Color 8", NULL, NULL, G_CALLBACK(view_menu_color_conv_color8_cb) },
+ { "/View/ColorizeConversation/Color 9", WIRESHARK_STOCK_COLOR9, "Color 9", NULL, NULL, G_CALLBACK(view_menu_color_conv_color9_cb) },
+ { "/View/ColorizeConversation/Color 10", WIRESHARK_STOCK_COLOR0, "Color 10", NULL, NULL, G_CALLBACK(view_menu_color_conv_color10_cb) },
+ { "/View/ColorizeConversation/NewColoringRule", NULL, "New Coloring Rule...", NULL, NULL, G_CALLBACK(view_menu_color_conv_new_rule_cb) },
+
+ { "/View/ResetColoring1-10", NULL, "Reset Coloring 1-10", "<control>space", NULL, G_CALLBACK(view_menu_reset_coloring_cb) },
+ { "/View/ColoringRules", GTK_STOCK_SELECT_COLOR, "_Coloring Rules...", NULL, NULL, G_CALLBACK(color_display_cb) },
+ { "/View/ShowPacketinNewWindow", NULL, "Show Packet in New _Window", NULL, NULL, G_CALLBACK(new_window_cb) },
+ { "/View/Reload", GTK_STOCK_REFRESH, "_Reload", "<control>R", NULL, G_CALLBACK(file_reload_cmd_cb) },
+
+
+ { "/Go/Back", GTK_STOCK_GO_BACK, "_Back", "<alt>Left", NULL, G_CALLBACK(history_back_cb) },
+ { "/Go/Forward", GTK_STOCK_GO_FORWARD, "_Forward", "<alt>Right", NULL, G_CALLBACK(history_forward_cb) },
+ { "/Go/Goto", GTK_STOCK_JUMP_TO, "_Go to Packet...", "<control>G", NULL, G_CALLBACK(goto_frame_cb) },
+ { "/Go/GotoCorrespondingPacket", NULL, "Go to _Corresponding Packet", NULL, NULL, G_CALLBACK(goto_framenum_cb) },
+ { "/Go/PreviousPacket", GTK_STOCK_GO_UP, "Previous Packet", "<control>Up", NULL, G_CALLBACK(goto_previous_frame_cb) },
+ { "/Go/NextPacket", GTK_STOCK_GO_DOWN, "Next Packet", "<control>Down", NULL, G_CALLBACK(goto_next_frame_cb) },
+ { "/Go/FirstPacket", GTK_STOCK_GOTO_TOP, "F_irst Packet", "<control>Home", NULL, G_CALLBACK(goto_top_frame_cb) },
+ { "/Go/LastPacket", GTK_STOCK_GOTO_BOTTOM, "_Last Packet", "<control>End", NULL, G_CALLBACK(goto_bottom_frame_cb) },
+ { "/Go/PreviousPacketInConversation", GTK_STOCK_GO_UP, "Previous Packet In Conversation", "<control>comma", NULL, G_CALLBACK(goto_previous_frame_conversation_cb) },
+ { "/Go/NextPacketInConversation", GTK_STOCK_GO_DOWN, "Next Packet In Conversation", "<control>period", NULL, G_CALLBACK(goto_next_frame_conversation_cb) },
+
+/*
+ * TODO Move this menu to capture_if_dlg.c
+ * eg put a "place holder" in the UI description and
+ * make a call from main_menubar.c i.e build_capture_menu()
+ * ad do the UI stuff there.
+ */
+ { "/Capture/Interfaces", WIRESHARK_STOCK_CAPTURE_INTERFACES, "_Interfaces...", "<control>I", NULL, G_CALLBACK(capture_cb) },
+ { "/Capture/Options", WIRESHARK_STOCK_CAPTURE_OPTIONS, "_Options...", "<control>K", NULL, G_CALLBACK(capture_cb) },
+ { "/Capture/Start", WIRESHARK_STOCK_CAPTURE_START, "_Start", "<control>E", NULL, G_CALLBACK(capture_cb) },
+ { "/Capture/Stop", WIRESHARK_STOCK_CAPTURE_STOP, "S_top", "<control>E", NULL, G_CALLBACK(capture_cb) },
+ { "/Capture/Restart", WIRESHARK_STOCK_CAPTURE_RESTART, "_Restart", "<control>R", NULL, G_CALLBACK(capture_cb) },
+ { "/Capture/CaptureFilters", WIRESHARK_STOCK_CAPTURE_FILTER, "Capture _Filters...", NULL, NULL, G_CALLBACK(capture_cb) },
+
+ { "/Analyze/DisplayFilters", WIRESHARK_STOCK_DISPLAY_FILTER, "_Display Filters...", NULL, NULL, G_CALLBACK(dfilter_dialog_cb) },
+
+ { "/Analyze/DisplayFilterMacros", NULL, "Display Filter _Macros...", NULL, NULL, G_CALLBACK(macros_dialog_cb) },
+ { "/Analyze/ApplyasColumn", NULL, "Apply as Column", NULL, NULL, G_CALLBACK(apply_as_custom_column_cb) },
+ { "/Analyze/ApplyasFilter", NULL, "Apply as Filter", NULL, NULL, NULL },
+
+ { "/Analyze/ApplyasFilter/Selected", NULL, "_Selected" , NULL, NULL, G_CALLBACK(tree_view_menu_apply_selected_cb) },
+ { "/Analyze/ApplyasFilter/NotSelected", NULL, "_Not Selected", NULL, NULL, G_CALLBACK(tree_view_menu_apply_not_selected_cb) },
+ { "/Analyze/ApplyasFilter/AndSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " _and Selected", NULL, NULL, G_CALLBACK(tree_view_menu_apply_and_selected_cb) },
+ { "/Analyze/ApplyasFilter/OrSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " _or Selected", NULL, NULL, G_CALLBACK(tree_view_menu_apply_or_selected_cb) },
+ { "/Analyze/ApplyasFilter/AndNotSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " a_nd not Selected", NULL, NULL, G_CALLBACK(tree_view_menu_apply_and_not_selected_cb) },
+ { "/Analyze/ApplyasFilter/OrNotSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " o_r not Selected", NULL, NULL, G_CALLBACK(tree_view_menu_apply_or_not_selected_cb) },
+
+ { "/Analyze/PrepareaFilter", NULL, "Prepare a Filter", NULL, NULL, NULL },
+ { "/Analyze/PrepareaFilter/Selected", NULL, "_Selected" , NULL, NULL, G_CALLBACK(tree_view_menu_prepare_selected_cb) },
+ { "/Analyze/PrepareaFilter/NotSelected", NULL, "_Not Selected", NULL, NULL, G_CALLBACK(tree_view_menu_prepare_not_selected_cb) },
+ { "/Analyze/PrepareaFilter/AndSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " _and Selected", NULL, NULL, G_CALLBACK(tree_view_menu_prepare_and_selected_cb) },
+ { "/Analyze/PrepareaFilter/OrSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " _or Selected", NULL, NULL, G_CALLBACK(tree_view_menu_prepare_or_selected_cb) },
+ { "/Analyze/PrepareaFilter/AndNotSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " a_nd not Selected", NULL, NULL, G_CALLBACK(tree_view_menu_prepare_and_not_selected_cb) },
+ { "/Analyze/PrepareaFilter/OrNotSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " o_r not Selected", NULL, NULL, G_CALLBACK(tree_view_menu_prepare_or_not_selected_cb) },
+
+ { "/Analyze/EnabledProtocols", WIRESHARK_STOCK_CHECKBOX, "_Enabled Protocols...", "<shift><control>E", NULL, G_CALLBACK(proto_cb) },
+ { "/Analyze/DecodeAs", WIRESHARK_STOCK_DECODE_AS, "Decode _As...", NULL, NULL, G_CALLBACK(decode_as_cb) },
+ { "/Analyze/UserSpecifiedDecodes", WIRESHARK_STOCK_DECODE_AS, "_User Specified Decodes...", NULL, NULL, G_CALLBACK(decode_show_cb) },
+
+ { "/Analyze/FollowTCPStream", NULL, "Follow TCP Stream", NULL, NULL, G_CALLBACK(follow_tcp_stream_cb) },
+ { "/Analyze/FollowUDPStream", NULL, "Follow UDP Stream", NULL, NULL, G_CALLBACK(follow_udp_stream_cb) },
+ { "/Analyze/FollowSSLStream", NULL, "Follow SSL Stream", NULL, NULL, G_CALLBACK(follow_ssl_stream_cb) },
+
+ { "/Analyze/ExpertInfo", WIRESHARK_STOCK_EXPERT_INFO, "Expert _Info", NULL, NULL, G_CALLBACK(expert_comp_dlg_launch) },
+
+ { "/Analyze/ConversationFilter", NULL, "Conversation Filter", NULL, NULL, NULL },
+
+
+ { "/Stataistics/ConversationList", NULL, "_Conversation List", NULL, NULL, NULL },
+ { "/Stataistics/ConversationList/Ethernet", WIRESHARK_STOCK_CONVERSATIONS, "Ethernet", NULL, NULL, G_CALLBACK(eth_endpoints_cb) },
+ { "/Stataistics/ConversationList/FibreChannel", WIRESHARK_STOCK_CONVERSATIONS, "Fibre Channel", NULL, NULL, G_CALLBACK(fc_endpoints_cb) },
+ { "/Stataistics/ConversationList/FDDI", WIRESHARK_STOCK_CONVERSATIONS, "FDDI", NULL, NULL, G_CALLBACK(fddi_endpoints_cb) },
+ { "/Stataistics/ConversationList/IP", WIRESHARK_STOCK_CONVERSATIONS, "IPv4", NULL, NULL, G_CALLBACK(ip_endpoints_cb) },
+ { "/Stataistics/ConversationList/IPv6", WIRESHARK_STOCK_CONVERSATIONS, "IPv6", NULL, NULL, G_CALLBACK(ipv6_endpoints_cb) },
+ { "/Stataistics/ConversationList/IPX", WIRESHARK_STOCK_CONVERSATIONS, "IPX", NULL, NULL, G_CALLBACK(ipx_endpoints_cb) },
+ { "/Stataistics/ConversationList/JXTA", WIRESHARK_STOCK_CONVERSATIONS, "JXTA", NULL, NULL, G_CALLBACK(jxta_conversation_cb) },
+ { "/Stataistics/ConversationList/NCP", WIRESHARK_STOCK_CONVERSATIONS, "NCP", NULL, NULL, G_CALLBACK(ncp_endpoints_cb) },
+ { "/Stataistics/ConversationList/RSVP", WIRESHARK_STOCK_CONVERSATIONS, "RSVP", NULL, NULL, G_CALLBACK(rsvp_endpoints_cb) },
+ { "/Stataistics/ConversationList/SCTP", WIRESHARK_STOCK_CONVERSATIONS, "SCTP", NULL, NULL, G_CALLBACK(sctp_conversation_cb) },
+ { "/Stataistics/ConversationList/TCPIP", WIRESHARK_STOCK_CONVERSATIONS, "TCP (IPv4 & IPv6)", NULL, NULL, G_CALLBACK(tcpip_conversation_cb) },
+ { "/Stataistics/ConversationList/TR", WIRESHARK_STOCK_CONVERSATIONS, "Token Ring", NULL, NULL, G_CALLBACK(tr_conversation_cb) },
+ { "/Stataistics/ConversationList/UDPIP", WIRESHARK_STOCK_CONVERSATIONS, "UDP (IPv4 & IPv6)", NULL, NULL, G_CALLBACK(udpip_conversation_cb) },
+ { "/Stataistics/ConversationList/USB", WIRESHARK_STOCK_CONVERSATIONS, "USB", NULL, NULL, G_CALLBACK(usb_endpoints_cb) },
+ { "/Stataistics/ConversationList/WLAN", WIRESHARK_STOCK_CONVERSATIONS, "WLAN", NULL, NULL, G_CALLBACK(wlan_endpoints_cb) },
+
+ { "/Statistics/EndpointList", NULL, "_Endpoint List", NULL, NULL, NULL },
+ { "/Statistics/EndpointList/Ethernet", WIRESHARK_STOCK_ENDPOINTS, "Ethernet", NULL, NULL, G_CALLBACK(gtk_eth_hostlist_cb) },
+ { "/Statistics/EndpointList/FibreChannel", WIRESHARK_STOCK_ENDPOINTS, "Fibre Channel", NULL, NULL, G_CALLBACK(gtk_fc_hostlist_cb) },
+ { "/Statistics/EndpointList/FDDI", WIRESHARK_STOCK_ENDPOINTS, "FDDI", NULL, NULL, G_CALLBACK(gtk_fddi_hostlist_cb) },
+ { "/Statistics/EndpointList/IP", WIRESHARK_STOCK_ENDPOINTS, "IPv4", NULL, NULL, G_CALLBACK(gtk_ip_hostlist_cb) },
+ { "/Statistics/EndpointList/IPv6", WIRESHARK_STOCK_ENDPOINTS, "IPv6", NULL, NULL, G_CALLBACK(gtk_ipv6_hostlist_cb) },
+ { "/Statistics/EndpointList/IPX", WIRESHARK_STOCK_ENDPOINTS, "IPX", NULL, NULL, G_CALLBACK(gtk_ipx_hostlist_cb) },
+ { "/Statistics/EndpointList/JXTA", WIRESHARK_STOCK_ENDPOINTS, "JXTA", NULL, NULL, G_CALLBACK(gtk_jxta_hostlist_cb) },
+ { "/Statistics/EndpointList/NCP", WIRESHARK_STOCK_ENDPOINTS, "NCP", NULL, NULL, G_CALLBACK(gtk_ncp_hostlist_cb) },
+ { "/Statistics/EndpointList/RSVP", WIRESHARK_STOCK_ENDPOINTS, "RSVP", NULL, NULL, G_CALLBACK(gtk_rsvp_hostlist_cb) },
+ { "/Statistics/EndpointList/SCTP", WIRESHARK_STOCK_ENDPOINTS, "SCTP", NULL, NULL, G_CALLBACK(gtk_sctp_hostlist_cb) },
+ { "/Statistics/EndpointList/TCPIP", WIRESHARK_STOCK_ENDPOINTS, "TCP (IPv4 & IPv6)", NULL, NULL, G_CALLBACK(gtk_tcpip_hostlist_cb) },
+ { "/Statistics/EndpointList/TR", WIRESHARK_STOCK_ENDPOINTS, "Token Ring", NULL, NULL, G_CALLBACK(gtk_tr_hostlist_cb) },
+ { "/Statistics/EndpointList/UDPIP", WIRESHARK_STOCK_ENDPOINTS, "UDP (IPv4 & IPv6)", NULL, NULL, G_CALLBACK(gtk_udpip_hostlist_cb) },
+ { "/Statistics/EndpointList/USB", WIRESHARK_STOCK_ENDPOINTS, "USB", NULL, NULL, G_CALLBACK(gtk_usb_hostlist_cb) },
+ { "/Statistics/EndpointList/WLAN", WIRESHARK_STOCK_ENDPOINTS, "WLAN", NULL, NULL, G_CALLBACK(gtk_wlan_hostlist_cb) },
+
+ { "/Statistics/ServiceResponseTime", NULL, "Service _Response Time", NULL, NULL, NULL },
+ { "/Statistics/ServiceResponseTime/ONC-RPC", WIRESHARK_STOCK_TIME, "ONC-RPC...", NULL, NULL, G_CALLBACK(gtk_rpcstat_cb) },
+ { "/Statistics/ServiceResponseTime/AFP", WIRESHARK_STOCK_TIME, "AFP...", NULL, NULL, G_CALLBACK(afp_srt_stat_cb) },
+ { "/Statistics/ServiceResponseTime/Camel", WIRESHARK_STOCK_TIME, "Camel...", NULL, NULL, G_CALLBACK(camel_srt_cb) },
+ { "/Statistics/ServiceResponseTime/DCE-RPC", WIRESHARK_STOCK_TIME, "DCE-RPC...", NULL, NULL, G_CALLBACK(gtk_dcerpcstat_cb) },
+ { "/Statistics/ServiceResponseTime/Diameter", WIRESHARK_STOCK_TIME, "Diameter...", NULL, NULL, G_CALLBACK(diameter_srt_cb) },
+ { "/Statistics/ServiceResponseTime/FibreChannel", WIRESHARK_STOCK_TIME, "Fibre Channel...", NULL, NULL, G_CALLBACK(fc_srt_cb) },
+ { "/Statistics/ServiceResponseTime/GTP", WIRESHARK_STOCK_TIME, "GTP...", NULL, NULL, G_CALLBACK(gtp_srt_cb) },
+ { "/Statistics/ServiceResponseTime/H225", WIRESHARK_STOCK_TIME, "H225...", NULL, NULL, G_CALLBACK(h225_srt_cb) },
+ { "/Statistics/ServiceResponseTime/LDAP", WIRESHARK_STOCK_TIME, "LDAP...", NULL, NULL, G_CALLBACK(ldap_srt_cb) },
+ { "/Statistics/ServiceResponseTime/MEGACO", WIRESHARK_STOCK_TIME, "MEGACO...", NULL, NULL, G_CALLBACK(megaco_srt_cb) },
+ { "/Statistics/ServiceResponseTime/MGCP", WIRESHARK_STOCK_TIME, "MGCP...", NULL, NULL, G_CALLBACK(mgcp_srt_cb) },
+ { "/Statistics/ServiceResponseTime/NCP", WIRESHARK_STOCK_TIME, "NCP...", NULL, NULL, G_CALLBACK(ncp_srt_cb) },
+ { "/Statistics/ServiceResponseTime/RADIUS", WIRESHARK_STOCK_TIME, "RADIUS...", NULL, NULL, G_CALLBACK(radius_srt_cb) },
+ { "/Statistics/ServiceResponseTime/SCSI", WIRESHARK_STOCK_TIME, "SCSI...", NULL, NULL, G_CALLBACK(scsi_srt_cb) },
+ { "/Statistics/ServiceResponseTime/SMB", WIRESHARK_STOCK_TIME, "SMB...", NULL, NULL, G_CALLBACK(smb_srt_cb) },
+ { "/Statistics/ServiceResponseTime/SMB2", WIRESHARK_STOCK_TIME, "SMB2...", NULL, NULL, G_CALLBACK(smb2_srt_cb) },
+
+ { "/StatisticsMenu/ancp", NULL, "ANCP", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
+ { "/StatisticsMenu/BACnet", NULL, "BACnet", NULL, NULL, NULL },
+ { "/StatisticsMenu/BACnet/bacapp_instanceid", NULL, "Packets sorted by Instance ID", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
+ { "/StatisticsMenu/BACnet/bacapp_ip", NULL, "Packets sorted by IP", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
+ { "/StatisticsMenu/BACnet/bacapp_objectid", NULL, "Packets sorted by Object Type", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
+ { "/StatisticsMenu/BACnet/bacapp_service", NULL, "Packets sorted by Service", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
+ { "/StatisticsMenu/BOOTP-DHCP", NULL, "BOOTP-DHCP...", NULL, NULL, G_CALLBACK(bootp_dhcp_stat_cb) },
+
+ { "/StatisticsMenu/collectd", NULL, "Collectd...", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
+ { "/StatisticsMenu/compare", NULL, "Compare...", NULL, NULL, G_CALLBACK(gtk_comparestat_cb) },
+ { "/StatisticsMenu/FlowGraph", WIRESHARK_STOCK_FLOW_GRAPH, "Flo_w Graph...", NULL, NULL, G_CALLBACK(flow_graph_launch) },
+ { "/StatisticsMenu/HTTP", NULL, "HTTP", NULL, NULL, NULL },
+ { "/StatisticsMenu/HTTP/http", NULL, "Packet Counter", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
+ { "/StatisticsMenu/HTTP/http_req", NULL, "Requests", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
+ { "/StatisticsMenu/HTTP/http_srv", NULL, "Load Distribution", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
+
+ { "/StatisticsMenu/ip_hosts", NULL, "IP Addresses...", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
+ { "/StatisticsMenu/dests", NULL, "IP Destinations...", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
+ { "/StatisticsMenu/ptype", NULL, "IP Protocol Types..", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
+ { "/StatisticsMenu/ONC-RPC-Programs", NULL, "ONC-RPC Programs", NULL, NULL, G_CALLBACK(gtk_rpcprogs_cb) },
+ { "/StatisticsMenu/Sametime", NULL, "Sametime", NULL, NULL, NULL },
+ { "/StatisticsMenu/Sametime/sametime", NULL, "Messages", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
+ { "/StatisticsMenu/TCPStreamGraphMenu", NULL, "TCP StreamGraph", NULL, NULL, NULL },
+ { "/StatisticsMenu/TCPStreamGraphMenu/Time-Sequence-Graph-Stevens", NULL, "Time-Sequence Graph (Stevens)", NULL, NULL, G_CALLBACK(tcp_graph_cb) },
+ { "/StatisticsMenu/TCPStreamGraphMenu/Time-Sequence-Graph-tcptrace", NULL, "Time-Sequence Graph (tcptrace)", NULL, NULL, G_CALLBACK(tcp_graph_cb) },
+ { "/StatisticsMenu/TCPStreamGraphMenu/Throughput-Graph", NULL, "Throughput Graph", NULL, NULL, G_CALLBACK(tcp_graph_cb) },
+ { "/StatisticsMenu/TCPStreamGraphMenu/RTT-Graph", NULL, "Round Trip Time Graph", NULL, NULL, G_CALLBACK(tcp_graph_cb) },
+ { "/StatisticsMenu/TCPStreamGraphMenu/Window-Scaling-Graph", NULL, "Window Scaling Graph", NULL, NULL, G_CALLBACK(tcp_graph_cb) },
+
+ { "/StatisticsMenu/UDPMulticastStreams", NULL, "UDP Multicast Streams", NULL, NULL, G_CALLBACK(mcaststream_launch) },
+ { "/StatisticsMenu/WLANTraffic", NULL, "WLAN Traffic", NULL, NULL, G_CALLBACK(wlanstat_launch) },
+
+ { "/Statistics/Summary", GTK_STOCK_PROPERTIES, "_Summary", NULL, NULL, G_CALLBACK(summary_open_cb) },
+ { "/Statistics/ProtocolHierarchy", NULL, "_Protocol Hierarchy", NULL, NULL, G_CALLBACK(proto_hier_stats_cb) },
+ { "/Statistics/Conversations", WIRESHARK_STOCK_CONVERSATIONS, "Conversations", NULL, NULL, G_CALLBACK(init_conversation_notebook_cb) },
+ { "/Statistics/Endpoints", WIRESHARK_STOCK_ENDPOINTS, "Endpoints", NULL, NULL, G_CALLBACK(init_hostlist_notebook_cb) },
+ { "/Statistics/IOGraphs", WIRESHARK_STOCK_GRAPHS, "_IO Graph", NULL, NULL, G_CALLBACK(gui_iostat_cb) },
+ { "/Statistics/plen", NULL, "Packet Lengths...", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
+
+ { "/Telephony/ANSI", NULL, "_ANSI", NULL, NULL, NULL },
+ { "/Telephony/ANSI/BSMAP", NULL, "A-Interface BSMAP", NULL, NULL, G_CALLBACK(ansi_a_stat_gtk_bsmap_cb) },
+ { "/Telephony/ANSI/DTAP", NULL, "A-Interface DTAP", NULL, NULL, G_CALLBACK(ansi_a_stat_gtk_dtap_cb) },
+ { "/Telephony/ANSI/MAP-OP", NULL, "MAP Operation", NULL, NULL, G_CALLBACK(ansi_map_stat_gtk_cb) },
+
+ { "/Telephony/GSM", NULL, "_GSM", NULL, NULL, NULL },
+ { "/Telephony/GSM/CAMEL", NULL, "CAMEL Messages and Response Status", NULL, NULL, G_CALLBACK(camel_counter_cb) },
+ { "/Telephony/GSM/BSSMAP", NULL, "_GSM/A-Interface BSSMAP", NULL, NULL, G_CALLBACK(gsm_a_stat_gtk_bssmap_cb) },
+
+ { "/Telephony/GSM/DTAP", NULL, "_GSM/A-Interface DTAP", NULL, NULL, NULL },
+ { "/Telephony/GSM/DTAP/CC", NULL, "Call Control", NULL, NULL, G_CALLBACK(gsm_a_stat_gtk_dtap_cc_cb) },
+ { "/Telephony/GSM/DTAP/GMM", NULL, "GPRS Mobility Management", NULL, NULL, G_CALLBACK(gsm_a_stat_gtk_dtap_gmm_cb) },
+ { "/Telephony/GSM/DTAP/SM", NULL, "GPRS Session Management", NULL, NULL, G_CALLBACK(gsm_a_stat_gtk_dtap_sm_cb) },
+ { "/Telephony/GSM/DTAP/MM", NULL, "Mobility Management", NULL, NULL, G_CALLBACK(gsm_a_stat_gtk_dtap_mm_cb) },
+ { "/Telephony/GSM/DTAP/RR", NULL, "Radio Resource Management",NULL, NULL, G_CALLBACK(gsm_a_stat_gtk_dtap_rr_cb) },
+ { "/Telephony/GSM/DTAP/SMS", NULL, "Short Message Service", NULL, NULL, G_CALLBACK(gsm_a_stat_gtk_dtap_sms_cb) },
+ { "/Telephony/GSM/DTAP/TP", NULL, "Special Conformance Testing Functions", NULL, NULL, G_CALLBACK(gsm_a_stat_gtk_dtap_tp_cb) },
+ { "/Telephony/GSM/DTAP/SS", NULL, "Supplementary Services", NULL, NULL, G_CALLBACK(gsm_a_stat_gtk_dtap_ss_cb) },
+
+ { "/Telephony/GSM/SACCH", NULL, "_GSM/A-Interface SACCH", NULL, NULL, G_CALLBACK(gsm_a_stat_gtk_sacch_rr_cb) },
+ { "/Telephony/GSM/MAP-OP", NULL, "_GSM/MAP Operation", NULL, NULL, G_CALLBACK(gsm_map_stat_gtk_cb) },
+ { "/Telephony/GSM/MAPSummary", NULL, "MAP Summary", NULL, NULL, G_CALLBACK(gsm_map_stat_gtk_sum_cb) },
+
+ { "/Telephony/H225", NULL, "_H.225...", NULL, NULL, G_CALLBACK(h225_counter_cb) },
+
+ { "/Telephony/IAX2", NULL, "IA_X2", NULL, NULL, NULL },
+ { "/Telephony/IAX2/StreamAnalysis", NULL, "Stream Analysis...", NULL, NULL, G_CALLBACK(iax2_analysis_cb) },
+
+ { "/Telephony/isup_msg", NULL, "_ISUP Messages", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
+
+ { "/Telephony/LTE", NULL, "_LTE", NULL, NULL, NULL },
+ { "/Telephony/LTE/MAC", NULL, "_MAC...", NULL, NULL, G_CALLBACK(mac_lte_stat_cb) },
+ { "/Telephony/LTE/RLC", NULL, "_RLC...", NULL, NULL, G_CALLBACK(rlc_lte_stat_cb) },
+ { "/Telephony/MTP3", NULL, "_MTP3", NULL, NULL, NULL },
+ { "/Telephony/MTP3/MSUs", NULL, "MSUs", NULL, NULL, G_CALLBACK(mtp3_stat_gtk_cb) },
+ { "/Telephony/MTP3/MSUSummary", NULL, "MSU Summary", NULL, NULL, G_CALLBACK(mtp3_sum_gtk_sum_cb) },
+ { "/Telephony/RTP", NULL, "_RTP", NULL, NULL, NULL },
+ { "/Telephony/RTP/StreamAnalysis", NULL, "Stream Analysis...", NULL, NULL, G_CALLBACK(rtp_analysis_cb) },
+ { "/Telephony/RTP/ShowAllStreams", NULL, "Show All Streams", NULL, NULL, G_CALLBACK(rtpstream_launch) },
+ { "/Telephony/RTSP", NULL, "RTSP", NULL, NULL, NULL },
+ { "/Telephony/RTSP/rtsp", NULL, "Packet Counter", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
+ { "/Telephony/SCTP", NULL, "S_CTP", NULL, NULL, NULL },
+ { "/Telephony/SCTP/AnalysethisAssociation", NULL, "Analyse this Association", NULL, NULL, G_CALLBACK(sctp_analyse_start) },
+ { "/Telephony/SCTP/ShowAllAssociations", NULL, "Show All Associations...", NULL, NULL, G_CALLBACK(sctp_stat_start) },
+ { "/Telephony/SCTP/ChunkCounter", NULL, "Chunk Counter", NULL, NULL, G_CALLBACK(sctp_chunk_counter_cb) },
+ { "/Telephony/SIP", NULL, "_SIP...", NULL, NULL, G_CALLBACK(sipstat_cb) },
+ { "/Telephony/smpp_commands", NULL, "SM_PPOperations", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
+ { "/Telephony/ucp_messages", NULL, "_UCP Messages", NULL, NULL, G_CALLBACK(gtk_stats_tree_cb) },
+ { "/Telephony/VoIPCalls", WIRESHARK_STOCK_TELEPHONE, "_VoIP Calls", NULL, NULL, G_CALLBACK(voip_calls_launch) },
+ { "/Telephony/WSP", NULL, "_WAP-WSP...", NULL, NULL, G_CALLBACK(wsp_stat_cb) },
+
+ { "/Tools/FirewallACLRules", NULL, "Firewall ACL Rules", NULL, NULL, G_CALLBACK(firewall_rule_cb) },
+
+ { "/Internals/Dissectortables", NULL, "_Dissector tables", NULL, NULL, G_CALLBACK(dissector_tables_dlg_cb) },
+ { "/Internals/SupportedProtocols", NULL, "_Supported Protocols (slow!)", NULL, NULL, G_CALLBACK(supported_cb) },
+
+ { "/Help/Contents", GTK_STOCK_HELP, "_Contents", "F1", NULL, G_CALLBACK(help_menu_cont_cb) },
+ { "/Help/ManualPages", NULL, "ManualPages", NULL, NULL, NULL },
+ { "/Help/ManualPages/Wireshark", NULL, "Wireshark", NULL, NULL, G_CALLBACK(help_menu_wireshark_cb) },
+ { "/Help/ManualPages/WiresharkFilter", NULL, "Wireshark Filter", NULL, NULL, G_CALLBACK(help_menu_wireshark_flt_cb) },
+ { "/Help/ManualPages/TShark", NULL, "Wireshark", NULL, NULL, G_CALLBACK(help_menu_Tshark_cb) },
+ { "/Help/ManualPages/RawShark", NULL, "RawShark", NULL, NULL, G_CALLBACK(help_menu_RawShark_cb) },
+ { "/Help/ManualPages/Dumpcap", NULL, "Dumpcap", NULL, NULL, G_CALLBACK(help_menu_Dumpcap_cb) },
+ { "/Help/ManualPages/Mergecap", NULL, "Mergecap", NULL, NULL, G_CALLBACK(help_menu_Mergecap_cb) },
+ { "/Help/ManualPages/Editcap", NULL, "Editcap", NULL, NULL, G_CALLBACK(help_menu_Editcap_cb) },
+ { "/Help/ManualPages/Text2pcap", NULL, "Text2pcap", NULL, NULL, G_CALLBACK(help_menu_Text2pcap_cb) },
+
+ { "/Help/Website", GTK_STOCK_HOME, "Website", NULL, NULL, G_CALLBACK(help_menu_Website_cb) },
+ { "/Help/FAQs", NULL, "FAQ's", NULL, NULL, G_CALLBACK(help_menu_faq_cb) },
+ { "/Help/Downloads", NULL, "Downloads", NULL, NULL, G_CALLBACK(help_menu_Downloads_cb) },
+ { "/Help/Wiki", WIRESHARK_STOCK_WIKI, "Wiki", NULL, NULL, G_CALLBACK(help_menu_Wiki_cb) },
+ { "/Help/SampleCaptures", NULL, "Sample Captures", NULL, NULL, G_CALLBACK(help_menu_SampleCaptures_cb) },
+ { "/Help/AboutWireshark", WIRESHARK_STOCK_ABOUT, "_About Wireshark", NULL, NULL, G_CALLBACK(about_wireshark_cb) },
+};
+
+static const GtkToggleActionEntry main_menu_bar_toggle_action_entries[] =
+{
+ /* name, stock id, label, accel, tooltip, callback, is_active */
+ {"/View/MainToolbar", NULL, "_Main Toolbar", NULL, NULL, G_CALLBACK(main_toolbar_show_hide_cb), TRUE},
+ {"/View/FilterToolbar", NULL, "_FilterToolbar", NULL, NULL, G_CALLBACK(filter_toolbar_show_hide_cb), TRUE},
+ {"/View/WirelessToolbar", NULL, "_WirelessToolbar", NULL, NULL, G_CALLBACK(wireless_toolbar_show_hide_cb), FALSE},
+ {"/View/Statusbar", NULL, "_Statusbar", NULL, NULL, G_CALLBACK(status_bar_show_hide_cb), TRUE},
+ {"/View/PacketList", NULL, "Packet _List", NULL, NULL, G_CALLBACK(packet_list_show_hide_cb), TRUE},
+ {"/View/PacketDetails", NULL, "Packet _Details", NULL, NULL, G_CALLBACK(packet_details_show_hide_cb), TRUE},
+ {"/View/PacketBytes", NULL, "Packet _Bytes", NULL, NULL, G_CALLBACK(packet_bytes_show_hide_cb), TRUE},
+ {"/View/TimeDisplayFormat/DisplaySecondsWithHoursAndMinutes", NULL, "Display Seconds with hours and minutes", NULL, NULL, G_CALLBACK(timestamp_seconds_time_cb), FALSE},
+ {"/View/NameResolution/ResolveName", NULL, "_Resolve Name", NULL, NULL, G_CALLBACK(resolve_name_cb), FALSE},
+ {"/View/NameResolution/EnableforMACLayer", NULL, "Enable for _MAC Layer", NULL, NULL, G_CALLBACK(view_menu_en_for_MAC_cb), TRUE},
+ {"/View/NameResolution/EnableforNetworkLayer", NULL, "Enable for _Network Layer", NULL, NULL, G_CALLBACK(view_menu_en_for_network_cb), TRUE },
+ {"/View/NameResolution/EnableforTransportLayer", NULL, "Enable for _Transport Layer", NULL, NULL, G_CALLBACK(view_menu_en_for_transport_cb), TRUE },
+ {"/View/ColorizePacketList", NULL, "Colorize Packet List", NULL, NULL, G_CALLBACK(view_menu_colorize_pkt_lst_cb), TRUE },
+ {"/View/AutoScrollinLiveCapture", NULL, "Auto Scroll in Li_ve Capture", NULL, NULL, G_CALLBACK(view_menu_auto_scroll_live_cb), TRUE },
+};
+
+static const GtkRadioActionEntry main_menu_bar_radio_view_time_entries [] =
+{
+ /* name, stock id, label, accel, tooltip, value */
+ { "/View/TimeDisplayFormat/DateandTimeofDay", NULL, "Date and Time of Day: 1970-01-01 01:02:03.123456", "<alt><control>1", NULL, TS_ABSOLUTE_WITH_DATE },
+ { "/View/TimeDisplayFormat/TimeofDay", NULL, "Time of Day: 01:02:03.123456", "<alt><control>2", NULL, TS_ABSOLUTE },
+ { "/View/TimeDisplayFormat/SecondsSinceEpoch", NULL, "Seconds Since Epoch (1970-01-01): 1234567890.123456", "<alt><control>3", NULL, TS_EPOCH },
+ { "/View/TimeDisplayFormat/SecondsSinceBeginningofCapture", NULL, "Seconds Since Beginning of Capture: 123.123456", "<alt><control>4", NULL, TS_RELATIVE },
+ { "/View/TimeDisplayFormat/SecondsSincePreviousCapturedPacket", NULL, "Seconds Since Previous Captured Packet: 1.123456", "<alt><control>5", NULL, TS_DELTA },
+ { "/View/TimeDisplayFormat/SecondsSincePreviousDisplayedPacket",NULL, "Seconds Since Previous Displayed Packet: 1.123456", "<alt><control>6", NULL, TS_DELTA_DIS },
+ { "/View/TimeDisplayFormat/UTCDateandTimeofDay", NULL, "UTC Date and Time of Day: 1970-01-01 01:02:03.123456", "<alt><control>7", NULL, TS_UTC_WITH_DATE },
+ { "/View/TimeDisplayFormat/UTCTimeofDay", NULL, "UTC Time of Day: 01:02:03.123456", "<alt><control>7", NULL, TS_UTC },
+};
+
+static const GtkRadioActionEntry main_menu_bar_radio_view_time_fileformat_prec_entries [] =
+{
+ /* name, stock id, label, accel, tooltip, value */
+ { "/View/TimeDisplayFormat/FileFormatPrecision-Automatic", NULL, "Automatic (File Format Precision)", NULL, NULL, TS_PREC_AUTO },
+ { "/View/TimeDisplayFormat/FileFormatPrecision-Seconds", NULL, "Seconds: 0", NULL, NULL, TS_PREC_FIXED_SEC },
+ { "/View/TimeDisplayFormat/FileFormatPrecision-Deciseconds", NULL, "Deciseconds: 0.1", NULL, NULL, TS_PREC_FIXED_DSEC },
+ { "/View/TimeDisplayFormat/FileFormatPrecision-Centiseconds", NULL, "Centiseconds: 0.12", NULL, NULL, TS_PREC_FIXED_CSEC },
+ { "/View/TimeDisplayFormat/FileFormatPrecision-Milliseconds", NULL, "Milliseconds: 0.123", NULL, NULL, TS_PREC_FIXED_MSEC },
+ { "/View/TimeDisplayFormat/FileFormatPrecision-Microseconds", NULL, "Microseconds: 0.123456", NULL, NULL, TS_PREC_FIXED_USEC },
+ { "/View/TimeDisplayFormat/FileFormatPrecision-Nanoseconds", NULL, "Nanoseconds: 0.123456789", NULL, NULL, TS_PREC_FIXED_NSEC },
+};
+
+
+static void
+select_bytes_view_cb (GtkRadioAction *action, GtkRadioAction *current _U_, gpointer user_data _U_)
+{
+ gint value;
+
+ value = gtk_radio_action_get_current_value (action);
+ /* Fix me */
+ select_bytes_view( NULL, NULL, value);
+}
+
+static void
+sort_ascending_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending");
+ new_packet_list_column_menu_cb( widget , user_data, COLUMN_SELECTED_SORT_ASCENDING);
+}
+
+static void
+sort_descending_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending");
+ new_packet_list_column_menu_cb( widget , user_data, COLUMN_SELECTED_SORT_DESCENDING);
+}
+
+static void
+no_sorting_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting");
+ new_packet_list_column_menu_cb( widget , user_data, COLUMN_SELECTED_SORT_NONE);
+}
+
+static void
+packet_list_heading_show_resolved_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_packet_list_heading, "/PacketListHeadingPopup/ShowResolved");
+
+ new_packet_list_column_menu_cb( widget , user_data, COLUMN_SELECTED_TOGGLE_RESOLVED);
+}
+
+static void
+packet_list_heading_align_left_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_packet_list_heading, "/PacketListHeadingPopup/AlignLeft");
+ new_packet_list_column_menu_cb( widget , user_data, COLUMN_SELECTED_ALIGN_LEFT);
+}
+
+static void
+packet_list_heading_align_center_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_packet_list_heading, "/PacketListHeadingPopup/AlignCenter");
+ new_packet_list_column_menu_cb( widget , user_data, COLUMN_SELECTED_ALIGN_CENTER);
+}
+
+static void
+packet_list_heading_align_right_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_packet_list_heading, "/PacketListHeadingPopup/AlignRight");
+ new_packet_list_column_menu_cb( widget , user_data, COLUMN_SELECTED_ALIGN_RIGHT);
+}
+
+static void
+packet_list_heading_col_pref_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_packet_list_heading, "/PacketListHeadingPopup/ColumnPreferences");
+ prefs_page_cb( widget , user_data, PREFS_PAGE_COLUMNS);
+}
+
+static void
+packet_list_heading_resize_col_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_packet_list_heading, "/PacketListHeadingPopup/ResizeColumn");
+ new_packet_list_column_menu_cb( widget , user_data, COLUMN_SELECTED_RESIZE);
+}
+
+static void
+packet_list_heading_change_col_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_packet_list_heading, "/PacketListHeadingPopup/EditColumnDetails");
+ new_packet_list_column_menu_cb( widget , user_data, COLUMN_SELECTED_CHANGE);
+}
+
+static void
+packet_list_heading_activate_all_columns_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ new_packet_list_set_all_columns_visible ();
+}
+
+static void
+packet_list_heading_hide_col_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_packet_list_heading, "/PacketListHeadingPopup/HideColumn");
+ new_packet_list_column_menu_cb( widget , user_data, COLUMN_SELECTED_HIDE);
+}
+
+static void
+packet_list_heading_remove_col_cb(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_packet_list_heading, "/PacketListHeadingPopup/RemoveColumn");
+ new_packet_list_column_menu_cb( widget , user_data, COLUMN_SELECTED_REMOVE);
+}
+
+static void
+packet_list_menu_set_ref_time_cb(GtkAction *action _U_, gpointer user_data)
+{
+ reftime_frame_cb( NULL /* widget _U_ */ , user_data, REFTIME_TOGGLE);
+}
+
+
+static void
+packet_list_menu_apply_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ match_selected_plist_cb( NULL /* widget _U_ */, user_data, MATCH_SELECTED_REPLACE|MATCH_SELECTED_APPLY_NOW);
+}
+
+static void
+packet_list_menu_apply_not_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ match_selected_plist_cb( NULL /* widget _U_ */ , user_data, MATCH_SELECTED_NOT|MATCH_SELECTED_APPLY_NOW);
+}
+
+static void
+packet_list_menu_apply_and_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ match_selected_plist_cb( NULL /* widget _U_ */ , user_data, MATCH_SELECTED_AND|MATCH_SELECTED_APPLY_NOW);
+}
+
+static void
+packet_list_menu_apply_or_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ match_selected_plist_cb( NULL /* widget _U_ */ , user_data, MATCH_SELECTED_OR|MATCH_SELECTED_APPLY_NOW);
+}
+
+static void
+packet_list_menu_apply_and_not_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ match_selected_plist_cb( NULL /* widget _U_ */ , user_data, MATCH_SELECTED_AND_NOT|MATCH_SELECTED_APPLY_NOW);
+}
+
+static void
+packet_list_menu_apply_or_not_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ match_selected_plist_cb( NULL /* widget _U_ */ , user_data,MATCH_SELECTED_OR_NOT|MATCH_SELECTED_APPLY_NOW);
+}
+/* Prepare a filter */
+static void
+packet_list_menu_prepare_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ match_selected_plist_cb( NULL /* widget _U_ */ , user_data, MATCH_SELECTED_REPLACE);
+}
+
+static void
+packet_list_menu_prepare_not_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ match_selected_plist_cb( NULL /* widget _U_ */ , user_data, MATCH_SELECTED_NOT);
+}
+
+static void
+packet_list_menu_prepare_and_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ match_selected_plist_cb( NULL /* widget _U_ */ , user_data, MATCH_SELECTED_AND);
+}
+
+static void
+packet_list_menu_prepare_or_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ match_selected_plist_cb( NULL /* widget _U_ */ , user_data, MATCH_SELECTED_OR);
+}
+
+static void
+packet_list_menu_prepare_and_not_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ match_selected_plist_cb( NULL /* widget _U_ */ , user_data, MATCH_SELECTED_AND_NOT);
+}
+
+static void
+packet_list_menu_prepare_or_not_selected_cb(GtkAction *action _U_, gpointer user_data)
+{
+ match_selected_plist_cb( NULL /* widget _U_ */ , user_data, MATCH_SELECTED_OR_NOT);
+}
+
+static void
+packet_list_menu_conversation_ethernet_cb(GtkAction *action, gpointer user_data)
+{
+ conversation_cb( action, user_data, CONV_ETHER);
+}
+
+static void
+packet_list_menu_conversation_ip_cb(GtkAction *action _U_, gpointer user_data)
+{
+ conversation_cb( action, user_data, CONV_IP);
+}
+
+static void
+packet_list_menu_conversation_tcp_cb(GtkAction *action _U_, gpointer user_data)
+{
+ conversation_cb( action, user_data, CONV_TCP);
+}
+
+static void
+packet_list_menu_conversation_udp_cb(GtkAction *action _U_, gpointer user_data)
+{
+ conversation_cb( action, user_data, CONV_UDP);
+}
+
+static void
+packet_list_menu_conversation_pn_cba_cb(GtkAction *action _U_, gpointer user_data)
+{
+ conversation_cb( action, user_data, CONV_CBA);
+}
+
+/* Ethernet */
+
+static void
+packet_list_menu_color_conv_ethernet_color1_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_ETHER+1*256);
+}
+
+static void
+packet_list_menu_color_conv_ethernet_color2_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_ETHER+2*256);
+}
+
+static void
+packet_list_menu_color_conv_ethernet_color3_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_ETHER+3*256);
+}
+
+static void
+packet_list_menu_color_conv_ethernet_color4_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_ETHER+4*256);
+}
+
+static void
+packet_list_menu_color_conv_ethernet_color5_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_ETHER+5*256);
+}
+
+static void
+packet_list_menu_color_conv_ethernet_color6_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_ETHER+6*256);
+}
+
+static void
+packet_list_menu_color_conv_ethernet_color7_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_ETHER+7*256);
+}
+
+static void
+packet_list_menu_color_conv_ethernet_color8_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_ETHER+8*256);
+}
+
+static void
+packet_list_menu_color_conv_ethernet_color9_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_ETHER+9*256);
+}
+
+static void
+packet_list_menu_color_conv_ethernet_color10_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_ETHER+10*256);
+}
+
+static void
+packet_list_menu_color_conv_ethernet_new_rule_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_ETHER);
+}
+
+/* IP */
+
+static void
+packet_list_menu_color_conv_ip_color1_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_IP+1*256);
+}
+
+static void
+packet_list_menu_color_conv_ip_color2_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_IP+2*256);
+}
+
+static void
+packet_list_menu_color_conv_ip_color3_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_IP+3*256);
+}
+
+static void
+packet_list_menu_color_conv_ip_color4_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_IP+4*256);
+}
+
+static void
+packet_list_menu_color_conv_ip_color5_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_IP+5*256);
+}
+
+static void
+packet_list_menu_color_conv_ip_color6_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_IP+6*256);
+}
+
+static void
+packet_list_menu_color_conv_ip_color7_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_IP+7*256);
+}
+
+static void
+packet_list_menu_color_conv_ip_color8_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_IP+8*256);
+}
+
+static void
+packet_list_menu_color_conv_ip_color9_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_IP+9*256);
+}
+
+static void
+packet_list_menu_color_conv_ip_color10_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_IP+10*256);
+}
+
+static void
+packet_list_menu_color_conv_ip_new_rule_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_TCP);
+}
+
+/* TCP */
+
+static void
+packet_list_menu_color_conv_tcp_color1_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_TCP+1*256);
+}
+
+static void
+packet_list_menu_color_conv_tcp_color2_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_TCP+2*256);
+}
+
+static void
+packet_list_menu_color_conv_tcp_color3_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_TCP+3*256);
+}
+
+static void
+packet_list_menu_color_conv_tcp_color4_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_TCP+4*256);
+}
+
+static void
+packet_list_menu_color_conv_tcp_color5_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_TCP+5*256);
+}
+
+static void
+packet_list_menu_color_conv_tcp_color6_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_TCP+6*256);
+}
+
+static void
+packet_list_menu_color_conv_tcp_color7_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_TCP+7*256);
+}
+
+static void
+packet_list_menu_color_conv_tcp_color8_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_TCP+8*256);
+}
+
+static void
+packet_list_menu_color_conv_tcp_color9_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_TCP+9*256);
+}
+
+static void
+packet_list_menu_color_conv_tcp_color10_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_TCP+10*256);
+}
+
+static void
+packet_list_menu_color_conv_tcp_new_rule_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_TCP);
+}
+
+/* UDP */
+
+static void
+packet_list_menu_color_conv_udp_color1_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_UDP+1*256);
+}
+
+static void
+packet_list_menu_color_conv_udp_color2_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_UDP+2*256);
+}
+
+static void
+packet_list_menu_color_conv_udp_color3_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_UDP+3*256);
+}
+
+static void
+packet_list_menu_color_conv_udp_color4_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_UDP+4*256);
+}
+
+static void
+packet_list_menu_color_conv_udp_color5_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_UDP+5*256);
+}
+
+static void
+packet_list_menu_color_conv_udp_color6_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_UDP+6*256);
+}
+
+static void
+packet_list_menu_color_conv_udp_color7_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_UDP+7*256);
+}
+
+static void
+packet_list_menu_color_conv_udp_color8_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_UDP+8*256);
+}
+
+static void
+packet_list_menu_color_conv_udp_color9_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_UDP+9*256);
+}
+
+static void
+packet_list_menu_color_conv_udp_color10_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_UDP+10*256);
+}
+
+static void
+packet_list_menu_color_conv_udp_new_rule_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_UDP);
+}
+
+/* CONV_CBA */
+
+static void
+packet_list_menu_color_conv_cba_color1_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_CBA+1*256);
+}
+
+static void
+packet_list_menu_color_conv_cba_color2_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_CBA+2*256);
+}
+
+static void
+packet_list_menu_color_conv_cba_color3_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_CBA+3*256);
+}
+
+static void
+packet_list_menu_color_conv_cba_color4_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_CBA+4*256);
+}
+
+static void
+packet_list_menu_color_conv_cba_color5_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_CBA+5*256);
+}
+
+static void
+packet_list_menu_color_conv_cba_color6_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_CBA+6*256);
+}
+
+static void
+packet_list_menu_color_conv_cba_color7_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_CBA+7*256);
+}
+
+static void
+packet_list_menu_color_conv_cba_color8_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_CBA+8*256);
+}
+
+static void
+packet_list_menu_color_conv_cba_color9_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_CBA+9*256);
+}
+
+static void
+packet_list_menu_color_conv_cba_color10_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_CBA+10*256);
+}
+
+static void
+packet_list_menu_color_conv_cba_new_rule_cb(GtkAction *action, gpointer user_data)
+{
+ colorize_conversation_cb(action, user_data, CONV_CBA);
+}
+
+static void
+packet_list_menu_copy_sum_txt(GtkAction *action _U_, gpointer user_data)
+{
+ new_packet_list_copy_summary_cb(user_data, CS_TEXT);
+}
+
+static void
+packet_list_menu_copy_sum_csv(GtkAction *action _U_, gpointer user_data)
+{
+ new_packet_list_copy_summary_cb(user_data, CS_CSV);
+}
+
+static void
+packet_list_menu_copy_as_flt(GtkAction *action _U_, gpointer user_data)
+{
+ match_selected_plist_cb( NULL /* widget _U_ */ , user_data, MATCH_SELECTED_REPLACE|MATCH_SELECTED_COPY_ONLY);
+}
+
+static void
+packet_list_menu_copy_bytes_oht_cb(GtkAction *action _U_, gpointer user_data)
+{
+ copy_hex_cb( NULL /* widget _U_ */ , user_data, CD_ALLINFO | CD_FLAGS_SELECTEDONLY);
+}
+
+static void
+packet_list_menu_copy_bytes_oh_cb(GtkAction *action _U_, gpointer user_data)
+{
+ copy_hex_cb( NULL /* widget _U_ */ , user_data, CD_HEXCOLUMNS | CD_FLAGS_SELECTEDONLY);
+}
+
+static void
+packet_list_menu_copy_bytes_text_cb(GtkAction *action _U_, gpointer user_data)
+{
+ copy_hex_cb( NULL /* widget _U_ */ , user_data, CD_TEXTONLY | CD_FLAGS_SELECTEDONLY);
+}
+
+static void
+packet_list_menu_copy_bytes_hex_strm_cb(GtkAction *action _U_, gpointer user_data)
+{
+ copy_hex_cb( NULL /* widget _U_ */ , user_data, CD_HEX | CD_FLAGS_SELECTEDONLY);
+}
+
+static void
+packet_list_menu_copy_bytes_bin_strm_cb(GtkAction *action _U_, gpointer user_data)
+{
+ copy_hex_cb( NULL /* widget _U_ */ , user_data, CD_BINARY | CD_FLAGS_SELECTEDONLY);
+}
+
+/* tree */
+
+static void
+tree_view_menu_color_with_flt_color1_cb(GtkAction *action _U_, gpointer user_data)
+{
+ colorize_selected_ptree_cb( NULL /* widget _U_ */ , user_data, 1);
+}
+
+static void
+tree_view_menu_color_with_flt_color2_cb(GtkAction *action _U_, gpointer user_data)
+{
+ colorize_selected_ptree_cb( NULL /* widget _U_ */ , user_data, 2);
+}
+
+static void
+tree_view_menu_color_with_flt_color3_cb(GtkAction *action _U_, gpointer user_data)
+{
+ colorize_selected_ptree_cb( NULL /* widget _U_ */ , user_data, 3);
+}
+
+static void
+tree_view_menu_color_with_flt_color4_cb(GtkAction *action _U_, gpointer user_data)
+{
+ colorize_selected_ptree_cb( NULL /* widget _U_ */ , user_data, 4);
+}
+
+static void
+tree_view_menu_color_with_flt_color5_cb(GtkAction *action _U_, gpointer user_data)
+{
+ colorize_selected_ptree_cb( NULL /* widget _U_ */ , user_data, 5);
+}
+
+static void
+tree_view_menu_color_with_flt_color6_cb(GtkAction *action _U_, gpointer user_data)
+{
+ colorize_selected_ptree_cb( NULL /* widget _U_ */ , user_data, 6);
+}
+
+static void
+tree_view_menu_color_with_flt_color7_cb(GtkAction *action _U_, gpointer user_data)
+{
+ colorize_selected_ptree_cb( NULL /* widget _U_ */ , user_data, 7);
+}
+
+static void
+tree_view_menu_color_with_flt_color8_cb(GtkAction *action _U_, gpointer user_data)
+{
+ colorize_selected_ptree_cb( NULL /* widget _U_ */ , user_data, 8);
+}
+
+static void
+tree_view_menu_color_with_flt_color9_cb(GtkAction *action _U_, gpointer user_data)
+{
+ colorize_selected_ptree_cb( NULL /* widget _U_ */ , user_data, 9);
+}
+
+static void
+tree_view_menu_color_with_flt_color10_cb(GtkAction *action _U_, gpointer user_data)
+{
+ colorize_selected_ptree_cb( NULL /* widget _U_ */ , user_data, 10);
+}
+
+static void
+tree_view_menu_color_with_flt_new_rule_cb(GtkAction *action _U_, gpointer user_data)
+{
+ colorize_selected_ptree_cb( NULL /* widget _U_ */ , user_data, 0);
+}
+
+
+static void
+tree_view_menu_copy_desc(GtkAction *action _U_, gpointer user_data)
+{
+ copy_selected_plist_cb( NULL /* widget _U_ */ , user_data, COPY_SELECTED_DESCRIPTION);
+}
+
+static void
+tree_view_menu_copy_field(GtkAction *action _U_, gpointer user_data)
+{
+ copy_selected_plist_cb( NULL /* widget _U_ */ , user_data, COPY_SELECTED_FIELDNAME);
+}
+
+static void
+tree_view_menu_copy_value(GtkAction *action _U_, gpointer user_data)
+{
+ copy_selected_plist_cb( NULL /* widget _U_ */ , user_data, COPY_SELECTED_VALUE);
+}
+
+static void
+tree_view_menu_copy_as_flt(GtkAction *action _U_, gpointer user_data)
+{
+ GtkWidget *widget = gtk_ui_manager_get_widget(ui_manager_tree_view_menu, "/TreeViewPopup/Copy/AsFilter");
+ match_selected_ptree_cb( widget , user_data, MATCH_SELECTED_REPLACE|MATCH_SELECTED_COPY_ONLY);
+}
+
+static const char *ui_desc_packet_list_heading_menu_popup =
+"<ui>\n"
+" <popup name='PacketListHeadingPopup' action='PopupAction'>\n"
+" <menuitem name='SortAscending' action='/Sort Ascending'/>\n"
+" <menuitem name='SortDescending' action='/Sort Descending'/>\n"
+" <menuitem name='NoSorting' action='/No Sorting'/>\n"
+" <separator/>\n"
+" <menuitem name='ShowResolved' action='/Show Resolved'/>\n"
+" <separator/>\n"
+" <menuitem name='AlignLeft' action='/Align Left'/>\n"
+" <menuitem name='AlignCenter' action='/Align Center'/>\n"
+" <menuitem name='AlignRight' action='/Align Right'/>\n"
+" <separator/>\n"
+" <menuitem name='ColumnPreferences' action='/Column Preferences'/>\n"
+" <menuitem name='EditColumnDetails' action='/Edit Column Details'/>\n"
+" <menuitem name='ResizeColumn' action='/Resize Column'/>\n"
+" <separator/>\n"
+" <menu name='DisplayedColumns' action='/Displayed Columns'>\n"
+" <menuitem name='Display All' action='/Displayed Columns/Display All'/>\n"
+" </menu>\n"
+" <menuitem name='HideColumn' action='/Hide Column'/>\n"
+" <menuitem name='RemoveColumn' action='/Remove Column'/>\n"
+" </popup>\n"
+"</ui>\n";
+
+static const GtkActionEntry packet_list_heading_menu_popup_action_entries[] = {
+ { "/Sort Ascending", GTK_STOCK_SORT_ASCENDING, "Sort Ascending", NULL, NULL, G_CALLBACK(sort_ascending_cb) },
+ { "/Sort Descending", GTK_STOCK_SORT_DESCENDING, "Sort Descending", NULL, NULL, G_CALLBACK(sort_descending_cb) },
+ { "/No Sorting", NULL, "No Sorting", NULL, NULL, G_CALLBACK(no_sorting_cb) },
+ { "/Align Left", GTK_STOCK_JUSTIFY_LEFT, "Align Left", NULL, NULL, G_CALLBACK(packet_list_heading_align_left_cb) },
+ { "/Align Center", GTK_STOCK_JUSTIFY_CENTER, "Align Center", NULL, NULL, G_CALLBACK(packet_list_heading_align_center_cb) },
+ { "/Align Right", GTK_STOCK_JUSTIFY_RIGHT, "Align Right", NULL, NULL, G_CALLBACK(packet_list_heading_align_right_cb) },
+ { "/Column Preferences", GTK_STOCK_PREFERENCES, "Column Preferences...", NULL, NULL, G_CALLBACK(packet_list_heading_col_pref_cb) },
+ { "/Edit Column Details", WIRESHARK_STOCK_EDIT, "Edit Column Details...", NULL, NULL, G_CALLBACK(packet_list_heading_change_col_cb) },
+ { "/Resize Column", WIRESHARK_STOCK_RESIZE_COLUMNS, "Resize Column", NULL, NULL, G_CALLBACK(packet_list_heading_resize_col_cb) },
+ { "/Displayed Columns", NULL, "Displayed Columns", NULL, NULL, NULL },
+ { "/Displayed Columns/Display All", NULL, "Display All", NULL, NULL, G_CALLBACK(packet_list_heading_activate_all_columns_cb) },
+ { "/Hide Column", NULL, "Hide Column", NULL, NULL, G_CALLBACK(packet_list_heading_hide_col_cb) },
+ { "/Remove Column", GTK_STOCK_DELETE, "Remove Column", NULL, NULL, G_CALLBACK(packet_list_heading_remove_col_cb) },
+};
+
+static const GtkToggleActionEntry packet_list_heading_menu_toggle_action_entries[] =
+{
+ /* name, stock id, label, accel, tooltip, callback, is_active */
+ {"/Show Resolved", NULL, "Show Resolved", NULL, NULL, G_CALLBACK(packet_list_heading_show_resolved_cb), FALSE},
+};
+
+static const char *ui_desc_packet_list_menu_popup =
+"<ui>\n"
+" <popup name='PacketListMenuPopup' action='PopupAction'>\n"
+" <menuitem name='MarkPacket' action='/MarkPacket'/>\n"
+" <menuitem name='IgnorePacket' action='/IgnorePacket'/>\n"
+" <menuitem name='SetTimeReference' action='/Set Time Reference'/>\n"
+" <menuitem name='TimeShift' action='/TimeShift'/>\n"
+" <separator/>\n"
+" <menuitem name='ManuallyResolveAddress' action='/ManuallyResolveAddress'/>\n"
+" <separator/>\n"
+" <menu name= 'ApplyAsFilter' action='/Apply as Filter'>\n"
+" <menuitem name='Selected' action='/Apply as Filter/Selected'/>\n"
+" <menuitem name='NotSelected' action='/Apply as Filter/Not Selected'/>\n"
+" <menuitem name='AndSelected' action='/Apply as Filter/AndSelected'/>\n"
+" <menuitem name='OrSelected' action='/Apply as Filter/OrSelected'/>\n"
+" <menuitem name='AndNotSelected' action='/Apply as Filter/AndNotSelected'/>\n"
+" <menuitem name='OrNotSelected' action='/Apply as Filter/OrNotSelected'/>\n"
+" </menu>\n"
+" <menu name= 'PrepareaFilter' action='/Prepare a Filter'>\n"
+" <menuitem name='Selected' action='/Prepare a Filter/Selected'/>\n"
+" <menuitem name='NotSelected' action='/Prepare a Filter/Not Selected'/>\n"
+" <menuitem name='AndSelected' action='/Prepare a Filter/AndSelected'/>\n"
+" <menuitem name='OrSelected' action='/Prepare a Filter/OrSelected'/>\n"
+" <menuitem name='AndNotSelected' action='/Prepare a Filter/AndNotSelected'/>\n"
+" <menuitem name='OrNotSelected' action='/Prepare a Filter/OrNotSelected'/>\n"
+" </menu>\n"
+" <menu name= 'ConversationFilter' action='/Conversation Filter'>\n"
+" <menuitem name='Ethernet' action='/Conversation Filter/Ethernet'/>\n"
+" <menuitem name='IP' action='/Conversation Filter/IP'/>\n"
+" <menuitem name='TCP' action='/Conversation Filter/TCP'/>\n"
+" <menuitem name='UDP' action='/Conversation Filter/UDP'/>\n"
+" <menuitem name='PN-CBA' action='/Conversation Filter/PN-CBA'/>\n"
+" </menu>\n"
+" <menu name= 'ColorizeConversation' action='/Colorize Conversation'>\n"
+" <menu name= 'Ethernet' action='/Colorize Conversation/Ethernet'>\n"
+" <menuitem name='Color1' action='/Colorize Conversation/Ethernet/Color 1'/>\n"
+" <menuitem name='Color2' action='/Colorize Conversation/Ethernet/Color 2'/>\n"
+" <menuitem name='Color3' action='/Colorize Conversation/Ethernet/Color 3'/>\n"
+" <menuitem name='Color4' action='/Colorize Conversation/Ethernet/Color 4'/>\n"
+" <menuitem name='Color5' action='/Colorize Conversation/Ethernet/Color 5'/>\n"
+" <menuitem name='Color6' action='/Colorize Conversation/Ethernet/Color 6'/>\n"
+" <menuitem name='Color7' action='/Colorize Conversation/Ethernet/Color 7'/>\n"
+" <menuitem name='Color8' action='/Colorize Conversation/Ethernet/Color 8'/>\n"
+" <menuitem name='Color9' action='/Colorize Conversation/Ethernet/Color 9'/>\n"
+" <menuitem name='Color10' action='/Colorize Conversation/Ethernet/Color 10'/>\n"
+" <menuitem name='NewColoringRule' action='/Colorize Conversation/Ethernet/New Coloring Rule'/>\n"
+" </menu>\n"
+" <menu name= 'IP' action='/Colorize Conversation/IP'>\n"
+" <menuitem name='Color1' action='/Colorize Conversation/IP/Color 1'/>\n"
+" <menuitem name='Color2' action='/Colorize Conversation/IP/Color 2'/>\n"
+" <menuitem name='Color3' action='/Colorize Conversation/IP/Color 3'/>\n"
+" <menuitem name='Color4' action='/Colorize Conversation/IP/Color 4'/>\n"
+" <menuitem name='Color5' action='/Colorize Conversation/IP/Color 5'/>\n"
+" <menuitem name='Color6' action='/Colorize Conversation/IP/Color 6'/>\n"
+" <menuitem name='Color7' action='/Colorize Conversation/IP/Color 7'/>\n"
+" <menuitem name='Color8' action='/Colorize Conversation/IP/Color 8'/>\n"
+" <menuitem name='Color9' action='/Colorize Conversation/IP/Color 9'/>\n"
+" <menuitem name='Color10' action='/Colorize Conversation/IP/Color 10'/>\n"
+" <menuitem name='NewColoringRule' action='/Colorize Conversation/IP/New Coloring Rule'/>\n"
+" </menu>\n"
+" <menu name= 'TCP' action='/Colorize Conversation/TCP'>\n"
+" <menuitem name='Color1' action='/Colorize Conversation/TCP/Color 1'/>\n"
+" <menuitem name='Color2' action='/Colorize Conversation/TCP/Color 2'/>\n"
+" <menuitem name='Color3' action='/Colorize Conversation/TCP/Color 3'/>\n"
+" <menuitem name='Color4' action='/Colorize Conversation/TCP/Color 4'/>\n"
+" <menuitem name='Color5' action='/Colorize Conversation/TCP/Color 5'/>\n"
+" <menuitem name='Color6' action='/Colorize Conversation/TCP/Color 6'/>\n"
+" <menuitem name='Color7' action='/Colorize Conversation/TCP/Color 7'/>\n"
+" <menuitem name='Color8' action='/Colorize Conversation/TCP/Color 8'/>\n"
+" <menuitem name='Color9' action='/Colorize Conversation/TCP/Color 9'/>\n"
+" <menuitem name='Color10' action='/Colorize Conversation/TCP/Color 10'/>\n"
+" <menuitem name='NewColoringRule' action='/Colorize Conversation/TCP/New Coloring Rule'/>\n"
+" </menu>\n"
+" <menu name= 'UDP' action='/Colorize Conversation/UDP'>\n"
+" <menuitem name='Color1' action='/Colorize Conversation/UDP/Color 1'/>\n"
+" <menuitem name='Color2' action='/Colorize Conversation/UDP/Color 2'/>\n"
+" <menuitem name='Color3' action='/Colorize Conversation/UDP/Color 3'/>\n"
+" <menuitem name='Color4' action='/Colorize Conversation/UDP/Color 4'/>\n"
+" <menuitem name='Color5' action='/Colorize Conversation/UDP/Color 5'/>\n"
+" <menuitem name='Color6' action='/Colorize Conversation/UDP/Color 6'/>\n"
+" <menuitem name='Color7' action='/Colorize Conversation/UDP/Color 7'/>\n"
+" <menuitem name='Color8' action='/Colorize Conversation/UDP/Color 8'/>\n"
+" <menuitem name='Color9' action='/Colorize Conversation/UDP/Color 9'/>\n"
+" <menuitem name='Color10' action='/Colorize Conversation/UDP/Color 10'/>\n"
+" <menuitem name='NewColoringRule' action='/Colorize Conversation/UDP/New Coloring Rule'/>\n"
+" </menu>\n"
+" <menu name= 'PN-CBA' action='/Colorize Conversation/PN-CBA'>\n"
+" <menuitem name='Color1' action='/Colorize Conversation/PN-CBA/Color 1'/>\n"
+" <menuitem name='Color2' action='/Colorize Conversation/PN-CBA/Color 2'/>\n"
+" <menuitem name='Color3' action='/Colorize Conversation/PN-CBA/Color 3'/>\n"
+" <menuitem name='Color4' action='/Colorize Conversation/PN-CBA/Color 4'/>\n"
+" <menuitem name='Color5' action='/Colorize Conversation/PN-CBA/Color 5'/>\n"
+" <menuitem name='Color6' action='/Colorize Conversation/PN-CBA/Color 6'/>\n"
+" <menuitem name='Color7' action='/Colorize Conversation/PN-CBA/Color 7'/>\n"
+" <menuitem name='Color8' action='/Colorize Conversation/PN-CBA/Color 8'/>\n"
+" <menuitem name='Color9' action='/Colorize Conversation/PN-CBA/Color 9'/>\n"
+" <menuitem name='Color10' action='/Colorize Conversation/PN-CBA/Color 10'/>\n"
+" <menuitem name='NewColoringRule' action='/Colorize Conversation/PN-CBA/New Coloring Rule'/>\n"
+" </menu>\n"
+" </menu>\n"
+" <menu name= 'SCTP' action='/SCTP'>\n"
+" <menuitem name='AnalysethisAssociation' action='/SCTP/Analyse this Association'/>\n"
+" <menuitem name='PrepareFilterforthisAssociation' action='/SCTP/Prepare Filter for this Association'/>\n"
+" </menu>\n"
+" <menuitem name='FollowTCPStream' action='/Follow TCP Stream'/>\n"
+" <menuitem name='FollowUDPStream' action='/Follow UDP Stream'/>\n"
+" <menuitem name='FollowSSLStream' action='/Follow SSL Stream'/>\n"
+" <separator/>\n"
+" <menu name= 'Copy' action='/Copy'>\n"
+" <menuitem name='SummaryTxt' action='/Copy/SummaryTxt'/>\n"
+" <menuitem name='SummaryCSV' action='/Copy/SummaryCSV'/>\n"
+" <menuitem name='AsFilter' action='/Copy/AsFilter'/>\n"
+" <separator/>\n"
+" <menu name= 'Bytes' action='/Copy/Bytes'>\n"
+" <menuitem name='OffsetHexText' action='/Copy/Bytes/OffsetHexText'/>\n"
+" <menuitem name='OffsetHex' action='/Copy/Bytes/OffsetHex'/>\n"
+" <menuitem name='PrintableTextOnly' action='/Copy/Bytes/PrintableTextOnly'/>\n"
+" <separator/>\n"
+" <menuitem name='HexStream' action='/Copy/Bytes/HexStream'/>\n"
+" <menuitem name='BinaryStream' action='/Copy/Bytes/BinaryStream'/>\n"
+" </menu>\n"
+" </menu>\n"
+" <separator/>\n"
+" <menuitem name='DecodeAs' action='/DecodeAs'/>\n"
+" <menuitem name='Print' action='/Print'/>\n"
+" <menuitem name='ShowPacketinNewWindow' action='/ShowPacketinNewWindow'/>\n"
+" </popup>\n"
+"</ui>\n";
+
+static const GtkActionEntry packet_list_menu_popup_action_entries[] = {
+ { "/MarkPacket", NULL, "Mark Packet (toggle)", NULL, NULL, G_CALLBACK(new_packet_list_mark_frame_cb) },
+ { "/IgnorePacket", NULL, "Ignore Packet (toggle)", NULL, NULL, G_CALLBACK(new_packet_list_ignore_frame_cb) },
+ { "/Set Time Reference", WIRESHARK_STOCK_TIME, "Set Time Reference (toggle)", NULL, NULL, G_CALLBACK(packet_list_menu_set_ref_time_cb) },
+ { "/TimeShift", WIRESHARK_STOCK_TIME, "Time Shift...", NULL, NULL, G_CALLBACK(time_shift_cb) },
+ { "/ManuallyResolveAddress", NULL, "Manually Resolve Address", NULL, NULL, G_CALLBACK(manual_addr_resolv_dlg) },
+ { "/Apply as Filter", NULL, "Apply as Filter", NULL, NULL, NULL },
+
+ { "/Apply as Filter/Selected", NULL, "_Selected" , NULL, NULL, G_CALLBACK(packet_list_menu_apply_selected_cb) },
+ { "/Apply as Filter/Not Selected", NULL, "_Not Selected", NULL, NULL, G_CALLBACK(packet_list_menu_apply_not_selected_cb) },
+ { "/Apply as Filter/AndSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " _and Selected", NULL, NULL, G_CALLBACK(packet_list_menu_apply_and_selected_cb) },
+ { "/Apply as Filter/OrSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " _or Selected", NULL, NULL, G_CALLBACK(packet_list_menu_apply_or_selected_cb) },
+ { "/Apply as Filter/AndNotSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " a_nd not Selected", NULL, NULL, G_CALLBACK(packet_list_menu_apply_and_not_selected_cb) },
+ { "/Apply as Filter/OrNotSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " o_r not Selected", NULL, NULL, G_CALLBACK(packet_list_menu_apply_or_not_selected_cb) },
+
+ { "/Prepare a Filter", NULL, "Prepare a Filter", NULL, NULL, NULL },
+ { "/Prepare a Filter/Selected", NULL, "_Selected" , NULL, NULL, G_CALLBACK(packet_list_menu_prepare_selected_cb) },
+ { "/Prepare a Filter/Not Selected", NULL, "_Not Selected", NULL, NULL, G_CALLBACK(packet_list_menu_prepare_not_selected_cb) },
+ { "/Prepare a Filter/AndSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " _and Selected", NULL, NULL, G_CALLBACK(packet_list_menu_prepare_and_selected_cb) },
+ { "/Prepare a Filter/OrSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " _or Selected", NULL, NULL, G_CALLBACK(packet_list_menu_prepare_or_selected_cb) },
+ { "/Prepare a Filter/AndNotSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " a_nd not Selected", NULL, NULL, G_CALLBACK(packet_list_menu_prepare_and_not_selected_cb) },
+ { "/Prepare a Filter/OrNotSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " o_r not Selected", NULL, NULL, G_CALLBACK(packet_list_menu_prepare_or_not_selected_cb) },
+
+ { "/Conversation Filter", NULL, "Conversation Filter", NULL, NULL, NULL },
+ { "/Conversation Filter/Ethernet", NULL, "Ethernet", NULL, NULL, G_CALLBACK(packet_list_menu_conversation_ethernet_cb) },
+ { "/Conversation Filter/IP", NULL, "IP", NULL, NULL, G_CALLBACK(packet_list_menu_conversation_ip_cb) },
+ { "/Conversation Filter/TCP", NULL, "TCP", NULL, NULL, G_CALLBACK(packet_list_menu_conversation_tcp_cb) },
+ { "/Conversation Filter/UDP", NULL, "UDP", NULL, NULL, G_CALLBACK(packet_list_menu_conversation_udp_cb) },
+ { "/Conversation Filter/PN-CBA", NULL, "PN-CBA", NULL, NULL, G_CALLBACK(packet_list_menu_conversation_pn_cba_cb) },
+
+ { "/Colorize Conversation", NULL, "Colorize Conversation", NULL, NULL, NULL },
+
+ { "/Colorize Conversation/Ethernet", NULL, "Ethernet", NULL, NULL, NULL },
+
+ { "/Colorize Conversation/Ethernet/Color 1", WIRESHARK_STOCK_COLOR1, "Color 1", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ethernet_color1_cb) },
+ { "/Colorize Conversation/Ethernet/Color 2", WIRESHARK_STOCK_COLOR2, "Color 2", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ethernet_color2_cb) },
+ { "/Colorize Conversation/Ethernet/Color 3", WIRESHARK_STOCK_COLOR3, "Color 3", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ethernet_color3_cb) },
+ { "/Colorize Conversation/Ethernet/Color 4", WIRESHARK_STOCK_COLOR4, "Color 4", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ethernet_color4_cb) },
+ { "/Colorize Conversation/Ethernet/Color 5", WIRESHARK_STOCK_COLOR5, "Color 5", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ethernet_color5_cb) },
+ { "/Colorize Conversation/Ethernet/Color 6", WIRESHARK_STOCK_COLOR6, "Color 6", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ethernet_color6_cb) },
+ { "/Colorize Conversation/Ethernet/Color 7", WIRESHARK_STOCK_COLOR7, "Color 7", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ethernet_color7_cb) },
+ { "/Colorize Conversation/Ethernet/Color 8", WIRESHARK_STOCK_COLOR8, "Color 8", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ethernet_color8_cb) },
+ { "/Colorize Conversation/Ethernet/Color 9", WIRESHARK_STOCK_COLOR9, "Color 9", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ethernet_color9_cb) },
+ { "/Colorize Conversation/Ethernet/Color 10", WIRESHARK_STOCK_COLOR0, "Color 10", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ethernet_color10_cb) },
+ { "/Colorize Conversation/Ethernet/New Coloring Rule", NULL, "New Coloring Rule...", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ethernet_new_rule_cb) },
+
+ { "/Colorize Conversation/IP", NULL, "IP", NULL, NULL, NULL },
+
+ { "/Colorize Conversation/IP/Color 1", WIRESHARK_STOCK_COLOR1, "Color 1", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ip_color1_cb) },
+ { "/Colorize Conversation/IP/Color 2", WIRESHARK_STOCK_COLOR2, "Color 2", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ip_color2_cb) },
+ { "/Colorize Conversation/IP/Color 3", WIRESHARK_STOCK_COLOR3, "Color 3", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ip_color3_cb) },
+ { "/Colorize Conversation/IP/Color 4", WIRESHARK_STOCK_COLOR4, "Color 4", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ip_color4_cb) },
+ { "/Colorize Conversation/IP/Color 5", WIRESHARK_STOCK_COLOR5, "Color 5", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ip_color5_cb) },
+ { "/Colorize Conversation/IP/Color 6", WIRESHARK_STOCK_COLOR6, "Color 6", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ip_color6_cb) },
+ { "/Colorize Conversation/IP/Color 7", WIRESHARK_STOCK_COLOR7, "Color 7", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ip_color7_cb) },
+ { "/Colorize Conversation/IP/Color 8", WIRESHARK_STOCK_COLOR8, "Color 8", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ip_color8_cb) },
+ { "/Colorize Conversation/IP/Color 9", WIRESHARK_STOCK_COLOR9, "Color 9", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ip_color9_cb) },
+ { "/Colorize Conversation/IP/Color 10", WIRESHARK_STOCK_COLOR0, "Color 10", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ip_color10_cb) },
+ { "/Colorize Conversation/IP/New Coloring Rule", NULL, "New Coloring Rule...", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_ip_new_rule_cb) },
+
+ { "/Colorize Conversation/TCP", NULL, "TCP", NULL, NULL, NULL },
+
+ { "/Colorize Conversation/TCP/Color 1", WIRESHARK_STOCK_COLOR1, "Color 1", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_tcp_color1_cb) },
+ { "/Colorize Conversation/TCP/Color 2", WIRESHARK_STOCK_COLOR2, "Color 2", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_tcp_color2_cb) },
+ { "/Colorize Conversation/TCP/Color 3", WIRESHARK_STOCK_COLOR3, "Color 3", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_tcp_color3_cb) },
+ { "/Colorize Conversation/TCP/Color 4", WIRESHARK_STOCK_COLOR4, "Color 4", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_tcp_color4_cb) },
+ { "/Colorize Conversation/TCP/Color 5", WIRESHARK_STOCK_COLOR5, "Color 5", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_tcp_color5_cb) },
+ { "/Colorize Conversation/TCP/Color 6", WIRESHARK_STOCK_COLOR6, "Color 6", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_tcp_color6_cb) },
+ { "/Colorize Conversation/TCP/Color 7", WIRESHARK_STOCK_COLOR7, "Color 7", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_tcp_color7_cb) },
+ { "/Colorize Conversation/TCP/Color 8", WIRESHARK_STOCK_COLOR8, "Color 8", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_tcp_color8_cb) },
+ { "/Colorize Conversation/TCP/Color 9", WIRESHARK_STOCK_COLOR9, "Color 9", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_tcp_color9_cb) },
+ { "/Colorize Conversation/TCP/Color 10", WIRESHARK_STOCK_COLOR0, "Color 10", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_tcp_color10_cb) },
+ { "/Colorize Conversation/TCP/New Coloring Rule", NULL, "New Coloring Rule...", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_tcp_new_rule_cb) },
+
+ { "/Colorize Conversation/UDP", NULL, "UDP", NULL, NULL, NULL },
+
+ { "/Colorize Conversation/UDP/Color 1", WIRESHARK_STOCK_COLOR1, "Color 1", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_udp_color1_cb) },
+ { "/Colorize Conversation/UDP/Color 2", WIRESHARK_STOCK_COLOR2, "Color 2", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_udp_color2_cb) },
+ { "/Colorize Conversation/UDP/Color 3", WIRESHARK_STOCK_COLOR3, "Color 3", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_udp_color3_cb) },
+ { "/Colorize Conversation/UDP/Color 4", WIRESHARK_STOCK_COLOR4, "Color 4", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_udp_color4_cb) },
+ { "/Colorize Conversation/UDP/Color 5", WIRESHARK_STOCK_COLOR5, "Color 5", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_udp_color5_cb) },
+ { "/Colorize Conversation/UDP/Color 6", WIRESHARK_STOCK_COLOR6, "Color 6", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_udp_color6_cb) },
+ { "/Colorize Conversation/UDP/Color 7", WIRESHARK_STOCK_COLOR7, "Color 7", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_udp_color7_cb) },
+ { "/Colorize Conversation/UDP/Color 8", WIRESHARK_STOCK_COLOR8, "Color 8", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_udp_color8_cb) },
+ { "/Colorize Conversation/UDP/Color 9", WIRESHARK_STOCK_COLOR9, "Color 9", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_udp_color9_cb) },
+ { "/Colorize Conversation/UDP/Color 10", WIRESHARK_STOCK_COLOR0, "Color 10", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_udp_color10_cb) },
+ { "/Colorize Conversation/UDP/New Coloring Rule", NULL, "New Coloring Rule...", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_udp_new_rule_cb) },
+
+ { "/Colorize Conversation/PN-CBA", NULL, "PN-CBA Server", NULL, NULL, NULL },
+
+ { "/Colorize Conversation/PN-CBA/Color 1", WIRESHARK_STOCK_COLOR1, "Color 1", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_cba_color1_cb) },
+ { "/Colorize Conversation/PN-CBA/Color 2", WIRESHARK_STOCK_COLOR2, "Color 2", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_cba_color2_cb) },
+ { "/Colorize Conversation/PN-CBA/Color 3", WIRESHARK_STOCK_COLOR3, "Color 3", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_cba_color3_cb) },
+ { "/Colorize Conversation/PN-CBA/Color 4", WIRESHARK_STOCK_COLOR4, "Color 4", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_cba_color4_cb) },
+ { "/Colorize Conversation/PN-CBA/Color 5", WIRESHARK_STOCK_COLOR5, "Color 5", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_cba_color5_cb) },
+ { "/Colorize Conversation/PN-CBA/Color 6", WIRESHARK_STOCK_COLOR6, "Color 6", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_cba_color6_cb) },
+ { "/Colorize Conversation/PN-CBA/Color 7", WIRESHARK_STOCK_COLOR7, "Color 7", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_cba_color7_cb) },
+ { "/Colorize Conversation/PN-CBA/Color 8", WIRESHARK_STOCK_COLOR8, "Color 8", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_cba_color8_cb) },
+ { "/Colorize Conversation/PN-CBA/Color 9", WIRESHARK_STOCK_COLOR9, "Color 9", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_cba_color9_cb) },
+ { "/Colorize Conversation/PN-CBA/Color 10", WIRESHARK_STOCK_COLOR0, "Color 10", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_cba_color10_cb) },
+ { "/Colorize Conversation/PN-CBA/New Coloring Rule", NULL, "New Coloring Rule...", NULL, NULL, G_CALLBACK(packet_list_menu_color_conv_cba_new_rule_cb) },
+
+ { "/SCTP", NULL, "SCTP", NULL, NULL, NULL },
+ { "/SCTP/Analyse this Association", NULL, "Analyse this Association", NULL, NULL, G_CALLBACK(sctp_analyse_start) },
+ { "/SCTP/Prepare Filter for this Association", NULL, "Prepare Filter for this Association", NULL, NULL, G_CALLBACK(sctp_set_assoc_filter) },
+
+
+ { "/Follow TCP Stream", NULL, "Follow TCP Stream", NULL, NULL, G_CALLBACK(follow_tcp_stream_cb) },
+ { "/Follow UDP Stream", NULL, "Follow UDP Stream", NULL, NULL, G_CALLBACK(follow_udp_stream_cb) },
+ { "/Follow SSL Stream", NULL, "Follow SSL Stream", NULL, NULL, G_CALLBACK(follow_ssl_stream_cb) },
+
+ { "/Copy", NULL, "Copy", NULL, NULL, NULL },
+ { "/Copy/SummaryTxt", NULL, "Summary (Text)", NULL, NULL, G_CALLBACK(packet_list_menu_copy_sum_txt) },
+ { "/Copy/SummaryCSV", NULL, "Summary (CSV)", NULL, NULL, G_CALLBACK(packet_list_menu_copy_sum_csv) },
+ { "/Copy/AsFilter", NULL, "As Filter", NULL, NULL, G_CALLBACK(packet_list_menu_copy_as_flt) },
+
+
+ { "/Copy/Bytes", NULL, "Bytes", NULL, NULL, NULL },
+ { "/Copy/Bytes/OffsetHexText", NULL, "Offset Hex Text", NULL, NULL, G_CALLBACK(packet_list_menu_copy_bytes_oht_cb) },
+ { "/Copy/Bytes/OffsetHex", NULL, "Offset Hex", NULL, NULL, G_CALLBACK(packet_list_menu_copy_bytes_oh_cb) },
+ { "/Copy/Bytes/PrintableTextOnly", NULL, "Printable Text Only", NULL, NULL, G_CALLBACK(packet_list_menu_copy_bytes_text_cb) },
+
+ { "/Copy/Bytes/HexStream", NULL, "Hex Stream", NULL, NULL, G_CALLBACK(packet_list_menu_copy_bytes_hex_strm_cb) },
+ { "/Copy/Bytes/BinaryStream", NULL, "Binary Stream", NULL, NULL, G_CALLBACK(packet_list_menu_copy_bytes_bin_strm_cb) },
+
+ { "/DecodeAs", WIRESHARK_STOCK_DECODE_AS, "Decode As...", NULL, NULL, G_CALLBACK(decode_as_cb) },
+ { "/Print", GTK_STOCK_PRINT, "Print...", NULL, NULL, G_CALLBACK(file_print_selected_cmd_cb) },
+ { "/ShowPacketinNewWindow", NULL, "Show Packet in New Window", NULL, NULL, G_CALLBACK(new_window_cb) },
+
+};
+
+static const char *ui_desc_tree_view_menu_popup =
+"<ui>\n"
+" <popup name='TreeViewPopup' action='PopupAction'>\n"
+" <menuitem name='ExpandSubtrees' action='/ExpandSubtrees'/>\n"
+" <menuitem name='ExpandAll' action='/ExpandAll'/>\n"
+" <menuitem name='CollapseAll' action='/CollapseAll'/>\n"
+" <separator/>\n"
+" <menuitem name='ApplyasColumn' action='/Apply as Column'/>\n"
+" <separator/>\n"
+" <menu name= 'ApplyAsFilter' action='/Apply as Filter'>\n"
+" <menuitem name='Selected' action='/Apply as Filter/Selected'/>\n"
+" <menuitem name='NotSelected' action='/Apply as Filter/Not Selected'/>\n"
+" <menuitem name='AndSelected' action='/Apply as Filter/AndSelected'/>\n"
+" <menuitem name='OrSelected' action='/Apply as Filter/OrSelected'/>\n"
+" <menuitem name='AndNotSelected' action='/Apply as Filter/AndNotSelected'/>\n"
+" <menuitem name='OrNotSelected' action='/Apply as Filter/OrNotSelected'/>\n"
+" </menu>\n"
+" <menu name= 'PrepareaFilter' action='/Prepare a Filter'>\n"
+" <menuitem name='Selected' action='/Prepare a Filter/Selected'/>\n"
+" <menuitem name='NotSelected' action='/Prepare a Filter/Not Selected'/>\n"
+" <menuitem name='AndSelected' action='/Prepare a Filter/AndSelected'/>\n"
+" <menuitem name='OrSelected' action='/Prepare a Filter/OrSelected'/>\n"
+" <menuitem name='AndNotSelected' action='/Prepare a Filter/AndNotSelected'/>\n"
+" <menuitem name='OrNotSelected' action='/Prepare a Filter/OrNotSelected'/>\n"
+" </menu>\n"
+" <menu name= 'ColorizewithFilter' action='/Colorize with Filter'>\n"
+" <menuitem name='Color1' action='/Colorize with Filter/Color 1'/>\n"
+" <menuitem name='Color2' action='/Colorize with Filter/Color 2'/>\n"
+" <menuitem name='Color3' action='/Colorize with Filter/Color 3'/>\n"
+" <menuitem name='Color4' action='/Colorize with Filter/Color 4'/>\n"
+" <menuitem name='Color5' action='/Colorize with Filter/Color 5'/>\n"
+" <menuitem name='Color6' action='/Colorize with Filter/Color 6'/>\n"
+" <menuitem name='Color7' action='/Colorize with Filter/Color 7'/>\n"
+" <menuitem name='Color8' action='/Colorize with Filter/Color 8'/>\n"
+" <menuitem name='Color9' action='/Colorize with Filter/Color 9'/>\n"
+" <menuitem name='Color10' action='/Colorize with Filter/Color 10'/>\n"
+" <menuitem name='NewColoringRule' action='/Colorize with Filter/New Coloring Rule'/>\n"
+" </menu>\n"
+" <menuitem name='FollowTCPStream' action='/Follow TCP Stream'/>\n"
+" <menuitem name='FollowUDPStream' action='/Follow UDP Stream'/>\n"
+" <menuitem name='FollowSSLStream' action='/Follow SSL Stream'/>\n"
+" <separator/>\n"
+" <menu name= 'Copy' action='/Copy'>\n"
+" <menuitem name='Description' action='/Copy/Description'/>\n"
+" <menuitem name='Fieldname' action='/Copy/Fieldname'/>\n"
+" <menuitem name='Value' action='/Copy/Value'/>\n"
+" <separator/>\n"
+" <menuitem name='AsFilter' action='/Copy/AsFilter'/>\n"
+" <separator/>\n"
+" <menu name= 'Bytes' action='/Copy/Bytes'>\n"
+" <menuitem name='OffsetHexText' action='/Copy/Bytes/OffsetHexText'/>\n"
+" <menuitem name='OffsetHex' action='/Copy/Bytes/OffsetHex'/>\n"
+" <menuitem name='PrintableTextOnly' action='/Copy/Bytes/PrintableTextOnly'/>\n"
+" <separator/>\n"
+" <menuitem name='HexStream' action='/Copy/Bytes/HexStream'/>\n"
+" <menuitem name='BinaryStream' action='/Copy/Bytes/BinaryStream'/>\n"
+" </menu>\n"
+" </menu>\n"
+" <menuitem name='ExportSelectedPacketBytes' action='/ExportSelectedPacketBytes'/>\n"
+" <separator/>\n"
+" <menuitem name='WikiProtocolPage' action='/WikiProtocolPage'/>\n"
+" <menuitem name='FilterFieldReference' action='/FilterFieldReference'/>\n"
+" <menuitem name='ProtocolHelp' action='/ProtocolHelp'/>\n"
+" <menuitem name='ProtocolPreferences' action='/ProtocolPreferences'/>\n"
+" <separator/>\n"
+" <menuitem name='DecodeAs' action='/DecodeAs'/>\n"
+" <menuitem name='DisableProtocol' action='/DisableProtocol'/>\n"
+" <menuitem name='ResolveName' action='/ResolveName'/>\n"
+" <menuitem name='GotoCorrespondingPacket' action='/GotoCorrespondingPacket'/>\n"
+" </popup>\n"
+"</ui>\n";
+
+static const GtkActionEntry tree_view_menu_popup_action_entries[] = {
+ { "/ExpandSubtrees", NULL, "Expand Subtrees", NULL, NULL, G_CALLBACK(expand_tree_cb) },
+ { "/ExpandAll", NULL, "Expand All", NULL, NULL, G_CALLBACK(expand_all_cb) },
+ { "/CollapseAll", NULL, "Collapse All", NULL, NULL, G_CALLBACK(collapse_all_cb) },
+ { "/Apply as Column", NULL, "Apply as Column", NULL, NULL, G_CALLBACK(apply_as_custom_column_cb) },
+ { "/Apply as Filter", NULL, "Apply as Filter", NULL, NULL, NULL },
+
+ { "/Apply as Filter/Selected", NULL, "_Selected" , NULL, NULL, G_CALLBACK(tree_view_menu_apply_selected_cb) },
+ { "/Apply as Filter/Not Selected", NULL, "_Not Selected", NULL, NULL, G_CALLBACK(tree_view_menu_apply_not_selected_cb) },
+ { "/Apply as Filter/AndSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " _and Selected", NULL, NULL, G_CALLBACK(tree_view_menu_apply_and_selected_cb) },
+ { "/Apply as Filter/OrSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " _or Selected", NULL, NULL, G_CALLBACK(tree_view_menu_apply_or_selected_cb) },
+ { "/Apply as Filter/AndNotSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " a_nd not Selected", NULL, NULL, G_CALLBACK(tree_view_menu_apply_and_not_selected_cb) },
+ { "/Apply as Filter/OrNotSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " o_r not Selected", NULL, NULL, G_CALLBACK(tree_view_menu_apply_or_not_selected_cb) },
+
+ { "/Prepare a Filter", NULL, "Prepare a Filter", NULL, NULL, NULL },
+ { "/Prepare a Filter/Selected", NULL, "_Selected" , NULL, NULL, G_CALLBACK(tree_view_menu_prepare_selected_cb) },
+ { "/Prepare a Filter/Not Selected", NULL, "_Not Selected", NULL, NULL, G_CALLBACK(tree_view_menu_prepare_not_selected_cb) },
+ { "/Prepare a Filter/AndSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " _and Selected", NULL, NULL, G_CALLBACK(tree_view_menu_prepare_and_selected_cb) },
+ { "/Prepare a Filter/OrSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " _or Selected", NULL, NULL, G_CALLBACK(tree_view_menu_prepare_or_selected_cb) },
+ { "/Prepare a Filter/AndNotSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " a_nd not Selected", NULL, NULL, G_CALLBACK(tree_view_menu_prepare_and_not_selected_cb) },
+ { "/Prepare a Filter/OrNotSelected", NULL, UTF8_HORIZONTAL_ELLIPSIS " o_r not Selected", NULL, NULL, G_CALLBACK(tree_view_menu_prepare_or_not_selected_cb) },
+
+ { "/Colorize with Filter", NULL, "Colorize with Filter", NULL, NULL, NULL },
+ { "/Colorize with Filter/Color 1", WIRESHARK_STOCK_COLOR1, "Color 1", NULL, NULL, G_CALLBACK(tree_view_menu_color_with_flt_color1_cb) },
+ { "/Colorize with Filter/Color 2", WIRESHARK_STOCK_COLOR2, "Color 2", NULL, NULL, G_CALLBACK(tree_view_menu_color_with_flt_color2_cb) },
+ { "/Colorize with Filter/Color 3", WIRESHARK_STOCK_COLOR3, "Color 3", NULL, NULL, G_CALLBACK(tree_view_menu_color_with_flt_color3_cb) },
+ { "/Colorize with Filter/Color 4", WIRESHARK_STOCK_COLOR4, "Color 4", NULL, NULL, G_CALLBACK(tree_view_menu_color_with_flt_color4_cb) },
+ { "/Colorize with Filter/Color 5", WIRESHARK_STOCK_COLOR5, "Color 5", NULL, NULL, G_CALLBACK(tree_view_menu_color_with_flt_color5_cb) },
+ { "/Colorize with Filter/Color 6", WIRESHARK_STOCK_COLOR6, "Color 6", NULL, NULL, G_CALLBACK(tree_view_menu_color_with_flt_color6_cb) },
+ { "/Colorize with Filter/Color 7", WIRESHARK_STOCK_COLOR7, "Color 7", NULL, NULL, G_CALLBACK(tree_view_menu_color_with_flt_color7_cb) },
+ { "/Colorize with Filter/Color 8", WIRESHARK_STOCK_COLOR8, "Color 8", NULL, NULL, G_CALLBACK(tree_view_menu_color_with_flt_color8_cb) },
+ { "/Colorize with Filter/Color 9", WIRESHARK_STOCK_COLOR9, "Color 9", NULL, NULL, G_CALLBACK(tree_view_menu_color_with_flt_color9_cb) },
+ { "/Colorize with Filter/Color 10", WIRESHARK_STOCK_COLOR0, "Color 10", NULL, NULL, G_CALLBACK(tree_view_menu_color_with_flt_color10_cb) },
+ { "/Colorize with Filter/New Coloring Rule", NULL, "New Coloring Rule...", NULL, NULL, G_CALLBACK(tree_view_menu_color_with_flt_new_rule_cb) },
+
+ { "/Follow TCP Stream", NULL, "Follow TCP Stream", NULL, NULL, G_CALLBACK(follow_tcp_stream_cb) },
+ { "/Follow UDP Stream", NULL, "Follow UDP Stream", NULL, NULL, G_CALLBACK(follow_udp_stream_cb) },
+ { "/Follow SSL Stream", NULL, "Follow SSL Stream", NULL, NULL, G_CALLBACK(follow_ssl_stream_cb) },
+
+ { "/Copy", NULL, "Copy", NULL, NULL, NULL },
+ { "/Copy/Description", NULL, "Description", NULL, NULL, G_CALLBACK(tree_view_menu_copy_desc) },
+ { "/Copy/Fieldname", NULL, "Fieldname", NULL, NULL, G_CALLBACK(tree_view_menu_copy_field) },
+ { "/Copy/Value", NULL, "Value", NULL, NULL, G_CALLBACK(tree_view_menu_copy_value) },
+
+ { "/Copy/AsFilter", NULL, "As Filter", NULL, NULL, G_CALLBACK(tree_view_menu_copy_as_flt) },
+
+ { "/Copy/Bytes", NULL, "Bytes", NULL, NULL, NULL },
+ { "/Copy/Bytes/OffsetHexText", NULL, "Offset Hex Text", NULL, NULL, G_CALLBACK(packet_list_menu_copy_bytes_oht_cb) },
+ { "/Copy/Bytes/OffsetHex", NULL, "Offset Hex", NULL, NULL, G_CALLBACK(packet_list_menu_copy_bytes_oh_cb) },
+ { "/Copy/Bytes/PrintableTextOnly", NULL, "Printable Text Only", NULL, NULL, G_CALLBACK(packet_list_menu_copy_bytes_text_cb) },
+
+ { "/Copy/Bytes/HexStream", NULL, "Hex Stream", NULL, NULL, G_CALLBACK(packet_list_menu_copy_bytes_hex_strm_cb) },
+ { "/Copy/Bytes/BinaryStream", NULL, "Binary Stream", NULL, NULL, G_CALLBACK(packet_list_menu_copy_bytes_bin_strm_cb) },
+
+ { "/ExportSelectedPacketBytes", NULL, "Export Selected Packet Bytes...", NULL, NULL, G_CALLBACK(savehex_cb) },
+
+ { "/WikiProtocolPage", WIRESHARK_STOCK_WIKI, "Wiki Protocol Page", NULL, NULL, G_CALLBACK(selected_ptree_info_cb) },
+ { "/FilterFieldReference", WIRESHARK_STOCK_INTERNET, "Filter Field Reference", NULL, NULL, G_CALLBACK(selected_ptree_ref_cb) },
+ { "/ProtocolHelp", NULL, "Protocol Help", NULL, NULL, NULL },
+ { "/ProtocolPreferences", NULL, "Protocol Preferences", NULL, NULL, NULL },
+ { "/DecodeAs", WIRESHARK_STOCK_DECODE_AS, "Decode As...", NULL, NULL, G_CALLBACK(decode_as_cb) },
+ { "/DisableProtocol", WIRESHARK_STOCK_CHECKBOX, "Disable Protocol...", NULL, NULL, G_CALLBACK(proto_disable_cb) },
+ { "/ResolveName", NULL, "_Resolve Name", NULL, NULL, G_CALLBACK(resolve_name_cb) },
+ { "/GotoCorrespondingPacket", NULL, "_Go to Corresponding Packet", NULL, NULL, G_CALLBACK(goto_framenum_cb) },
+};
+
+static const char *ui_desc_bytes_menu_popup =
+"<ui>\n"
+" <popup name='BytesMenuPopup' action='PopupAction'>\n"
+" <menuitem name='HexView' action='/HexView'/>\n"
+" <menuitem name='BitsView' action='/BitsView'/>\n"
+" </popup>\n"
+"</ui>\n";
+
+static const GtkRadioActionEntry bytes_menu_radio_action_entries [] =
+{
+ /* name, stock id, label, accel, tooltip, value */
+ { "/HexView", NULL, "Hex View", NULL, NULL, BYTES_HEX },
+ { "/BitsView", NULL, "Bits View", NULL, NULL, BYTES_BITS },
+};
+
+static const char *ui_statusbar_profiles_menu_popup =
+"<ui>\n"
+" <popup name='ProfilesMenuPopup' action='PopupAction'>\n"
+" <menuitem name='Profiles' action='/Profiles'/>\n"
+" <separator/>\n"
+" <menuitem name='New' action='/New'/>\n"
+" <menuitem name='Edit' action='/Edit'/>\n"
+" <menuitem name='Delete' action='/Delete'/>\n"
+" <separator/>\n"
+" <menu name='Change' action='/Change'>\n"
+" <menuitem name='Default' action='/Change/Default'/>\n"
+" </menu>\n"
+" </popup>\n"
+"</ui>\n";
+static const GtkActionEntry statusbar_profiles_menu_action_entries [] =
+{
+ { "/Profiles", NULL, "Configuration Profiles...", NULL, NULL, G_CALLBACK(profile_dialog_cb) },
+ { "/New", GTK_STOCK_NEW, "New...", NULL, NULL, G_CALLBACK(profile_new_cb) },
+ { "/Edit", GTK_STOCK_EDIT, "Edit...", NULL, NULL, G_CALLBACK(profile_edit_cb) },
+ { "/Delete", GTK_STOCK_DELETE, "Delete", NULL, NULL, G_CALLBACK(profile_delete_cb) },
+ { "/Change", NULL, "Change", NULL, NULL, NULL },
+ { "/Change/Default", NULL, "Default", NULL, NULL, NULL },
+};
+
+GtkWidget *
+main_menu_new(GtkAccelGroup ** table) {
+ GtkWidget *menubar;
+#ifdef HAVE_IGE_MAC_INTEGRATION
+ GtkWidget *quit_item, *about_item, *preferences_item;
+ IgeMacMenuGroup *group;
+#endif
+#ifdef HAVE_GTKOSXAPPLICATION
+ GtkOSXApplication *theApp;
+ GtkWidget * item;
+ GtkWidget * dock_menu;
+#endif
+
+ grp = gtk_accel_group_new();
+
+ if (initialize)
+ menus_init();
+
+ menubar = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar");
+#ifdef HAVE_IGE_MAC_INTEGRATION
+ if(prefs.gui_macosx_style) {
+ ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(menubar));
+ ige_mac_menu_set_global_key_handler_enabled(TRUE);
+
+ /* Create menu items to populate the application menu with. We have to
+ * do this because we are still using the old GtkItemFactory API for
+ * the main menu. */
+ group = ige_mac_menu_add_app_menu_group();
+ about_item = gtk_menu_item_new_with_label("About");
+ g_signal_connect(about_item, "activate", G_CALLBACK(about_wireshark_cb),
+ NULL);
+ ige_mac_menu_add_app_menu_item(group, GTK_MENU_ITEM(about_item), NULL);
+
+ group = ige_mac_menu_add_app_menu_group();
+ preferences_item = gtk_menu_item_new_with_label("Preferences");
+ g_signal_connect(preferences_item, "activate", G_CALLBACK(prefs_cb),
+ NULL);
+ ige_mac_menu_add_app_menu_item(group, GTK_MENU_ITEM(preferences_item),
+ NULL);
+ }
+
+ /* The quit item in the application menu shows up whenever ige mac
+ * integration is enabled, even if the OS X UI style in Wireshark isn't
+ * turned on. */
+ quit_item = gtk_menu_item_new_with_label("Quit");
+ g_signal_connect(quit_item, "activate", G_CALLBACK(file_quit_cmd_cb), NULL);
+ ige_mac_menu_set_quit_menu_item(GTK_MENU_ITEM(quit_item));
+#endif
+
+#ifdef HAVE_GTKOSXAPPLICATION
+ theApp = g_object_new(GTK_TYPE_OSX_APPLICATION, NULL);
+
+ if(prefs.gui_macosx_style) {
+ gtk_osxapplication_set_menu_bar(theApp, GTK_MENU_SHELL(menubar));
+ gtk_osxapplication_set_use_quartz_accelerators(theApp, TRUE);
+
+
+ item = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/HelpMenu/AboutWireshark");
+ gtk_osxapplication_insert_app_menu_item(theApp, item, 0);
+
+ item = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/EditMenu/Preferences");
+ gtk_osxapplication_insert_app_menu_item(theApp, item, 0);
+
+ item = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/HelpMenu");
+ gtk_osxapplication_set_help_menu(theApp,GTK_MENU_ITEM(item));
+
+ /* Quit item is not needed */
+ /* XXXX FIX ME */
+ /*gtk_item_factory_delete_item(main_menu_factory,"/File/Quit");*/
+ }
+
+ /* generate dock menu */
+ dock_menu = gtk_menu_new();
+
+ item = gtk_menu_item_new_with_label("Start");
+ g_signal_connect(item, "activate", G_CALLBACK (capture_start_cb), NULL);
+ gtk_menu_shell_append(GTK_MENU_SHELL(dock_menu), item);
+
+ item = gtk_menu_item_new_with_label("Stop");
+ g_signal_connect(item, "activate", G_CALLBACK (capture_stop_cb), NULL);
+ gtk_menu_shell_append(GTK_MENU_SHELL(dock_menu), item);
+
+ item = gtk_menu_item_new_with_label("Restart");
+ g_signal_connect(item, "activate", G_CALLBACK (capture_restart_cb), NULL);
+ gtk_menu_shell_append(GTK_MENU_SHELL(dock_menu), item);
+
+ gtk_osxapplication_set_dock_menu(theApp, GTK_MENU_SHELL(dock_menu));
+#endif
+
+ if (table)
+ *table = grp;
+
+ return menubar;
+}
+
+static void
+menu_dissector_filter_cb( GtkAction *action _U_, gpointer callback_data)
+{
+ dissector_filter_t *filter_entry = callback_data;
+ GtkWidget *filter_te;
+ const char *buf;
+
+ filter_te = gtk_bin_get_child(GTK_BIN(g_object_get_data(G_OBJECT(top_level), E_DFILTER_CM_KEY)));
+
+ /* XXX - this gets the packet_info of the last dissected packet, */
+ /* which is not necessarily the last selected packet */
+ /* e.g. "Update list of packets in real time" won't work correct */
+ buf = filter_entry->build_filter_string(&cfile.edt->pi);
+
+ gtk_entry_set_text(GTK_ENTRY(filter_te), buf);
+
+ /* Run the display filter so it goes in effect - even if it's the
+ same as the previous display filter. */
+ main_filter_packets(&cfile, buf, TRUE);
+
+ g_free( (void *) buf);
+}
+
+static gboolean menu_dissector_filter_spe_cb(frame_data *fd _U_, epan_dissect_t *edt, gpointer callback_data) {
+ dissector_filter_t *filter_entry = callback_data;
+
+ /* XXX - this gets the packet_info of the last dissected packet, */
+ /* which is not necessarily the last selected packet */
+ /* e.g. "Update list of packets in real time" won't work correct */
+ return (edt != NULL) ? filter_entry->is_filter_valid(&edt->pi) : FALSE;
+}
+
+static void menu_dissector_filter(capture_file *cf) {
+ GList *list_entry = dissector_filter_list;
+ dissector_filter_t *filter_entry;
+
+ guint merge_id;
+ GtkActionGroup *action_group;
+ GtkAction *action;
+ GtkWidget *submenu_dissector_filters;
+ gchar *action_name;
+ guint i = 0;
+
+
+ merge_id = gtk_ui_manager_new_merge_id (ui_manager_main_menubar);
+
+ action_group = gtk_action_group_new ("diessector-filters-group");
+
+ submenu_dissector_filters = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/AnalyzeMenu/ConversationFilterMenu");
+ if(!submenu_dissector_filters){
+ g_warning("add_recent_items: No submenu_dissector_filters found, path= /Menubar/AnalyzeMenu/ConversationFilterMenu");
+ }
+
+ gtk_ui_manager_insert_action_group (ui_manager_main_menubar, action_group, 0);
+ g_object_set_data (G_OBJECT (ui_manager_main_menubar),
+ "diessector-filters-merge-id", GUINT_TO_POINTER (merge_id));
+
+ /* no items */
+ if (!list_entry){
+
+ action = g_object_new (GTK_TYPE_ACTION,
+ "name", "filter-list-empty",
+ "label", "No fileters",
+ "sensitive", FALSE,
+ NULL);
+ gtk_action_group_add_action (action_group, action);
+ gtk_action_set_sensitive(action, FALSE);
+ g_object_unref (action);
+
+ gtk_ui_manager_add_ui (ui_manager_main_menubar, merge_id,
+ "/Menubar/AnalyzeMenu/ConversationFilterMenu/Filters",
+ "filter-list-empty",
+ "filter-list-empty",
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+
+ return;
+ }
+
+ while(list_entry != NULL) {
+ filter_entry = list_entry->data;
+ action_name = g_strdup_printf ("filter-%u", i);
+ /*g_warning("action_name %s, filter_entry->name %s",action_name,filter_entry->name);*/
+ action = g_object_new (GTK_TYPE_ACTION,
+ "name", action_name,
+ "label", filter_entry->name,
+ "sensitive", menu_dissector_filter_spe_cb(/* frame_data *fd _U_*/ NULL, cf->edt, filter_entry),
+ NULL);
+ g_signal_connect (action, "activate",
+ G_CALLBACK (menu_dissector_filter_cb), filter_entry);
+ gtk_action_group_add_action (action_group, action);
+ g_object_unref (action);
+
+ gtk_ui_manager_add_ui (ui_manager_main_menubar, merge_id,
+ "/Menubar/AnalyzeMenu/ConversationFilterMenu/Filters",
+ action_name,
+ action_name,
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+ i++;
+ list_entry = g_list_next(list_entry);
+ }
+}
+
+static void
+menus_init(void) {
+ GtkActionGroup *packet_list_heading_action_group, *packet_list_action_group,
+ *packet_list_details_action_group, *packet_list_byte_menu_action_group,
+ *statusbar_profiles_action_group;
+ GError *error = NULL;
+ guint merge_id;
+
+#ifdef NEW_MENU_CODE
+ gchar* gui_desc_file_name_and_path;
+#endif
+ if (initialize) {
+ initialize = FALSE;
+
+ popup_menu_object = gtk_menu_new();
+
+ /* packet list heading pop-up menu */
+ packet_list_heading_action_group = gtk_action_group_new ("PacketListHeadingPopUpMenuActionGroup");
+
+ gtk_action_group_add_actions (packet_list_heading_action_group, /* the action group */
+ (gpointer)packet_list_heading_menu_popup_action_entries, /* an array of action descriptions */
+ G_N_ELEMENTS(packet_list_heading_menu_popup_action_entries), /* the number of entries */
+ popup_menu_object); /* data to pass to the action callbacks */
+
+ gtk_action_group_add_toggle_actions(packet_list_heading_action_group, /* the action group */
+ (gpointer)packet_list_heading_menu_toggle_action_entries, /* an array of action descriptions */
+ G_N_ELEMENTS(packet_list_heading_menu_toggle_action_entries), /* the number of entries */
+ NULL); /* data to pass to the action callbacks */
+
+ ui_manager_packet_list_heading = gtk_ui_manager_new ();
+ gtk_ui_manager_insert_action_group (ui_manager_packet_list_heading,
+ packet_list_heading_action_group,
+ 0); /* the position at which the group will be inserted. */
+
+ gtk_ui_manager_add_ui_from_string (ui_manager_packet_list_heading,ui_desc_packet_list_heading_menu_popup, -1, &error);
+ if (error != NULL)
+ {
+ fprintf (stderr, "Warning: building Packet List Heading Pop-Up failed: %s\n",
+ error->message);
+ g_error_free (error);
+ error = NULL;
+ }
+
+ g_object_set_data(G_OBJECT(popup_menu_object), PM_PACKET_LIST_COL_KEY,
+ gtk_ui_manager_get_widget(ui_manager_packet_list_heading, "/PacketListHeadingPopup"));
+
+ popup_menu_list = g_slist_append((GSList *)popup_menu_list, ui_manager_packet_list_heading);
+
+ /* packet list pop-up menu */
+ packet_list_action_group = gtk_action_group_new ("PacketListPopUpMenuActionGroup");
+
+ gtk_action_group_add_actions (packet_list_action_group, /* the action group */
+ (gpointer)packet_list_menu_popup_action_entries, /* an array of action descriptions */
+ G_N_ELEMENTS(packet_list_menu_popup_action_entries), /* the number of entries */
+ popup_menu_object); /* data to pass to the action callbacks */
+
+ ui_manager_packet_list_menu = gtk_ui_manager_new ();
+
+ gtk_ui_manager_insert_action_group (ui_manager_packet_list_menu,
+ packet_list_action_group,
+ 0); /* the position at which the group will be inserted. */
+
+ gtk_ui_manager_add_ui_from_string (ui_manager_packet_list_menu, ui_desc_packet_list_menu_popup, -1, &error);
+ if (error != NULL)
+ {
+ fprintf (stderr, "Warning: building Packet List Pop-Up menu failed: %s\n",
+ error->message);
+ g_error_free (error);
+ error = NULL;
+ }
+
+ g_object_set_data(G_OBJECT(popup_menu_object), PM_PACKET_LIST_KEY,
+ gtk_ui_manager_get_widget(ui_manager_packet_list_menu, "/PacketListMenuPopup"));
+
+ popup_menu_list = g_slist_append((GSList *)popup_menu_list, ui_manager_packet_list_menu);
+
+
+ /* packet detail pop-up menu */
+ packet_list_details_action_group = gtk_action_group_new ("PacketListDetailsMenuPopUpActionGroup");
+
+ gtk_action_group_add_actions (packet_list_details_action_group, /* the action group */
+ (gpointer)tree_view_menu_popup_action_entries, /* an array of action descriptions */
+ G_N_ELEMENTS(tree_view_menu_popup_action_entries), /* the number of entries */
+ popup_menu_object); /* data to pass to the action callbacks */
+
+ ui_manager_tree_view_menu = gtk_ui_manager_new ();
+
+ gtk_ui_manager_insert_action_group (ui_manager_tree_view_menu,
+ packet_list_details_action_group,
+ 0); /* the position at which the group will be inserted. */
+ gtk_ui_manager_add_ui_from_string (ui_manager_tree_view_menu, ui_desc_tree_view_menu_popup, -1, &error);
+#if 0
+ /* If we want to load the treewiew popup UI description from file */
+ gui_desc_file_name_and_path = get_ui_file_path("tree-view-ui.xml");
+ gtk_ui_manager_add_ui_from_file ( ui_manager_tree_view_menu, gui_desc_file_name_and_path, &error);
+ g_free (gui_desc_file_name_and_path);
+#endif
+ if (error != NULL)
+ {
+ fprintf (stderr, "Warning: building TreeWiew Pop-Up menu failed: %s\n",
+ error->message);
+ g_error_free (error);
+ error = NULL;
+ }
+
+ g_object_set_data(G_OBJECT(popup_menu_object), PM_TREE_VIEW_KEY,
+ gtk_ui_manager_get_widget(ui_manager_tree_view_menu, "/TreeViewPopup"));
+
+ popup_menu_list = g_slist_append((GSList *)popup_menu_list, ui_manager_tree_view_menu);
+
+ /*
+ * Hex dump pop-up menu.
+ * We provide our own empty menu to suppress the default pop-up menu
+ * for text widgets.
+ */
+ packet_list_byte_menu_action_group = gtk_action_group_new ("PacketListByteMenuPopUpActionGroup");
+
+
+ gtk_action_group_add_radio_actions (packet_list_byte_menu_action_group, /* the action group */
+ (gpointer)bytes_menu_radio_action_entries, /* an array of radio action descriptions */
+ G_N_ELEMENTS(bytes_menu_radio_action_entries), /* the number of entries */
+ recent.gui_bytes_view, /* the value of the action to activate initially, or -1 if no action should be activated */
+ G_CALLBACK(select_bytes_view_cb), /* the callback to connect to the changed signal */
+ popup_menu_object); /* data to pass to the action callbacks */
+
+ ui_manager_bytes_menu = gtk_ui_manager_new ();
+
+ gtk_ui_manager_insert_action_group (ui_manager_bytes_menu,
+ packet_list_byte_menu_action_group,
+ 0); /* the position at which the group will be inserted. */
+ gtk_ui_manager_add_ui_from_string (ui_manager_bytes_menu, ui_desc_bytes_menu_popup, -1, &error);
+#if 0
+ /* If we want to load the bytesview poupup UI description from file */
+ gui_desc_file_name_and_path = get_ui_file_path("bytes-view-ui.xml");
+ gtk_ui_manager_add_ui_from_file ( ui_manager_bytes_menu, gui_desc_file_name_and_path, &error);
+ g_free (gui_desc_file_name_and_path);
+#endif
+ if (error != NULL)
+ {
+ fprintf (stderr, "Warning: building Bytes Pop-Up menu failed: %s\n",
+ error->message);
+ g_error_free (error);
+ error = NULL;
+ }
+ g_object_unref(packet_list_byte_menu_action_group);
+
+ g_object_set_data(G_OBJECT(popup_menu_object), PM_BYTES_VIEW_KEY,
+ gtk_ui_manager_get_widget(ui_manager_bytes_menu, "/BytesMenuPopup"));
+
+ popup_menu_list = g_slist_append((GSList *)popup_menu_list, ui_manager_bytes_menu);
+
+ /* main */
+ main_menu_bar_action_group = gtk_action_group_new ("MenuActionGroup");
+ gtk_action_group_add_actions (main_menu_bar_action_group, /* the action group */
+ main_menu_bar_entries, /* an array of action descriptions */
+ G_N_ELEMENTS(main_menu_bar_entries), /* the number of entries */
+ NULL); /* data to pass to the action callbacks */
+
+ gtk_action_group_add_toggle_actions(main_menu_bar_action_group, /* the action group */
+ main_menu_bar_toggle_action_entries, /* an array of action descriptions */
+ G_N_ELEMENTS(main_menu_bar_toggle_action_entries), /* the number of entries */
+ NULL); /* data to pass to the action callbacks */
+
+ gtk_action_group_add_radio_actions (main_menu_bar_action_group, /* the action group */
+ main_menu_bar_radio_view_time_entries, /* an array of radio action descriptions */
+ G_N_ELEMENTS(main_menu_bar_radio_view_time_entries), /* the number of entries */
+ recent.gui_time_format, /* the value of the action to activate initially, or -1 if no action should be activated */
+ G_CALLBACK(timestamp_format_new_cb), /* the callback to connect to the changed signal */
+ NULL); /* data to pass to the action callbacks */
+
+ gtk_action_group_add_radio_actions (main_menu_bar_action_group, /* the action group */
+ main_menu_bar_radio_view_time_fileformat_prec_entries, /* an array of radio action descriptions */
+ G_N_ELEMENTS(main_menu_bar_radio_view_time_fileformat_prec_entries), /* the number of entries */
+ recent.gui_time_precision, /* the value of the action to activate initially, or -1 if no action should be activated */
+ G_CALLBACK(timestamp_precision_new_cb), /* the callback to connect to the changed signal */
+ NULL); /* data to pass to the action callbacks */
+
+
+
+ ui_manager_main_menubar = gtk_ui_manager_new ();
+ gtk_ui_manager_insert_action_group (ui_manager_main_menubar, main_menu_bar_action_group, 0);
+#ifndef NEW_MENU_CODE
+
+ gtk_ui_manager_add_ui_from_string (ui_manager_main_menubar,ui_desc_menubar, -1, &error);
+#else
+ gui_desc_file_name_and_path = get_ui_file_path("main-menubar-ui.xml");
+ gtk_ui_manager_add_ui_from_file ( ui_manager_main_menubar, gui_desc_file_name_and_path, &error);
+ g_free (gui_desc_file_name_and_path);
+#endif
+ if (error != NULL)
+ {
+ fprintf (stderr, "Warning: building main menubar failed: %s\n",
+ error->message);
+ g_error_free (error);
+ error = NULL;
+ }
+ g_object_unref(main_menu_bar_action_group);
+ gtk_window_add_accel_group (GTK_WINDOW(top_level),
+ gtk_ui_manager_get_accel_group(ui_manager_main_menubar));
+
+
+ /* Add the recent files items to the menu
+ * use place holders and
+ * gtk_ui_manager_add_ui().
+ */
+ merge_id = gtk_ui_manager_new_merge_id (ui_manager_main_menubar);
+ add_recent_items (merge_id, ui_manager_main_menubar);
+
+ statusbar_profiles_action_group = gtk_action_group_new ("StatusBarProfilesPopUpMenuActionGroup");
+
+ gtk_action_group_add_actions (statusbar_profiles_action_group, /* the action group */
+ (gpointer)statusbar_profiles_menu_action_entries, /* an array of action descriptions */
+ G_N_ELEMENTS(statusbar_profiles_menu_action_entries), /* the number of entries */
+ popup_menu_object); /* data to pass to the action callbacks */
+
+ ui_manager_statusbar_profiles_menu = gtk_ui_manager_new ();
+ gtk_ui_manager_insert_action_group (ui_manager_statusbar_profiles_menu,
+ statusbar_profiles_action_group,
+ 0); /* the position at which the group will be inserted. */
+
+ gtk_ui_manager_add_ui_from_string (ui_manager_statusbar_profiles_menu,ui_statusbar_profiles_menu_popup, -1, &error);
+ if (error != NULL)
+ {
+ fprintf (stderr, "Warning: building Statusbar Profiles Pop-Up failed: %s\n",
+ error->message);
+ g_error_free (error);
+ error = NULL;
+ }
+
+ g_object_unref(statusbar_profiles_action_group);
+
+ g_object_set_data(G_OBJECT(popup_menu_object), PM_STATUSBAR_PROFILES_KEY,
+ gtk_ui_manager_get_widget(ui_manager_statusbar_profiles_menu, "/ProfilesMenuPopup"));
+
+ popup_menu_list = g_slist_append((GSList *)popup_menu_list, ui_manager_statusbar_profiles_menu);
+
+ menu_dissector_filter(&cfile);
+ /* Only Lua uses this currently. */
+ merge_lua_menu_items(merge_lua_menu_items_list);
+
+ /* Add external menus and items */
+ ws_menubar_build_external_menus();
+
+ /* Initialize enabled/disabled state of menu items */
+ set_menus_for_capture_file(NULL);
+#if 0
+ /* Un-#if this when we actually implement Cut/Copy/Paste.
+ Then make sure you enable them when they can be done. */
+ set_menu_sensitivity_old("/Edit/Cut", FALSE);
+ set_menu_sensitivity_old("/Edit/Copy", FALSE);
+ set_menu_sensitivity_old("/Edit/Paste", FALSE);
+#endif
+ /* Hide not usable menus */
+#ifndef WANT_PACKET_EDITOR
+ set_menu_visible(ui_manager_main_menubar, "/Menubar/EditMenu/EditPacket", FALSE);
+#endif /* WANT_PACKET_EDITOR */
+#ifndef HAVE_AIRPCAP
+ set_menu_visible(ui_manager_main_menubar, "/Menubar/ViewMenu/WirelessToolbar", FALSE);
+#endif /* HAVE_AIRPCAP */
+
+#ifndef HAVE_LIBPCAP
+ set_menu_visible(ui_manager_main_menubar, "/Menubar/CaptureMenu", FALSE);
+#endif
+ set_menus_for_captured_packets(FALSE);
+ set_menus_for_selected_packet(&cfile);
+ set_menus_for_selected_tree_row(&cfile);
+ set_menus_for_capture_in_progress(FALSE);
+ set_menus_for_file_set(/* dialog */TRUE, /* previous file */ FALSE, /* next_file */ FALSE);
+
+ }
+}
+
+/* Get a merge id for the menubar */
+void
+ws_add_build_menubar_items_callback(gpointer callback)
+{
+ build_menubar_items_callback_list = g_list_append(build_menubar_items_callback_list, callback);
+
+}
+
+static void
+ws_menubar_build_external_menus(void)
+{
+ void (*callback)(gpointer);
+
+ while(build_menubar_items_callback_list != NULL) {
+ callback = build_menubar_items_callback_list->data;
+ callback(ui_manager_main_menubar);
+ build_menubar_items_callback_list = g_list_next(build_menubar_items_callback_list);
+ }
+
+
+}
+
+typedef struct _menu_item {
+ const char *gui_path;
+ const char *name;
+ const char *stock_id;
+ const char *label;
+ const char *accelerator;
+ const gchar *tooltip;
+ GCallback callback;
+ gpointer callback_data;
+ gboolean enabled;
+ gboolean (*selected_packet_enabled)(frame_data *, epan_dissect_t *, gpointer callback_data);
+ gboolean (*selected_tree_row_enabled)(field_info *, gpointer callback_data);
+} menu_item_t;
+
+void register_lua_menu_bar_menu_items(
+ const char *gui_path,
+ const char *name,
+ const gchar *stock_id,
+ const char *label,
+ const char *accelerator,
+ const gchar *tooltip,
+ gpointer callback,
+ gpointer callback_data,
+ gboolean enabled,
+ gboolean (*selected_packet_enabled)(frame_data *, epan_dissect_t *, gpointer callback_data),
+ gboolean (*selected_tree_row_enabled)(field_info *, gpointer callback_data))
+{
+ menu_item_t *menu_item_data;
+
+ menu_item_data = g_malloc0(sizeof (menu_item_t));
+ menu_item_data->gui_path = gui_path;
+ menu_item_data->name = name;
+ menu_item_data->label = label;
+ menu_item_data->stock_id = stock_id;
+ menu_item_data->accelerator = accelerator;
+ menu_item_data->tooltip = tooltip;
+ menu_item_data->callback = callback;
+ menu_item_data->callback_data = callback_data;
+ menu_item_data->enabled = enabled;
+ menu_item_data->selected_packet_enabled = selected_packet_enabled;
+ menu_item_data->selected_tree_row_enabled = selected_tree_row_enabled;
+
+ merge_lua_menu_items_list = g_list_append(merge_lua_menu_items_list, menu_item_data);
+
+}
+
+#define XMENU_MAX_DEPTH (1 + 32) /* max number of menus in an xpath (+1 for Menubar) */
+#define XMENU_HEADER "<ui><menubar name='Menubar'>\n"
+#define XMENU_FOOTER "</menubar></ui>\n"
+
+/**
+ * Creates an XML string, containing a UI definition that can be merged
+ * with Wireshark's menu bar using gtk_ui_manager_add_ui_from_string().
+ * Free the returned string with g_free() when no longer needed.
+ *
+ * The last item in the path is treated as the menu item; all preceding path
+ * elements are the names of parent menus. Path elements are stripped of
+ * leading/trailing spaces.
+ *
+ * Examples:
+ * make_menu_xml("/Foo/Bar/I_tem");
+ * -->
+ * "<ui><menubar name='Menubar'>
+ * <menu action='Foo'>
+ * <menu action='Bar'>
+ * <menuitem action='I_tem'/> <!-- puts shortcut on 't' -->
+ * </menu>
+ * </menu>
+ * <menubar></ui>"
+ *
+ * make_menu_xml("/Foo/Bar/-/Baz/Item");
+ * -->
+ * "<ui><menubar name='Menubar'>
+ * <menu action='Foo'>
+ * <menu action='Bar'>
+ * <separator/>
+ * <menu action='Baz'>
+ * <menuitem action='Item'/>
+ * </menu>
+ * </menu>
+ * </menu>
+ * <menubar></ui>"
+ *
+ * http://developer.gnome.org/gtk/2.24/GtkUIManager.html#XML-UI
+ * http://developer.gnome.org/gtk/2.24/GtkUIManager.html#gtk-ui-manager-add-ui-from-string
+ */
+const gchar*
+make_menu_xml(const char *path) {
+ GString *xml;
+ char **p;
+ char **tokens;
+ const char *tok = path;
+ char *lbl;
+ gchar *markup;
+ guint num_menus;
+ size_t len;
+
+ if (path == NULL) return NULL;
+
+ xml = g_string_new(XMENU_HEADER);
+
+ /* no need to specify menu bar...skip it */
+ len = strlen("/Menubar");
+ if (g_ascii_strncasecmp(path, "/Menubar", len) == 0) {
+ path += len;
+ }
+
+ /* open nested menu tag for each path token */
+ num_menus = 0;
+ tokens = g_strsplit(path, "/", XMENU_MAX_DEPTH);
+ for (p = tokens; (p != NULL) && (*p != NULL); p++) {
+
+ tok = g_strstrip(*p);
+ if (tok[0] == '\0') continue;
+
+ /* reserve last token for menu-item processing */
+ if (*(p+1) == NULL) break;
+
+ if (g_strcmp0(tok, "-") == 0) {
+ xml = g_string_append(xml, "<separator/>\n");
+ } else {
+ /* strip label from token...it's used later in menu actions */
+ lbl = strchr(tok, '|');
+ if (lbl != NULL) *lbl++ = '\0';
+
+ markup = g_markup_printf_escaped("<menu action='%s'>\n", tok);
+ xml = g_string_append(xml, markup);
+ g_free(markup);
+ num_menus++;
+ }
+ }
+
+ /* Use the last path element as the name of the menu item. Allow blank
+ * menu name or else the menu is hidden (and thus useless). Showing a
+ * blank menu allows the developer to see the problem and fix it.
+ */
+ if ( (tok != NULL) /* && (tok[0] != '\0') */ ) {
+ if (g_strcmp0(tok, "-") == 0) {
+ xml = g_string_append(xml, "<separator/>\n");
+ } else {
+ /* strip label from token...it's used later in menu actions */
+ lbl = strchr(tok, '|');
+ if (lbl != NULL) *lbl++ = '\0';
+
+ /* append self-closing menu-item tag */
+ markup = g_markup_printf_escaped("<menuitem action='%s'/>\n", tok);
+ xml = g_string_append(xml, markup);
+ g_free(markup);
+ }
+ }
+
+ /* we just processed the last token, so free the list */
+ g_strfreev(tokens);
+
+ /* close all menu tags, and then append the footer */
+ for (; num_menus > 0; num_menus--) {
+ xml = g_string_append(xml, "</menu>");
+ }
+ xml = g_string_append(xml, XMENU_FOOTER);
+
+ /* free the GString object, return the allocated char buf which must be g_freed */
+ markup = g_string_free(xml, FALSE);
+ /* printf("Lua Menu XML:\n%s\n", markup); */
+
+ return markup;
+}
+
+/**
+ * Creates an action group for the menu items in xpath, and returns it. The caller should
+ * use g_object_unref() on the returned pointer if transferring scope.
+ */
+#ifdef HAVE_LUA_5_1
+/* NOTE currently only used from Lua, remove this ifdef when used
+ outside of #ifdef LUA */
+static GtkActionGroup*
+make_menu_actions(const char *path, const menu_item_t *menu_item_data) {
+ GtkActionGroup *action_group;
+ GtkAction *action;
+ char **p;
+ char **tokens;
+ char *lbl;
+ const char *tok = path;
+
+ action_group = gtk_action_group_new (path);
+
+ tokens = g_strsplit(path, "/", XMENU_MAX_DEPTH);
+ for (p = tokens; (p != NULL) && (*p != NULL); p++) {
+
+ tok = g_strstrip(*p);
+
+ if (tok[0] == '\0') continue;
+
+ /* reserve last token for item name */
+ if ( *(p+1) == NULL ) break;
+
+ if (g_strcmp0(tok, "-") != 0) {
+
+ /* parse label from token */
+ lbl = strchr(tok, '|');
+ if (lbl != NULL) {
+ *lbl++ = '\0';
+ }
+ if ((lbl == NULL) || (*lbl == '\0')) {
+ lbl = (char*)tok;
+ }
+
+ action = g_object_new (
+ GTK_TYPE_ACTION,
+ "name", tok,
+ "label", lbl,
+ NULL
+ );
+ gtk_action_group_add_action (action_group, action);
+ g_object_unref (action);
+ }
+ }
+
+ /* handle menu item (blank names ok) */
+ if ( (tok != NULL) /* && (tok[0] != '\0') */ && (menu_item_data != NULL) ) {
+
+ /* parse label from token */
+ lbl = strchr(tok, '|');
+ if (lbl != NULL) {
+ *lbl++ = '\0';
+ }
+ if ((lbl == NULL) || (*lbl == '\0')) {
+ lbl = (char*)tok;
+ }
+
+ action = g_object_new (
+ GTK_TYPE_ACTION,
+ "name", tok,
+ "label", lbl,
+ "stock-id", menu_item_data->stock_id,
+ "tooltip", menu_item_data->tooltip,
+ "sensitive", menu_item_data->enabled,
+ NULL
+ );
+ if (menu_item_data->callback != NULL) {
+ g_signal_connect (
+ action,
+ "activate",
+ G_CALLBACK (menu_item_data->callback),
+ menu_item_data->callback_data
+ );
+ }
+ gtk_action_group_add_action (action_group, action);
+ g_object_unref (action);
+ }
+
+ /* we just processed the last token, so free the list */
+ g_strfreev(tokens);
+
+ return action_group;
+}
+#endif
+
+static void
+merge_lua_menu_items(GList *merge_lua_menu_items_list _U_)
+{
+#ifdef HAVE_LUA_5_1
+ guint merge_id;
+ GtkActionGroup *action_group;
+ menu_item_t *menu_item_data;
+ GError *err;
+ const gchar *xml;
+ gchar *xpath;
+
+ while(merge_lua_menu_items_list != NULL) {
+ menu_item_data = merge_lua_menu_items_list->data;
+ xpath = g_strdup_printf("%s/%s", menu_item_data->gui_path, menu_item_data->name);
+
+ xml = make_menu_xml(xpath);
+ if (xml != NULL) {
+
+ /* create action group for menu elements */
+ action_group = make_menu_actions(xpath, menu_item_data);
+ gtk_ui_manager_insert_action_group (ui_manager_main_menubar, action_group, 0);
+
+ /* add menu elements to menu bar */
+ err = NULL;
+ merge_id = gtk_ui_manager_add_ui_from_string (ui_manager_main_menubar, xml, -1, &err);
+ if (err != NULL) {
+ fprintf (stderr, "Warning: building Lua menus failed: %s\n",
+ err->message);
+ g_error_free (err);
+
+ /* undo the mess */
+ gtk_ui_manager_remove_ui (ui_manager_main_menubar, merge_id);
+ gtk_ui_manager_remove_action_group (ui_manager_main_menubar, action_group);
+ }
+ g_free ((gchar*)xml);
+ g_object_unref (action_group);
+ }
+
+ g_free(xpath);
+ merge_lua_menu_items_list = g_list_next(merge_lua_menu_items_list);
+ }
+#endif
+}
+
+
+/*
+ * Enable/disable menu sensitivity.
+ */
+static void
+set_menu_sensitivity(GtkUIManager *ui_manager, const gchar *path, gint val)
+{
+ GtkAction *action;
+
+ action = gtk_ui_manager_get_action(ui_manager, path);
+ if(!action){
+#if 0
+ fprintf (stderr, "Warning: set_menu_sensitivity couldn't find action path= %s\n",
+ path);
+#endif
+ return;
+ }
+ gtk_action_set_sensitive (action, val); /* TRUE to make the action sensitive */
+}
+
+static void
+set_menu_visible(GtkUIManager *ui_manager, const gchar *path, gint val)
+{
+ GtkAction *action;
+
+ action = gtk_ui_manager_get_action(ui_manager, path);
+ if(!action){
+ fprintf (stderr, "Warning: set_menu_visible couldn't find action path= %s\n",
+ path);
+ return;
+ }
+ gtk_action_set_visible (action, val); /* TRUE to make the action visible */
+}
+
+
+static void
+set_menu_object_data_meat(GtkUIManager *ui_manager, const gchar *path, const gchar *key, gpointer data)
+{
+ GtkWidget *menu = NULL;
+
+ if ((menu = gtk_ui_manager_get_widget(ui_manager, path)) != NULL){
+ g_object_set_data(G_OBJECT(menu), key, data);
+ }else{
+#if 0
+ g_warning("set_menu_object_data_meat: no menu, path: %s",path);
+#endif
+ }
+}
+
+void
+set_menu_object_data (const gchar *path, const gchar *key, gpointer data) {
+ if (strncmp (path,"/Menubar",8) == 0){
+ set_menu_object_data_meat(ui_manager_main_menubar, path, key, data);
+ }else if (strncmp (path,"/PacketListMenuPopup",20) == 0){
+ set_menu_object_data_meat(ui_manager_packet_list_menu, path, key, data);
+ }else if (strncmp (path,"/TreeViewPopup",14) == 0){
+ set_menu_object_data_meat(ui_manager_tree_view_menu, path, key, data);
+ }else if (strncmp (path,"/BytesMenuPopup",15) == 0){
+ set_menu_object_data_meat(ui_manager_bytes_menu, path, key, data);
+ }else if (strncmp (path,"/ProfilesMenuPopup",18) == 0){
+ set_menu_object_data_meat(ui_manager_statusbar_profiles_menu, path, key, data);
+ }
+}
+
+
+/* Recently used capture files submenu:
+ * Submenu containing the recently used capture files.
+ * The capture filenames are always kept with the absolute path, to be independant
+ * of the current path.
+ * They are only stored inside the labels of the submenu (no separate list). */
+
+#define MENU_RECENT_FILES_PATH "/Menubar/FileMenu/OpenRecent"
+#define MENU_RECENT_FILES_KEY "Recent File Name"
+
+/* Add a file name to the top of the list, if its allrady present remove it first */
+static GList *
+remove_present_file_name(GList *recent_files_list, const gchar *cf_name){
+GList *li;
+gchar *widget_cf_name;
+
+ for (li = g_list_first(recent_files_list); li; li = li->next) {
+ widget_cf_name = li->data;
+ if (
+#ifdef _WIN32
+ /* do a case insensitive compare on win32 */
+ g_ascii_strncasecmp(widget_cf_name, cf_name, 1000) == 0){
+#else /* _WIN32 */
+ /* do a case sensitive compare on unix */
+ strncmp(widget_cf_name, cf_name, 1000) == 0 ){
+#endif
+ recent_files_list = g_list_remove(recent_files_list,widget_cf_name);
+ }
+ }
+
+ return recent_files_list;
+}
+
+static void
+recent_changed_cb (GtkUIManager *ui_manager,
+ gpointer user_data _U_)
+{
+ guint merge_id;
+ GList *action_groups, *l;
+
+
+ merge_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (ui_manager),
+ "recent-files-merge-id"));
+
+ /* remove the UI */
+ gtk_ui_manager_remove_ui (ui_manager, merge_id);
+
+ /* remove the action group; gtk_ui_manager_remove_action_group()
+ * should really take the action group's name instead of its
+ * pointer.
+ */
+ action_groups = gtk_ui_manager_get_action_groups (ui_manager);
+ for (l = action_groups; l != NULL; l = l->next)
+ {
+ GtkActionGroup *group = l->data;
+
+ if (strcmp (gtk_action_group_get_name (group), "recent-files-group") == 0){
+ /* this unrefs the action group and all of its actions */
+ gtk_ui_manager_remove_action_group (ui_manager, group);
+ break;
+ }
+ }
+
+ /* generate a new merge id and re-add everything */
+ merge_id = gtk_ui_manager_new_merge_id (ui_manager);
+ add_recent_items (merge_id, ui_manager);
+}
+
+static void
+recent_clear_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ GtkWidget *submenu_recent_files;
+ GList *recent_files_list;
+
+ /* Get the list of recent files, free the list and store the empty list with the widget */
+ submenu_recent_files = gtk_ui_manager_get_widget(ui_manager_main_menubar, MENU_RECENT_FILES_PATH);
+ recent_files_list = g_object_get_data(G_OBJECT(submenu_recent_files), "recent-files-list");
+ /* Free the name strings ?? */
+ g_list_free(recent_files_list);
+ recent_files_list = NULL;
+ g_object_set_data(G_OBJECT(submenu_recent_files), "recent-files-list", recent_files_list);
+ /* Calling recent_changed_cb will rebuild the GUI call add_recent_items which will in turn call
+ * main_welcome_reset_recent_capture_files
+ */
+ recent_changed_cb(ui_manager_main_menubar, NULL);
+}
+
+static void
+add_recent_items (guint merge_id, GtkUIManager *ui_manager)
+{
+ GtkActionGroup *action_group;
+ GtkAction *action;
+ GtkWidget *submenu_recent_files;
+ GList *items, *l;
+ gchar *action_name;
+ guint i;
+
+ /* Reset the recent files list in the welcome screen */
+ main_welcome_reset_recent_capture_files();
+
+ action_group = gtk_action_group_new ("recent-files-group");
+
+ submenu_recent_files = gtk_ui_manager_get_widget(ui_manager_main_menubar, MENU_RECENT_FILES_PATH);
+ if(!submenu_recent_files){
+ g_warning("add_recent_items: No submenu_recent_files found, path= MENU_RECENT_FILES_PATH");
+ }
+ items = g_object_get_data(G_OBJECT(submenu_recent_files), "recent-files-list");
+
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ g_object_set_data (G_OBJECT (ui_manager),
+ "recent-files-merge-id", GUINT_TO_POINTER (merge_id));
+
+ /* no items */
+ if (!items){
+
+ action = g_object_new (GTK_TYPE_ACTION,
+ "name", "recent-info-empty",
+ "label", "No recently used files",
+ "sensitive", FALSE,
+ NULL);
+ gtk_action_group_add_action (action_group, action);
+ gtk_action_set_sensitive(action, FALSE);
+ g_object_unref (action);
+
+ gtk_ui_manager_add_ui (ui_manager, merge_id,
+ "/Menubar/FileMenu/OpenRecent/RecentFiles",
+ "recent-info-empty",
+ "recent-info-empty",
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+
+ return;
+ }
+
+ for (i = 0, l = items;
+ i < prefs.gui_recent_files_count_max && l != NULL;
+ i +=1, l = l->next)
+ {
+ gchar *item_name = l->data;
+ action_name = g_strdup_printf ("recent-info-%u", i);
+
+ action = g_object_new (GTK_TYPE_ACTION,
+ "name", action_name,
+ "label", item_name,
+ "stock_id", WIRESHARK_STOCK_FILE,
+ NULL);
+ g_signal_connect (action, "activate",
+ G_CALLBACK (menu_open_recent_file_cmd_cb), NULL);
+#if !GTK_CHECK_VERSION(2,16,0)
+ g_object_set_data (G_OBJECT (action), "FileName", item_name);
+#endif
+ gtk_action_group_add_action (action_group, action);
+ g_object_unref (action);
+
+ gtk_ui_manager_add_ui (ui_manager, merge_id,
+ "/Menubar/FileMenu/OpenRecent/RecentFiles",
+ action_name,
+ action_name,
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+
+ /* Add the file name to the recent files list on the Welcome screen */
+ main_welcome_add_recent_capture_file(item_name, G_OBJECT(action));
+
+ g_free (action_name);
+ }
+ /* Add a Separator */
+ gtk_ui_manager_add_ui (ui_manager, merge_id,
+ "/Menubar/FileMenu/OpenRecent/RecentFiles",
+ "separator-recent-info",
+ NULL,
+ GTK_UI_MANAGER_SEPARATOR,
+ FALSE);
+
+ /* Add a clear Icon */
+ action = g_object_new (GTK_TYPE_ACTION,
+ "name", "clear-recent-info",
+ "label", "Clear the recent files list",
+ "stock_id", GTK_STOCK_CLEAR,
+ NULL);
+
+ g_signal_connect (action, "activate",
+ G_CALLBACK (recent_clear_cb), NULL);
+
+ gtk_action_group_add_action (action_group, action);
+ g_object_unref (action);
+
+ gtk_ui_manager_add_ui (ui_manager, merge_id,
+ "/Menubar/FileMenu/OpenRecent/RecentFiles",
+ "clear-recent-info",
+ "clear-recent-info",
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+
+}
+
+
+/* Open a file by it's name
+ (Beware: will not ask to close existing capture file!) */
+void
+menu_open_filename(gchar *cf_name)
+{
+ GtkWidget *submenu_recent_files;
+ int err;
+ GList *recent_files_list;
+
+
+ submenu_recent_files = gtk_ui_manager_get_widget(ui_manager_main_menubar, MENU_RECENT_FILES_PATH);
+ if(!submenu_recent_files){
+ g_warning("menu_open_filename: No submenu_recent_files found, path= MENU_RECENT_FILES_PATH");
+ }
+ recent_files_list = g_object_get_data(G_OBJECT(submenu_recent_files), "recent-files-list");
+ /* XXX: ask user to remove item, it's maybe only a temporary problem */
+ /* open and read the capture file (this will close an existing file) */
+ if (cf_open(&cfile, cf_name, FALSE, &err) == CF_OK) {
+ cf_read(&cfile, FALSE);
+ }else{
+ recent_files_list = remove_present_file_name(recent_files_list, cf_name);
+ g_object_set_data(G_OBJECT(submenu_recent_files), "recent-files-list", recent_files_list);
+ /* Calling recent_changed_cb will rebuild the GUI call add_recent_items which will in turn call
+ * main_welcome_reset_recent_capture_files
+ */
+ recent_changed_cb(ui_manager_main_menubar, NULL);
+ }
+}
+
+/* callback, if the user pushed a recent file submenu item */
+void
+menu_open_recent_file_cmd(gpointer action)
+{
+ GtkWidget *submenu_recent_files;
+ GList *recent_files_list;
+ const gchar *cf_name;
+ int err;
+
+#if GTK_CHECK_VERSION(2,16,0)
+ cf_name = gtk_action_get_label(action);
+#else
+ cf_name = g_object_get_data(G_OBJECT(action), "FileName");
+#endif
+
+ /* open and read the capture file (this will close an existing file) */
+ if (cf_open(&cfile, cf_name, FALSE, &err) == CF_OK) {
+ cf_read(&cfile, FALSE);
+ } else {
+ submenu_recent_files = gtk_ui_manager_get_widget(ui_manager_main_menubar, MENU_RECENT_FILES_PATH);
+ recent_files_list = g_object_get_data(G_OBJECT(submenu_recent_files), "recent-files-list");
+
+ recent_files_list = remove_present_file_name(recent_files_list, cf_name);
+ g_object_set_data(G_OBJECT(submenu_recent_files), "recent-files-list", recent_files_list);
+ /* Calling recent_changed_cb will rebuild the GUI call add_recent_items which will in turn call
+ * main_welcome_reset_recent_capture_files
+ */
+ recent_changed_cb(ui_manager_main_menubar, NULL);
+ }
+}
+
+static void menu_open_recent_file_answered_cb(gpointer dialog _U_, gint btn, gpointer data)
+{
+ switch(btn) {
+ case(ESD_BTN_YES):
+ /* save file first */
+ file_save_as_cmd(after_save_open_recent_file, data, FALSE);
+ break;
+ case(ESD_BTN_NO):
+ cf_close(&cfile);
+ menu_open_recent_file_cmd(data);
+ break;
+ case(ESD_BTN_CANCEL):
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+static void
+menu_open_recent_file_cmd_cb(GtkAction *action, gpointer data _U_) {
+ gpointer dialog;
+
+
+ if((cfile.state != FILE_CLOSED) && !cfile.user_saved && prefs.gui_ask_unsaved) {
+ /* user didn't saved his current file, ask him */
+ dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_YES_NO_CANCEL,
+ "%sSave capture file before opening a new one?%s\n\n"
+ "If you open a new capture file without saving, your current capture data will be discarded.",
+ simple_dialog_primary_start(), simple_dialog_primary_end());
+ simple_dialog_set_cb(dialog, menu_open_recent_file_answered_cb, action);
+ } else {
+ /* unchanged file */
+ menu_open_recent_file_cmd(action);
+ }
+}
+
+static void
+add_menu_recent_capture_file_absolute(gchar *cf_name) {
+ GtkWidget *submenu_recent_files;
+ GList *li;
+ gchar *widget_cf_name;
+ gchar *normalized_cf_name;
+ guint cnt;
+ GList *recent_files_list;
+
+ normalized_cf_name = g_strdup(cf_name);
+#ifdef _WIN32
+ /* replace all slashes by backslashes */
+ g_strdelimit(normalized_cf_name, "/", '\\');
+#endif
+
+ /* get the submenu container item */
+ submenu_recent_files = gtk_ui_manager_get_widget(ui_manager_main_menubar, MENU_RECENT_FILES_PATH);
+ if(!submenu_recent_files){
+ g_warning("add_menu_recent_capture_file_absolute: No submenu_recent_files found, path= MENU_RECENT_FILES_PATH");
+ return;
+ }
+ recent_files_list = g_object_get_data(G_OBJECT(submenu_recent_files), "recent-files-list");
+ cnt = 1;
+ for (li = g_list_first(recent_files_list); li; li = li->next, cnt++) {
+ widget_cf_name = li->data;
+ if (
+#ifdef _WIN32
+ /* do a case insensitive compare on win32 */
+ g_ascii_strncasecmp(widget_cf_name, normalized_cf_name, 1000) == 0 ||
+#else /* _WIN32 */
+ /* do a case sensitive compare on unix */
+ strncmp(widget_cf_name, normalized_cf_name, 1000) == 0 ||
+#endif
+ cnt >= prefs.gui_recent_files_count_max) {
+ recent_files_list = g_list_remove(recent_files_list,widget_cf_name);
+ cnt--;
+ }
+ }
+ recent_files_list = g_list_prepend(recent_files_list, normalized_cf_name);
+ g_object_set_data(G_OBJECT(submenu_recent_files), "recent-files-list", recent_files_list);
+ recent_changed_cb( ui_manager_main_menubar, NULL);
+}
+
+
+/* add the capture filename to the "Recent Files" menu */
+/* (will change nothing, if this filename is already in the menu) */
+/*
+ * XXX - We might want to call SHAddToRecentDocs under Windows 7:
+ * http://stackoverflow.com/questions/437212/how-do-you-register-a-most-recently-used-list-with-windows-in-preparation-for-win
+ */
+void
+add_menu_recent_capture_file(gchar *cf_name) {
+ gchar *curr;
+ gchar *absolute;
+
+
+ /* if this filename is an absolute path, we can use it directly */
+ if (g_path_is_absolute(cf_name)) {
+ add_menu_recent_capture_file_absolute(cf_name);
+ return;
+ }
+
+ /* this filename is not an absolute path, prepend the current dir */
+ curr = g_get_current_dir();
+ absolute = g_strdup_printf("%s%s%s", curr, G_DIR_SEPARATOR_S, cf_name);
+ add_menu_recent_capture_file_absolute(absolute);
+ g_free(curr);
+ g_free(absolute);
+}
+
+
+/* write all capture filenames of the menu to the user's recent file */
+void
+menu_recent_file_write_all(FILE *rf) {
+ GtkWidget *submenu_recent_files;
+ GList *children;
+ GList *child;
+ gchar *cf_name;
+ GList *recent_files_list, *list;
+
+ submenu_recent_files = gtk_ui_manager_get_widget(ui_manager_main_menubar, MENU_RECENT_FILES_PATH);
+ if(!submenu_recent_files){
+ g_warning("menu_recent_file_write_all: No submenu_recent_files found, path= MENU_RECENT_FILES_PATH");
+ }
+ recent_files_list = g_object_get_data(G_OBJECT(submenu_recent_files), "recent-files-list");
+ list = g_list_last(recent_files_list);
+ while(list != NULL) {
+ cf_name = list->data;
+ if (cf_name) {
+ if(u3_active())
+ fprintf (rf, RECENT_KEY_CAPTURE_FILE ": %s\n", u3_contract_device_path(cf_name));
+ else
+ fprintf (rf, RECENT_KEY_CAPTURE_FILE ": %s\n", cf_name);
+ }
+ list = g_list_previous(list);
+ }
+ g_list_free(recent_files_list);
+ return;
+
+ /* we have to iterate backwards through the children's list,
+ * so we get the latest item last in the file.
+ * (don't use gtk_container_foreach() here, it will return the wrong iteration order) */
+ children = gtk_container_get_children(GTK_CONTAINER(submenu_recent_files));
+ child = g_list_last(children);
+ while(child != NULL) {
+ /* get capture filename from the menu item label */
+ cf_name = g_object_get_data(G_OBJECT(child->data), MENU_RECENT_FILES_KEY);
+ if (cf_name) {
+ if(u3_active())
+ fprintf (rf, RECENT_KEY_CAPTURE_FILE ": %s\n", u3_contract_device_path(cf_name));
+ else
+ fprintf (rf, RECENT_KEY_CAPTURE_FILE ": %s\n", cf_name);
+ }
+
+ child = g_list_previous(child);
+ }
+
+ g_list_free(children);
+}
+
+void
+menu_name_resolution_changed(void)
+{
+ GtkWidget *menu = NULL;
+ menu = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/NameResolution/EnableforMACLayer");
+ if(!menu){
+ g_warning("menu_name_resolution_changed: No menu found, path= /Menubar/ViewMenu/NameResolution/EnableforMACLayer");
+ }
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), gbl_resolv_flags & RESOLV_MAC);
+
+ menu = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/NameResolution/EnableforNetworkLayer");
+ if(!menu){
+ g_warning("menu_name_resolution_changed: No menu found, path= /Menubar/ViewMenu/NameResolution/EnableforNetworkLayer");
+ }
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), gbl_resolv_flags & RESOLV_NETWORK);
+
+ menu = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/NameResolution/EnableforTransportLayer");
+ if(!menu){
+ g_warning("menu_name_resolution_changed: No menu found, path= /Menubar/ViewMenu/NameResolution/EnableforTransportLayer");
+ }
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), gbl_resolv_flags & RESOLV_TRANSPORT);
+
+}
+
+static void
+name_resolution_cb(GtkWidget *w, gpointer d _U_, gint action)
+{
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w))) {
+ gbl_resolv_flags |= action;
+ } else {
+ gbl_resolv_flags &= ~action;
+ }
+}
+
+void
+menu_auto_scroll_live_changed(gboolean auto_scroll_live_in) {
+ GtkWidget *menu;
+
+
+ /* tell menu about it */
+ menu = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/AutoScrollinLiveCapture");
+ if(!menu){
+ g_warning("menu_auto_scroll_live_changed: No menu found, path= /Menubar/ViewMenu/AutoScrollinLiveCapture");
+ }
+ if( ((gboolean) gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu)) != auto_scroll_live_in) ) {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), auto_scroll_live_in);
+ }
+
+#ifdef HAVE_LIBPCAP
+ /* tell toolbar about it */
+ toolbar_auto_scroll_live_changed(auto_scroll_live_in);
+
+ /* change auto scroll */
+ if(auto_scroll_live_in != auto_scroll_live) {
+ auto_scroll_live = auto_scroll_live_in;
+ }
+#endif /*HAVE_LIBPCAP */
+}
+
+
+
+
+void
+menu_colorize_changed(gboolean packet_list_colorize) {
+ GtkWidget *menu;
+
+
+ /* tell menu about it */
+ menu = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/ColorizePacketList");
+ if(!menu){
+ g_warning("menu_colorize_changed: No menu found, path= /Menubar/ViewMenu/ColorizePacketList");
+ }
+ if( (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu)) != packet_list_colorize) ) {
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), packet_list_colorize);
+ }
+
+ /* tell toolbar about it */
+ toolbar_colorize_changed(packet_list_colorize);
+
+ /* change colorization */
+ if(packet_list_colorize != recent.packet_list_colorize) {
+ recent.packet_list_colorize = packet_list_colorize;
+ color_filters_enable(packet_list_colorize);
+ new_packet_list_colorize_packets();
+ }
+}
+
+static void
+colorize_cb(GtkWidget *w, gpointer d _U_)
+{
+ menu_colorize_changed(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(w)));
+}
+
+
+/* the recent file read has finished, update the menu corresponding */
+void
+menu_recent_read_finished(void) {
+ GtkWidget *menu = NULL;
+
+ menu = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/MainToolbar");
+ if(!menu){
+ g_warning("menu_recent_read_finished: No menu found, path= /Menubar/ViewMenu/MainToolbar");
+ }else{
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), recent.main_toolbar_show);
+ }
+ menu = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/FilterToolbar");
+ if(!menu){
+ g_warning("menu_recent_read_finished: No menu found, path= /Menubar/ViewMenu/FilterToolbar");
+ }else{
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), recent.filter_toolbar_show);
+ };
+#ifdef HAVE_AIRPCAP
+ menu = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/WirelessToolbar");
+ if(!menu){
+ g_warning("menu_recent_read_finished: No menu found, path= /Menubar/ViewMenu/WirelessToolbar");
+ }else{
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), recent.airpcap_toolbar_show);
+ }
+#endif /* HAVE_AIRPCAP */
+
+ /* Fix me? */
+ menu = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/Statusbar");
+ if(!menu){
+ g_warning("menu_recent_read_finished: No menu found, path= /Menubar/ViewMenu/Statusbar");
+ }else{
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), recent.statusbar_show);
+ }
+
+ menu = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/PacketList");
+ if(!menu){
+ g_warning("menu_recent_read_finished: No menu found, path= /Menubar/ViewMenu/PacketList");
+ }else{
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), recent.packet_list_show);
+ }
+
+ menu = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/PacketDetails");
+ if(!menu){
+ g_warning("menu_recent_read_finished: No menu found, path= /Menubar/ViewMenu/PacketDetails");
+ }else{
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), recent.tree_view_show);
+ }
+
+ menu = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/PacketBytes");
+ if(!menu){
+ g_warning("menu_recent_read_finished: No menu found, path= /Menubar/ViewMenu/PacketBytes");
+ }else{
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), recent.byte_view_show);
+ }
+
+ menu = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/ColorizePacketList");
+ if(!menu){
+ g_warning("menu_recent_read_finished: No menu found, path= /Menubar/ViewMenu/ColorizePacketList");
+ }else{
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), recent.packet_list_colorize);
+ }
+
+ menu_name_resolution_changed();
+
+#ifdef HAVE_LIBPCAP
+ menu = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/AutoScrollinLiveCapture");
+ if(!menu){
+ g_warning("menu_recent_read_finished: No menu found, path= /Menubar/ViewMenu/AutoScrollinLiveCapture");
+ }else{
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), auto_scroll_live);
+ }
+#endif
+ main_widgets_rearrange();
+
+ /* don't change the time format, if we had a command line value */
+ if (timestamp_get_type() != TS_NOT_SET) {
+ recent.gui_time_format = timestamp_get_type();
+ }
+
+ /* XXX Fix me */
+ timestamp_set_type(recent.gui_time_format);
+ /* This call adjusts column width */
+ cf_timestamp_auto_precision(&cfile);
+ new_packet_list_queue_draw();
+ /* the actual precision will be set in new_packet_list_queue_draw() below */
+ if (recent.gui_time_precision == TS_PREC_AUTO) {
+ timestamp_set_precision(TS_PREC_AUTO_SEC);
+ } else {
+ timestamp_set_precision(recent.gui_time_precision);
+ }
+ /* This call adjusts column width */
+ cf_timestamp_auto_precision(&cfile);
+ new_packet_list_queue_draw();
+
+ /* don't change the seconds format, if we had a command line value */
+ if (timestamp_get_seconds_type() != TS_SECONDS_NOT_SET) {
+ recent.gui_seconds_format = timestamp_get_seconds_type();
+ }
+ menu = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/TimeDisplayFormat/DisplaySecondsWithHoursAndMinutes");
+ if(!menu){
+ g_warning("menu_recent_read_finished: No menu found, path= /Menubar/ViewMenu/TimeDisplayFormat/DisplaySecondsWithHoursAndMinutes");
+ }
+
+ switch (recent.gui_seconds_format) {
+ case TS_SECONDS_DEFAULT:
+ recent.gui_seconds_format = -1;
+ /* set_active will not trigger the callback when deactivating an inactive item! */
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), TRUE);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), FALSE);
+ break;
+ case TS_SECONDS_HOUR_MIN_SEC:
+ recent.gui_seconds_format = -1;
+ /* set_active will not trigger the callback when activating an active item! */
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), FALSE);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), TRUE);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ menu_colorize_changed(recent.packet_list_colorize);
+}
+
+
+gboolean
+popup_menu_handler(GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ GtkWidget *menu = (GtkWidget *)data;
+ GdkEventButton *event_button = NULL;
+ gint row, column;
+
+ if(widget == NULL || event == NULL || data == NULL) {
+ return FALSE;
+ }
+
+ /*
+ * If we ever want to make the menu differ based on what row
+ * and/or column we're above, we'd use "eth_clist_get_selection_info()"
+ * to find the row and column number for the coordinates; a CTree is,
+ * I guess, like a CList with one column(?) and the expander widget
+ * as a pixmap.
+ */
+ /* Check if we are on packet_list object */
+ if (widget == g_object_get_data(G_OBJECT(popup_menu_object), E_MPACKET_LIST_KEY) &&
+ ((GdkEventButton *)event)->button != 1) {
+ gint physical_row;
+ if (new_packet_list_get_event_row_column((GdkEventButton *)event, &physical_row, &row, &column)) {
+ g_object_set_data(G_OBJECT(popup_menu_object), E_MPACKET_LIST_ROW_KEY,
+ GINT_TO_POINTER(row));
+ g_object_set_data(G_OBJECT(popup_menu_object), E_MPACKET_LIST_COL_KEY,
+ GINT_TO_POINTER(column));
+ new_packet_list_set_selected_row(row);
+ }
+ }
+
+ /* Check if we are on tree_view object */
+ if (widget == tree_view_gbl) {
+ tree_view_select(widget, (GdkEventButton *) event);
+ }
+
+ /* context menu handler */
+ if(event->type == GDK_BUTTON_PRESS) {
+ event_button = (GdkEventButton *) event;
+
+ /* To quote the "Gdk Event Structures" doc:
+ * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
+ if(event_button->button == 3) {
+ gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
+ event_button->button,
+ event_button->time);
+ g_signal_stop_emission_by_name(widget, "button_press_event");
+ return TRUE;
+ }
+ }
+
+ /* Check if we are on byte_view object */
+ if(widget == get_notebook_bv_ptr(byte_nb_ptr_gbl)) {
+ byte_view_select(widget, (GdkEventButton *) event);
+ }
+
+ /* GDK_2BUTTON_PRESS is a doubleclick -> expand/collapse tree row */
+ /* GTK version 1 seems to be doing this automatically */
+ if (widget == tree_view_gbl && event->type == GDK_2BUTTON_PRESS) {
+ GtkTreePath *path;
+
+ if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
+ (gint) (((GdkEventButton *)event)->x),
+ (gint) (((GdkEventButton *)event)->y),
+ &path, NULL, NULL, NULL))
+ {
+ if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(widget), path))
+ gtk_tree_view_collapse_row(GTK_TREE_VIEW(widget), path);
+ else
+ gtk_tree_view_expand_row(GTK_TREE_VIEW(widget), path,
+ FALSE);
+ gtk_tree_path_free(path);
+ }
+ }
+ return FALSE;
+}
+
+/* Enable or disable menu items based on whether you have a capture file
+ you've finished reading and, if you have one, whether it's been saved
+ and whether it could be saved except by copying the raw packet data. */
+void
+set_menus_for_capture_file(capture_file *cf)
+{
+ if (cf == NULL) {
+ /* We have no capture file */
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/Merge", FALSE);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/Close", FALSE);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/Save", FALSE);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/SaveAs", FALSE);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/Export", FALSE);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/Reload", FALSE);
+ set_toolbar_for_capture_file(FALSE);
+ set_toolbar_for_unsaved_capture_file(FALSE);
+ } else {
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/Merge", TRUE);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/Close", TRUE);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/Save", !cf->user_saved);
+ /*
+ * "Save As..." works only if we can write the file out in at least
+ * one format (so we can save the whole file or just a subset) or
+ * if we have an unsaved capture (so writing the whole file out
+ * with a raw data copy makes sense).
+ */
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/SaveAs",
+ cf_can_save_as(cf) || !cf->user_saved);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/Export", TRUE);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/Reload", TRUE);
+ set_toolbar_for_unsaved_capture_file(!cf->user_saved);
+ set_toolbar_for_capture_file(TRUE);
+ }
+}
+
+/* Enable or disable menu items based on whether there's a capture in
+ progress. */
+void
+set_menus_for_capture_in_progress(gboolean capture_in_progress)
+{
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/Open",
+ !capture_in_progress);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/OpenRecent",
+ !capture_in_progress);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/Export",
+ capture_in_progress);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/Set",
+ !capture_in_progress);
+ set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortAscending",
+ !capture_in_progress);
+ set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/SortDescending",
+ !capture_in_progress);
+ set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/NoSorting",
+ !capture_in_progress);
+
+#ifdef HAVE_LIBPCAP
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/CaptureMenu/Options",
+ !capture_in_progress);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/CaptureMenu/Start",
+ !capture_in_progress);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/CaptureMenu/Stop",
+ capture_in_progress);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/CaptureMenu/Restart",
+ capture_in_progress);
+ set_toolbar_for_capture_in_progress(capture_in_progress);
+
+ set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
+#endif /* HAVE_LIBPCAP */
+}
+
+
+void
+set_menus_for_captured_packets(gboolean have_captured_packets)
+{
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/Print",
+ have_captured_packets);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Print",
+ have_captured_packets);
+
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/FindPacket",
+ have_captured_packets);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/FindNext",
+ have_captured_packets);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/FindPrevious",
+ have_captured_packets);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ZoomIn",
+ have_captured_packets);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ZoomOut",
+ have_captured_packets);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/NormalSize",
+ have_captured_packets);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/Goto",
+ have_captured_packets);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/PreviousPacket",
+ have_captured_packets);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/NextPacket",
+ have_captured_packets);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/FirstPacket",
+ have_captured_packets);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/LastPacket",
+ have_captured_packets);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/PreviousPacketInConversation",
+ have_captured_packets);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/NextPacketInConversation",
+ have_captured_packets);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/StatisticsMenu/Summary",
+ have_captured_packets);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/StatisticsMenu/ProtocolHierarchy",
+ have_captured_packets);
+ set_toolbar_for_captured_packets(have_captured_packets);
+}
+
+
+gboolean
+packet_is_ssl(epan_dissect_t* edt)
+{
+ GPtrArray* array;
+ int ssl_id;
+ gboolean is_ssl;
+
+ if (!edt || !edt->tree)
+ return FALSE;
+ ssl_id = proto_get_id_by_filter_name("ssl");
+ if (ssl_id < 0)
+ return FALSE;
+ array = proto_find_finfo(edt->tree, ssl_id);
+ is_ssl = (array->len > 0) ? TRUE : FALSE;
+ g_ptr_array_free(array, TRUE);
+ return is_ssl;
+}
+
+void
+set_menus_for_selected_packet(capture_file *cf)
+{
+ GList *list_entry = dissector_filter_list;
+ guint i = 0;
+ /* Making the menu context-sensitive allows for easier selection of the
+ desired item and has the added benefit, with large captures, of
+ avoiding needless looping through huge lists for marked, ignored,
+ or time-referenced packets. */
+ gboolean is_ssl = packet_is_ssl(cf->edt);
+ gboolean frame_selected = cf->current_frame != NULL;
+ /* A frame is selected */
+ gboolean have_marked = frame_selected && cf->marked_count > 0;
+ /* We have marked frames. (XXX - why check frame_selected?) */
+ gboolean another_is_marked = have_marked &&
+ !(cf->marked_count == 1 && cf->current_frame->flags.marked);
+ /* We have a marked frame other than the current frame (i.e.,
+ we have at least one marked frame, and either there's more
+ than one marked frame or the current frame isn't marked). */
+ gboolean have_time_ref = cf->ref_time_count > 0;
+ gboolean another_is_time_ref = frame_selected && have_time_ref &&
+ !(cf->ref_time_count == 1 && cf->current_frame->flags.ref_time);
+ /* We have a time reference frame other than the current frame (i.e.,
+ we have at least one time reference frame, and either there's more
+ than one time reference frame or the current frame isn't a
+ time reference frame). (XXX - why check frame_selected?) */
+
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/MarkPacket",
+ frame_selected);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/MarkPacket",
+ frame_selected);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/MarkAllDisplayedPackets",
+ cf->displayed_count > 0);
+ /* Unlike un-ignore, do not allow unmark of all frames when no frames are displayed */
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/UnmarkAllDisplayedPackets",
+ have_marked);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/FindNextMark",
+ another_is_marked);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/FindPreviousMark",
+ another_is_marked);
+
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/IgnorePacket",
+ frame_selected);
+#ifdef WANT_PACKET_EDITOR
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/EditPacket",
+ frame_selected);
+#endif /* WANT_PACKET_EDITOR */
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/IgnorePacket",
+ frame_selected);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/IgnoreAllDisplayedPackets",
+ cf->displayed_count > 0 && cf->displayed_count != cf->count);
+ /* Allow un-ignore of all frames even with no frames currently displayed */
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/Un-IgnoreAllPackets",
+ cf->ignored_count > 0);
+
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/SetTimeReference",
+ frame_selected);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/Un-TimeReferenceAllPackets",
+ have_time_ref);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/TimeShift",
+ cf->count > 0);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/SetTimeReference",
+ frame_selected);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/FindNextTimeReference",
+ another_is_time_ref);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/FindPreviousTimeReference",
+ another_is_time_ref);
+
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ResizeAllColumns",
+ frame_selected);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/CollapseAll",
+ frame_selected);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/CollapseAll",
+ frame_selected);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ExpandAll",
+ frame_selected);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/ExpandAll",
+ frame_selected);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ColorizeConversation",
+ frame_selected);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ResetColoring1-10",
+ tmp_color_filters_used());
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ShowPacketinNewWindow",
+ frame_selected);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/ShowPacketinNewWindow",
+ frame_selected);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/ManuallyResolveAddress",
+ frame_selected ? ((cf->edt->pi.ethertype == ETHERTYPE_IP)||(cf->edt->pi.ethertype == ETHERTYPE_IPv6)) : FALSE);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/SCTP",
+ frame_selected ? (cf->edt->pi.ipproto == IP_PROTO_SCTP) : FALSE);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/FollowTCPStream",
+ frame_selected ? (cf->edt->pi.ipproto == IP_PROTO_TCP) : FALSE);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/FollowTCPStream",
+ frame_selected ? (cf->edt->pi.ipproto == IP_PROTO_TCP) : FALSE);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/FollowUDPStream",
+ frame_selected ? (cf->edt->pi.ipproto == IP_PROTO_UDP) : FALSE);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/FollowSSLStream",
+ frame_selected ? is_ssl : FALSE);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/FollowSSLStream",
+ frame_selected ? is_ssl : FALSE);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/ConversationFilter",
+ frame_selected);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/ConversationFilter/Ethernet",
+ frame_selected ? (cf->edt->pi.dl_src.type == AT_ETHER) : FALSE);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/ConversationFilter/IP",
+ frame_selected ? ((cf->edt->pi.ethertype == ETHERTYPE_IP)||(cf->edt->pi.ethertype == ETHERTYPE_IPv6)) : FALSE);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/ConversationFilter/TCP",
+ frame_selected ? (cf->edt->pi.ipproto == IP_PROTO_TCP) : FALSE);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/ConversationFilter/UDP",
+ frame_selected ? (cf->edt->pi.ipproto == IP_PROTO_UDP) : FALSE);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/FollowUDPStream",
+ frame_selected ? (cf->edt->pi.ipproto == IP_PROTO_UDP) : FALSE);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/ConversationFilter/PN-CBA",
+ frame_selected ? (cf->edt->pi.profinet_type != 0 && cf->edt->pi.profinet_type < 10) : FALSE);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/ColorizeConversation",
+ frame_selected);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/ColorizeConversation/Ethernet",
+ frame_selected ? (cf->edt->pi.dl_src.type == AT_ETHER) : FALSE);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/ColorizeConversation/IP",
+ frame_selected ? ((cf->edt->pi.ethertype == ETHERTYPE_IP)||(cf->edt->pi.ethertype == ETHERTYPE_IPv6)) : FALSE);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/ColorizeConversation/TCP",
+ frame_selected ? (cf->edt->pi.ipproto == IP_PROTO_TCP) : FALSE);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/ColorizeConversation/UDP",
+ frame_selected ? (cf->edt->pi.ipproto == IP_PROTO_UDP) : FALSE);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/ColorizeConversation/PN-CBA",
+ frame_selected ? (cf->edt->pi.profinet_type != 0 && cf->edt->pi.profinet_type < 10) : FALSE);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/DecodeAs",
+ frame_selected && decode_as_ok());
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/DecodeAs",
+ frame_selected && decode_as_ok());
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/Copy",
+ frame_selected);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/ApplyAsFilter",
+ frame_selected);
+ set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/PrepareaFilter",
+ frame_selected);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/ResolveName",
+ frame_selected && (gbl_resolv_flags & RESOLV_ALL_ADDRS) != RESOLV_ALL_ADDRS);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/AnalyzeMenu/FollowTCPStream",
+ frame_selected ? (cf->edt->pi.ipproto == IP_PROTO_TCP) : FALSE);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/AnalyzeMenu/FollowUDPStream",
+ frame_selected ? (cf->edt->pi.ipproto == IP_PROTO_UDP) : FALSE);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/AnalyzeMenu/FollowSSLStream",
+ frame_selected ? is_ssl : FALSE);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/AnalyzeMenu/DecodeAs",
+ frame_selected && decode_as_ok());
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/NameResolution/ResolveName",
+ frame_selected && (gbl_resolv_flags & RESOLV_ALL_ADDRS) != RESOLV_ALL_ADDRS);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ToolsMenu/FirewallACLRules",
+ frame_selected);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/StatisticsMenu/TCPStreamGraphMenu",
+ tcp_graph_selected_packet_enabled(cf->current_frame,cf->edt, NULL));
+
+ while(list_entry != NULL) {
+ dissector_filter_t *filter_entry;
+ gchar *path;
+
+ filter_entry = list_entry->data;
+ path = g_strdup_printf("/Menubar/AnalyzeMenu/ConversationFilterMenu/filter-%u", i);
+
+ set_menu_sensitivity(ui_manager_main_menubar, path,
+ menu_dissector_filter_spe_cb(/* frame_data *fd _U_*/ NULL, cf->edt, filter_entry));
+ i++;
+ list_entry = g_list_next(list_entry);
+ }
+}
+
+
+static void
+menu_prefs_toggle_bool (GtkWidget *w, gpointer data)
+{
+ gboolean *value = data;
+ module_t *module = g_object_get_data (G_OBJECT(w), "module");
+
+ module->prefs_changed = TRUE;
+ *value = !(*value);
+
+ prefs_apply (module);
+ if (!prefs.gui_use_pref_save) {
+ prefs_main_write();
+ }
+ redissect_packets();
+}
+
+static void
+menu_prefs_change_enum (GtkWidget *w, gpointer data)
+{
+ gint *value = data;
+ module_t *module = g_object_get_data (G_OBJECT(w), "module");
+ gint new_value = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(w), "enumval"));
+
+ if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM(w)))
+ return;
+
+ if (*value != new_value) {
+ module->prefs_changed = TRUE;
+ *value = new_value;
+
+ prefs_apply (module);
+ if (!prefs.gui_use_pref_save) {
+ prefs_main_write();
+ }
+ redissect_packets();
+ }
+}
+
+void
+menu_prefs_reset(void)
+{
+ g_free (g_object_get_data(G_OBJECT(ui_manager_tree_view_menu), "menu_abbrev"));
+ g_object_set_data(G_OBJECT(ui_manager_tree_view_menu), "menu_abbrev", NULL);
+}
+
+static void
+menu_prefs_change_ok (GtkWidget *w, gpointer parent_w)
+{
+ GtkWidget *entry = g_object_get_data (G_OBJECT(w), "entry");
+ module_t *module = g_object_get_data (G_OBJECT(w), "module");
+ pref_t *pref = g_object_get_data (G_OBJECT(w), "pref");
+ const gchar *new_value = gtk_entry_get_text(GTK_ENTRY(entry));
+ range_t *newrange;
+ gchar *p;
+ guint uval;
+
+ switch (pref->type) {
+ case PREF_UINT:
+ uval = strtoul(new_value, &p, pref->info.base);
+ if (p == new_value || *p != '\0') {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "The value \"%s\" isn't a valid number.",
+ new_value);
+ return;
+ }
+ if (*pref->varp.uint != uval) {
+ module->prefs_changed = TRUE;
+ *pref->varp.uint = uval;
+ }
+ break;
+ case PREF_STRING:
+ if (strcmp (*pref->varp.string, new_value) != 0) {
+ module->prefs_changed = TRUE;
+ g_free((void*)*pref->varp.string);
+ *pref->varp.string = g_strdup(new_value);
+ }
+ break;
+ case PREF_RANGE:
+ if (range_convert_str(&newrange, new_value, pref->info.max_value) != CVT_NO_ERROR) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "The value \"%s\" isn't a valid range.",
+ new_value);
+ return;
+ }
+ if (!ranges_are_equal(*pref->varp.range, newrange)) {
+ module->prefs_changed = TRUE;
+ g_free(*pref->varp.range);
+ *pref->varp.range = newrange;
+ } else {
+ g_free (newrange);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+
+ if (module->prefs_changed) {
+ /* Ensure we reload the sub menu */
+ menu_prefs_reset();
+ prefs_apply (module);
+ if (!prefs.gui_use_pref_save) {
+ prefs_main_write();
+ }
+ redissect_packets();
+ }
+
+ window_destroy(GTK_WIDGET(parent_w));
+}
+
+static void
+menu_prefs_change_cancel (GtkWidget *w _U_, gpointer parent_w)
+{
+ window_destroy(GTK_WIDGET(parent_w));
+}
+
+static void
+menu_prefs_edit_dlg (GtkWidget *w, gpointer data)
+{
+ pref_t *pref = data;
+ module_t *module = g_object_get_data (G_OBJECT(w), "module");
+ gchar *value = NULL;
+
+ GtkWidget *win, *main_tb, *main_vb, *bbox, *cancel_bt, *ok_bt;
+ GtkWidget *entry, *label;
+
+ switch (pref->type) {
+ case PREF_UINT:
+ switch (pref->info.base) {
+ case 8:
+ value = g_strdup_printf("%o", *pref->varp.uint);
+ break;
+ case 10:
+ value = g_strdup_printf("%u", *pref->varp.uint);
+ break;
+ case 16:
+ value = g_strdup_printf("%x", *pref->varp.uint);
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ break;
+ case PREF_STRING:
+ value = g_strdup(*pref->varp.string);
+ break;
+ case PREF_RANGE:
+ value = g_strdup(range_convert_range (*pref->varp.range));
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+
+ win = dlg_window_new(module->description);
+
+ gtk_window_set_resizable(GTK_WINDOW(win),FALSE);
+ gtk_window_resize(GTK_WINDOW(win), 400, 100);
+
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_add(GTK_CONTAINER(win), main_vb);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 6);
+
+ main_tb = gtk_table_new(2, 2, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 10);
+
+ label = gtk_label_new(ep_strdup_printf("%s:", pref->title));
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, 1, 2);
+ gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
+ if (pref->description)
+ gtk_widget_set_tooltip_text(label, pref->description);
+
+ entry = gtk_entry_new();
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), entry, 1, 2, 1, 2);
+ gtk_entry_set_text(GTK_ENTRY(entry), value);
+ if (pref->description)
+ gtk_widget_set_tooltip_text(entry, pref->description);
+
+ bbox = dlg_button_row_new(GTK_STOCK_CANCEL,GTK_STOCK_OK, NULL);
+ gtk_box_pack_end(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+
+ ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ g_object_set_data (G_OBJECT(ok_bt), "module", module);
+ g_object_set_data (G_OBJECT(ok_bt), "entry", entry);
+ g_object_set_data (G_OBJECT(ok_bt), "pref", pref);
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(menu_prefs_change_ok), win);
+
+ dlg_set_activate(entry, ok_bt);
+
+ cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ g_signal_connect(cancel_bt, "clicked", G_CALLBACK(menu_prefs_change_cancel), win);
+ window_set_cancel_button(win, cancel_bt, NULL);
+
+ gtk_widget_grab_default(ok_bt);
+ gtk_widget_show_all(win);
+ g_free(value);
+}
+
+static guint
+add_protocol_prefs_menu (pref_t *pref, gpointer data)
+{
+ GtkWidget *menu_preferences;
+ GtkWidget *menu_item, *menu_sub_item, *sub_menu;
+ GSList *group = NULL;
+ module_t *module = data;
+ const enum_val_t *enum_valp;
+ gchar *label = NULL;
+
+ switch (pref->type) {
+ case PREF_UINT:
+ switch (pref->info.base) {
+ case 8:
+ label = g_strdup_printf ("%s: %o", pref->title, *pref->varp.uint);
+ break;
+ case 10:
+ label = g_strdup_printf ("%s: %u", pref->title, *pref->varp.uint);
+ break;
+ case 16:
+ label = g_strdup_printf ("%s: %x", pref->title, *pref->varp.uint);
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ menu_item = gtk_menu_item_new_with_label(label);
+ g_object_set_data (G_OBJECT(menu_item), "module", module);
+ g_signal_connect(menu_item, "activate", G_CALLBACK(menu_prefs_edit_dlg), pref);
+ g_free (label);
+ break;
+ case PREF_BOOL:
+ menu_item = gtk_check_menu_item_new_with_label(pref->title);
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(menu_item), *pref->varp.boolp);
+ g_object_set_data (G_OBJECT(menu_item), "module", module);
+ g_signal_connect(menu_item, "activate", G_CALLBACK(menu_prefs_toggle_bool), pref->varp.boolp);
+ break;
+ case PREF_ENUM:
+ menu_item = gtk_menu_item_new_with_label(pref->title);
+ sub_menu = gtk_menu_new();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM(menu_item), sub_menu);
+ enum_valp = pref->info.enum_info.enumvals;
+ while (enum_valp->name != NULL) {
+ menu_sub_item = gtk_radio_menu_item_new_with_label(group, enum_valp->description);
+ group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (menu_sub_item));
+ if (enum_valp->value == *pref->varp.enump) {
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(menu_sub_item), TRUE);
+ }
+ g_object_set_data (G_OBJECT(menu_sub_item), "module", module);
+ g_object_set_data (G_OBJECT(menu_sub_item), "enumval", GINT_TO_POINTER(enum_valp->value));
+ g_signal_connect(menu_sub_item, "activate", G_CALLBACK(menu_prefs_change_enum), pref->varp.enump);
+ gtk_menu_shell_append (GTK_MENU_SHELL(sub_menu), menu_sub_item);
+ gtk_widget_show (menu_sub_item);
+ enum_valp++;
+ }
+ break;
+ case PREF_STRING:
+ label = g_strdup_printf ("%s: %s", pref->title, *pref->varp.string);
+ menu_item = gtk_menu_item_new_with_label(label);
+ g_object_set_data (G_OBJECT(menu_item), "module", module);
+ g_signal_connect(menu_item, "activate", G_CALLBACK(menu_prefs_edit_dlg), pref);
+ g_free (label);
+ break;
+ case PREF_RANGE:
+ label = g_strdup_printf ("%s: %s", pref->title, range_convert_range (*pref->varp.range));
+ menu_item = gtk_menu_item_new_with_label(label);
+ g_object_set_data (G_OBJECT(menu_item), "module", module);
+ g_signal_connect(menu_item, "activate", G_CALLBACK(menu_prefs_edit_dlg), pref);
+ g_free (label);
+ break;
+ case PREF_UAT:
+ label = g_strdup_printf ("%s...", pref->title);
+ menu_item = gtk_menu_item_new_with_label(label);
+ g_signal_connect (menu_item, "activate", G_CALLBACK(uat_window_cb), pref->varp.uat);
+ g_free (label);
+ break;
+ case PREF_STATIC_TEXT:
+ case PREF_OBSOLETE:
+ default:
+ /* Nothing to add */
+ return 0;
+ }
+
+ menu_preferences = gtk_ui_manager_get_widget(ui_manager_tree_view_menu, "/TreeViewPopup/ProtocolPreferences");
+ if(!menu_preferences)
+ g_warning("menu_preferences Not found path:TreeViewPopup/ProtocolPreferences");
+ sub_menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM(menu_preferences));
+ gtk_menu_shell_append (GTK_MENU_SHELL(sub_menu), menu_item);
+ gtk_widget_show (menu_item);
+
+ return 0;
+}
+
+static void
+rebuild_protocol_prefs_menu (module_t *prefs_module_p, gboolean preferences)
+{
+ GtkWidget *menu_preferences, *menu_item;
+ GtkWidget *sub_menu;
+ gchar *label;
+
+ menu_preferences = gtk_ui_manager_get_widget(ui_manager_tree_view_menu, "/TreeViewPopup/ProtocolPreferences");
+ if (prefs_module_p && preferences) {
+ sub_menu = gtk_menu_new();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM(menu_preferences), sub_menu);
+
+ label = g_strdup_printf ("%s Preferences...", prefs_module_p->description);
+ menu_item = gtk_image_menu_item_new_with_label (label);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM(menu_item),
+ gtk_image_new_from_stock(GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU));
+ gtk_menu_shell_append (GTK_MENU_SHELL(sub_menu), menu_item);
+ g_signal_connect_swapped(G_OBJECT(menu_item), "activate",
+ G_CALLBACK(properties_cb), (GObject *) menu_item);
+ gtk_widget_show (menu_item);
+ g_free (label);
+
+ menu_item = gtk_menu_item_new();
+ gtk_menu_shell_append (GTK_MENU_SHELL(sub_menu), menu_item);
+ gtk_widget_show (menu_item);
+
+ prefs_pref_foreach(prefs_module_p, add_protocol_prefs_menu, prefs_module_p);
+ } else {
+ /* No preferences, remove sub menu */
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM(menu_preferences), NULL);
+ }
+
+}
+
+static void
+menu_visible_column_toggle (GtkWidget *w _U_, gpointer data)
+{
+ new_packet_list_toggle_visible_column (GPOINTER_TO_INT(data));
+}
+
+void
+rebuild_visible_columns_menu (void)
+{
+ GtkWidget *menu_columns[2], *menu_item;
+ GtkWidget *sub_menu;
+ GList *clp;
+ fmt_data *cfmt;
+ gchar *title;
+ gint i, col_id;
+ menu_columns[0] = gtk_ui_manager_get_widget(ui_manager_main_menubar, "/Menubar/ViewMenu/DisplayedColumns");
+ if(! menu_columns[0]){
+ fprintf (stderr, "Warning: couldn't find menu_columns[0] path=/Menubar/ViewMenu/DisplayedColumns");
+ }
+ menu_columns[1] = gtk_ui_manager_get_widget(ui_manager_packet_list_heading, "/PacketListHeadingPopup/DisplayedColumns");
+ /* Debug */
+ if(! menu_columns[1]){
+ fprintf (stderr, "Warning: couldn't find menu_columns[1] path=/PacketListHeadingPopup/DisplayedColumns");
+ }
+
+ for (i = 0; i < 2; i++) {
+ sub_menu = gtk_menu_new();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM(menu_columns[i]), sub_menu);
+
+ clp = g_list_first (prefs.col_list);
+ col_id = 0;
+ while (clp) {
+ cfmt = (fmt_data *) clp->data;
+ if (cfmt->title[0]) {
+ if (cfmt->fmt == COL_CUSTOM) {
+ title = g_strdup_printf ("%s (%s)", cfmt->title, cfmt->custom_field);
+ } else {
+ title = g_strdup_printf ("%s (%s)", cfmt->title, col_format_desc (cfmt->fmt));
+ }
+ } else {
+ if (cfmt->fmt == COL_CUSTOM) {
+ title = g_strdup_printf ("(%s)", cfmt->custom_field);
+ } else {
+ title = g_strdup_printf ("(%s)", col_format_desc (cfmt->fmt));
+ }
+ }
+ menu_item = gtk_check_menu_item_new_with_label(title);
+ g_free (title);
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(menu_item), cfmt->visible);
+ g_signal_connect(menu_item, "activate", G_CALLBACK(menu_visible_column_toggle), GINT_TO_POINTER(col_id));
+ gtk_menu_shell_append (GTK_MENU_SHELL(sub_menu), menu_item);
+ gtk_widget_show (menu_item);
+ clp = g_list_next (clp);
+ col_id++;
+ }
+
+ menu_item = gtk_menu_item_new();
+ gtk_menu_shell_append (GTK_MENU_SHELL(sub_menu), menu_item);
+ gtk_widget_show (menu_item);
+
+ menu_item = gtk_menu_item_new_with_label ("Display All");
+ gtk_menu_shell_append (GTK_MENU_SHELL(sub_menu), menu_item);
+ g_signal_connect(menu_item, "activate", G_CALLBACK(packet_list_heading_activate_all_columns_cb), NULL);
+ gtk_widget_show (menu_item);
+ }
+}
+
+void
+menus_set_column_resolved (gboolean resolved, gboolean can_resolve)
+{
+ GtkWidget *menu;
+
+ menu = gtk_ui_manager_get_widget(ui_manager_packet_list_heading, "/PacketListHeadingPopup/ShowResolved");
+ if(!menu){
+ fprintf (stderr, "Warning: couldn't find menu path=/PacketListHeadingPopup/ShowResolved");
+ }
+ g_object_set_data(G_OBJECT(menu), "skip-update", (void *)1);
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu), resolved && can_resolve);
+ set_menu_sensitivity(ui_manager_packet_list_heading, "/PacketListHeadingPopup/ShowResolved", can_resolve);
+ g_object_set_data(G_OBJECT(menu), "skip-update", NULL);
+}
+
+void
+menus_set_column_align_default (gboolean right_justify)
+{
+ GtkWidget *submenu, *menu_item_child;
+ GList *child_list, *child_list_item;
+ const gchar *menu_item_name;
+ size_t menu_item_len;
+
+ /* get the submenu container item */
+ submenu = gtk_ui_manager_get_widget (ui_manager_packet_list_heading, "/PacketListHeadingPopup");
+ if(!submenu){
+ fprintf (stderr, "Warning: couldn't find submenu path=/PacketListHeadingPopup");
+ }
+
+ /* find the corresponding menu items to update */
+ child_list = gtk_container_get_children(GTK_CONTAINER(submenu));
+ child_list_item = child_list;
+ while(child_list_item) {
+ menu_item_child = gtk_bin_get_child(GTK_BIN(child_list_item->data));
+ if (menu_item_child != NULL) {
+ menu_item_name = gtk_label_get_text(GTK_LABEL(menu_item_child));
+ menu_item_len = strlen (menu_item_name);
+ if(strncmp(menu_item_name, "Align Left", 10) == 0) {
+ if (!right_justify && menu_item_len == 10) {
+ gtk_label_set_text(GTK_LABEL(menu_item_child), "Align Left\t(default)");
+ } else if (right_justify && menu_item_len > 10) {
+ gtk_label_set_text(GTK_LABEL(menu_item_child), "Align Left");
+ }
+ } else if (strncmp (menu_item_name, "Align Right", 11) == 0) {
+ if (right_justify && menu_item_len == 11) {
+ gtk_label_set_text(GTK_LABEL(menu_item_child), "Align Right\t(default)");
+ } else if (!right_justify && menu_item_len > 11) {
+ gtk_label_set_text(GTK_LABEL(menu_item_child), "Align Right");
+ }
+ }
+ }
+ child_list_item = g_list_next(child_list_item);
+ }
+ g_list_free(child_list);
+}
+
+void
+set_menus_for_selected_tree_row(capture_file *cf)
+{
+ gboolean properties;
+ gint id;
+
+ if (cf->finfo_selected != NULL) {
+ header_field_info *hfinfo = cf->finfo_selected->hfinfo;
+ const char *abbrev;
+ char *prev_abbrev;
+
+ if (hfinfo->parent == -1) {
+ abbrev = hfinfo->abbrev;
+ id = (hfinfo->type == FT_PROTOCOL) ? proto_get_id((protocol_t *)hfinfo->strings) : -1;
+ } else {
+ abbrev = proto_registrar_get_abbrev(hfinfo->parent);
+ id = hfinfo->parent;
+ }
+ properties = prefs_is_registered_protocol(abbrev);
+ set_menu_sensitivity(ui_manager_tree_view_menu,
+ "/TreeViewPopup/GotoCorrespondingPacket", hfinfo->type == FT_FRAMENUM);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/Copy",
+ TRUE);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/Copy/AsFilter",
+ proto_can_match_selected(cf->finfo_selected, cf->edt));
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/ApplyasColumn",
+ hfinfo->type != FT_NONE);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/ApplyAsFilter",
+ proto_can_match_selected(cf->finfo_selected, cf->edt));
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/PrepareaFilter",
+ proto_can_match_selected(cf->finfo_selected, cf->edt));
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/ColorizewithFilter",
+ proto_can_match_selected(cf->finfo_selected, cf->edt));
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/ProtocolPreferences",
+ properties);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/DisableProtocol",
+ (id == -1) ? FALSE : proto_can_toggle_protocol(id));
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/ExpandSubtrees",
+ cf->finfo_selected->tree_type != -1);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/WikiProtocolPage",
+ (id == -1) ? FALSE : TRUE);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/FilterFieldReference",
+ (id == -1) ? FALSE : TRUE);
+ set_menu_sensitivity(ui_manager_main_menubar,
+ "/Menubar/FileMenu/Export/SelectedPacketBytes", TRUE);
+ set_menu_sensitivity(ui_manager_main_menubar,
+ "/Menubar/GoMenu/GotoCorrespondingPacket", hfinfo->type == FT_FRAMENUM);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/Copy/Description",
+ proto_can_match_selected(cf->finfo_selected, cf->edt));
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/Copy/Fieldname",
+ proto_can_match_selected(cf->finfo_selected, cf->edt));
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/Copy/Value",
+ proto_can_match_selected(cf->finfo_selected, cf->edt));
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/Copy/AsFilter",
+ proto_can_match_selected(cf->finfo_selected, cf->edt));
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/AnalyzeMenu/ApplyasColumn",
+ hfinfo->type != FT_NONE);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/AnalyzeMenu/ApplyAsFilter",
+ proto_can_match_selected(cf->finfo_selected, cf->edt));
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/AnalyzeMenu/PrepareaFilter",
+ proto_can_match_selected(cf->finfo_selected, cf->edt));
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ExpandSubtrees",
+ cf->finfo_selected->tree_type != -1);
+ prev_abbrev = g_object_get_data(G_OBJECT(ui_manager_tree_view_menu), "menu_abbrev");
+ if (!prev_abbrev || (strcmp (prev_abbrev, abbrev) != 0)) {
+ /* No previous protocol or protocol changed - update Protocol Preferences menu */
+ module_t *prefs_module_p = prefs_find_module(abbrev);
+ rebuild_protocol_prefs_menu (prefs_module_p, properties);
+
+ g_object_set_data(G_OBJECT(ui_manager_tree_view_menu), "menu_abbrev", g_strdup(abbrev));
+ g_free (prev_abbrev);
+ }
+ } else {
+ set_menu_sensitivity(ui_manager_tree_view_menu,
+ "/TreeViewPopup/GotoCorrespondingPacket", FALSE);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/Copy", FALSE);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/ApplyasColumn", FALSE);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/ApplyAsFilter", FALSE);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/PrepareaFilter", FALSE);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/ColorizewithFilter", FALSE);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/ProtocolPreferences",
+ FALSE);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/DisableProtocol", FALSE);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/ExpandSubtrees", FALSE);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/WikiProtocolPage",
+ FALSE);
+ set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/FilterFieldReference",
+ FALSE);
+ set_menu_sensitivity(ui_manager_main_menubar,
+ "/Menubar/FileMenu/Export/SelectedPacketBytes", FALSE);
+ set_menu_sensitivity(ui_manager_main_menubar,
+ "/Menubar/GoMenu/GotoCorrespondingPacket", FALSE);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/Copy/Description", FALSE);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/Copy/Fieldname", FALSE);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/Copy/Value", FALSE);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/EditMenu/Copy/AsFilter", FALSE);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/AnalyzeMenu/ApplyasColumn", FALSE);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/AnalyzeMenu/ApplyAsFilter", FALSE);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/AnalyzeMenu/PrepareaFilter", FALSE);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/ExpandSubtrees", FALSE);
+ }
+}
+
+void set_menus_for_packet_history(gboolean back_history, gboolean forward_history) {
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/Back", back_history);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/GoMenu/Forward", forward_history);
+ set_toolbar_for_packet_history(back_history, forward_history);
+}
+
+
+void set_menus_for_file_set(gboolean file_set, gboolean previous_file, gboolean next_file) {
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/Set/ListFiles", file_set);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/Set/PreviousFile", previous_file);
+ set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/FileMenu/Set/NextFile", next_file);
+}
+
+GtkWidget *menus_get_profiles_edit_menu (void)
+{
+ return gtk_ui_manager_get_widget(ui_manager_statusbar_profiles_menu, "/ProfilesMenuPopup/Edit");
+}
+
+GtkWidget *menus_get_profiles_delete_menu (void)
+{
+ return gtk_ui_manager_get_widget(ui_manager_statusbar_profiles_menu, "/ProfilesMenuPopup/Delete");
+}
+
+GtkWidget *menus_get_profiles_change_menu (void)
+{
+ return gtk_ui_manager_get_widget(ui_manager_statusbar_profiles_menu, "/ProfilesMenuPopup/Change");
+}
+
+void set_menus_for_profiles(gboolean default_profile)
+{
+ set_menu_sensitivity(ui_manager_statusbar_profiles_menu, "/ProfilesMenuPopup/Edit", !default_profile);
+ set_menu_sensitivity(ui_manager_statusbar_profiles_menu, "/ProfilesMenuPopup/Delete", !default_profile);
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
+
diff --git a/ui/gtk/main_proto_draw.c b/ui/gtk/main_proto_draw.c
new file mode 100644
index 0000000000..d431b941ac
--- /dev/null
+++ b/ui/gtk/main_proto_draw.c
@@ -0,0 +1,2250 @@
+/* proto_draw.c
+ * Routines for GTK+ packet display
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * Jeff Foster, 2001/03/12, added support for displaying named
+ * data sources as tabbed hex windows
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <ctype.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#if GTK_CHECK_VERSION(3,0,0)
+# include <gdk/gdkkeysyms-compat.h>
+#endif
+
+#include <string.h>
+
+#include <epan/epan_dissect.h>
+
+#include <epan/packet.h>
+#include <epan/charsets.h>
+#include <epan/prefs.h>
+#include <epan/filesystem.h>
+
+#include "../isprint.h"
+#include "../alert_box.h"
+#include "../simple_dialog.h"
+#include "../progress_dlg.h"
+#include "../ui_util.h"
+#include <wsutil/file_util.h>
+
+#include "ui/gtk/keys.h"
+#include "ui/gtk/color_utils.h"
+#include "ui/gtk/capture_file_dlg.h"
+#include "ui/gtk/packet_win.h"
+#include "ui/gtk/file_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/font_utils.h"
+#include "ui/gtk/webbrowser.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/menus.h"
+#include "ui/gtk/main_proto_draw.h"
+#include "ui/gtk/recent.h"
+
+#ifdef _WIN32
+#include <gdk/gdkwin32.h>
+#include <windows.h>
+#include "win32/file_dlg_win32.h"
+#endif
+
+
+#define E_BYTE_VIEW_TREE_PTR "byte_view_tree_ptr"
+#define E_BYTE_VIEW_TREE_VIEW_PTR "byte_view_tree_view_ptr"
+#define E_BYTE_VIEW_NDIGITS_KEY "byte_view_ndigits"
+#define E_BYTE_VIEW_TVBUFF_KEY "byte_view_tvbuff"
+#define E_BYTE_VIEW_START_KEY "byte_view_start"
+#define E_BYTE_VIEW_END_KEY "byte_view_end"
+#define E_BYTE_VIEW_MASK_KEY "byte_view_mask"
+#define E_BYTE_VIEW_MASKLE_KEY "byte_view_mask_le"
+#define E_BYTE_VIEW_APP_START_KEY "byte_view_app_start"
+#define E_BYTE_VIEW_APP_END_KEY "byte_view_app_end"
+#define E_BYTE_VIEW_ENCODE_KEY "byte_view_encode"
+
+static GtkWidget *
+add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
+ proto_tree *tree, GtkWidget *tree_view);
+
+static void
+proto_tree_draw_node(proto_node *node, gpointer data);
+
+/* Get the current text window for the notebook. */
+GtkWidget *
+get_notebook_bv_ptr(GtkWidget *nb_ptr)
+{
+ int num;
+ GtkWidget *bv_page;
+
+ num = gtk_notebook_get_current_page(GTK_NOTEBOOK(nb_ptr));
+ bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num);
+ if (bv_page)
+ return gtk_bin_get_child(GTK_BIN(bv_page));
+ else
+ return NULL;
+}
+
+/*
+ * Get the data and length for a byte view, given the byte view page.
+ * Return the pointer, or NULL on error, and set "*data_len" to the length.
+ */
+const guint8 *
+get_byte_view_data_and_length(GtkWidget *byte_view, guint *data_len)
+{
+ tvbuff_t *byte_view_tvb;
+ const guint8 *data_ptr;
+
+ byte_view_tvb = g_object_get_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY);
+ if (byte_view_tvb == NULL)
+ return NULL;
+
+ data_ptr = tvb_get_ptr(byte_view_tvb, 0, -1);
+ *data_len = tvb_length(byte_view_tvb);
+ return data_ptr;
+}
+
+/*
+ * Set the current text window for the notebook to the window that
+ * refers to a particular tvbuff.
+ */
+void
+set_notebook_page(GtkWidget *nb_ptr, tvbuff_t *tvb)
+{
+ int num;
+ GtkWidget *bv_page, *bv;
+ tvbuff_t *bv_tvb;
+
+ for (num = 0;
+ (bv_page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(nb_ptr), num)) != NULL;
+ num++) {
+ bv = gtk_bin_get_child(GTK_BIN(bv_page));
+ bv_tvb = g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_TVBUFF_KEY);
+ if (bv_tvb == tvb) {
+ /* Found it. */
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(nb_ptr), num);
+ break;
+ }
+ }
+}
+
+/* Redraw a given byte view window. */
+void
+redraw_packet_bytes(GtkWidget *nb, frame_data *fd, field_info *finfo)
+{
+ GtkWidget *bv;
+ const guint8 *data;
+ guint len;
+
+ bv = get_notebook_bv_ptr(nb);
+ if (bv != NULL) {
+ data = get_byte_view_data_and_length(bv, &len);
+ if (data != NULL)
+ packet_hex_print(bv, data, fd, finfo, len);
+ }
+}
+
+/* Redraw all byte view windows. */
+void
+redraw_packet_bytes_all(void)
+{
+ if (cfile.current_frame != NULL)
+ redraw_packet_bytes( byte_nb_ptr_gbl, cfile.current_frame, cfile.finfo_selected);
+
+ redraw_packet_bytes_packet_wins();
+
+ /* XXX - this is a hack, to workaround a bug in GTK2.x!
+ when changing the font size, even refilling of the corresponding
+ gtk_text_buffer doesn't seem to trigger an update.
+ The only workaround is to freshly select the frame, which will remove any
+ existing notebook tabs and "restart" the whole byte view again. */
+ if (cfile.current_frame != NULL) {
+ cfile.current_row = -1;
+ cf_goto_frame(&cfile, cfile.current_frame->num);
+ }
+}
+
+static void
+expand_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
+ GtkTreePath *path _U_, gpointer user_data _U_)
+{
+ field_info *finfo;
+ GtkTreeModel *model;
+
+ model = gtk_tree_view_get_model(tree_view);
+ gtk_tree_model_get(model, iter, 1, &finfo, -1);
+ g_assert(finfo);
+
+ /* scroll the expanded item to reduce the need to do a manual scroll down
+ * and provide faster navigation of deeper trees */
+
+ if(prefs.gui_auto_scroll_on_expand)
+ gtk_tree_view_scroll_to_cell(tree_view, path, NULL, TRUE, (prefs.gui_auto_scroll_percentage/100.0f), 0.0f);
+
+ /*
+ * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
+ * are thus presumably leaf nodes and cannot be expanded.
+ */
+ if (finfo->tree_type != -1) {
+ g_assert(finfo->tree_type >= 0 &&
+ finfo->tree_type < num_tree_types);
+ tree_is_expanded[finfo->tree_type] = TRUE;
+ }
+}
+
+static void
+collapse_tree(GtkTreeView *tree_view, GtkTreeIter *iter,
+ GtkTreePath *path _U_, gpointer user_data _U_)
+{
+ field_info *finfo;
+ GtkTreeModel *model;
+
+ model = gtk_tree_view_get_model(tree_view);
+ gtk_tree_model_get(model, iter, 1, &finfo, -1);
+ g_assert(finfo);
+
+ /*
+ * Nodes with "finfo->tree_type" of -1 have no ett_ value, and
+ * are thus presumably leaf nodes and cannot be collapsed.
+ */
+ if (finfo->tree_type != -1) {
+ g_assert(finfo->tree_type >= 0 &&
+ finfo->tree_type < num_tree_types);
+ tree_is_expanded[finfo->tree_type] = FALSE;
+ }
+}
+
+#define MAX_OFFSET_LEN 8 /* max length of hex offset of bytes */
+#define BYTES_PER_LINE 16 /* max byte values in a line */
+#define BITS_PER_LINE 8 /* max bit values in a line */
+#define BYTE_VIEW_SEP 8 /* insert a space every BYTE_VIEW_SEP bytes */
+#define HEX_DUMP_LEN (BYTES_PER_LINE*3 + 1)
+/* max number of characters hex dump takes -
+ 2 digits plus trailing blank
+ plus separator between first and
+ second 8 digits */
+#define DATA_DUMP_LEN (HEX_DUMP_LEN + 2 + BYTES_PER_LINE)
+/* number of characters those bytes take;
+ 3 characters per byte of hex dump,
+ 2 blanks separating hex from ASCII,
+ 1 character per byte of ASCII dump */
+#define MAX_LINE_LEN (MAX_OFFSET_LEN + 2 + DATA_DUMP_LEN)
+/* number of characters per line;
+ offset, 2 blanks separating offset
+ from data dump, data dump */
+#define MAX_LINES 100
+#define MAX_LINES_LEN (MAX_LINES*MAX_LINE_LEN)
+
+/* Which byte the offset is referring to. Associates
+ * whitespace with the preceding digits. */
+static int
+byte_num(int offset, int start_point)
+{
+ return (offset - start_point) / 3;
+}
+static int
+bit_num(int offset, int start_point)
+{
+ return (offset - start_point) / 9;
+}
+
+struct field_lookup_info {
+ field_info *fi;
+ GtkTreeIter iter;
+};
+
+static gboolean
+lookup_finfo(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
+ gpointer data)
+{
+ field_info *fi;
+ struct field_lookup_info *fli = (struct field_lookup_info *)data;
+
+ gtk_tree_model_get(model, iter, 1, &fi, -1);
+ if (fi == fli->fi) {
+ fli->iter = *iter;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+GtkTreePath
+*tree_find_by_field_info(GtkTreeView *tree_view, field_info *finfo)
+{
+ GtkTreeModel *model;
+ struct field_lookup_info fli;
+
+ g_assert(finfo != NULL);
+
+ model = gtk_tree_view_get_model(tree_view);
+ fli.fi = finfo;
+ gtk_tree_model_foreach(model, lookup_finfo, &fli);
+
+ return gtk_tree_model_get_path(model, &fli.iter);
+}
+
+static int
+hex_view_get_byte(guint ndigits, int row, int column)
+{
+ int byte;
+ int digits_start_1;
+ int digits_end_1;
+ int digits_start_2;
+ int digits_end_2;
+ int text_start_1;
+ int text_end_1;
+ int text_start_2;
+ int text_end_2;
+
+ /*
+ * The column of the first hex digit in the first half.
+ * That starts after "ndigits" digits of offset and two
+ * separating blanks.
+ */
+ digits_start_1 = ndigits + 2;
+
+ /*
+ * The column of the last hex digit in the first half.
+ * There are BYTES_PER_LINE/2 bytes displayed in the first
+ * half; there are 2 characters per byte, plus a separating
+ * blank after all but the last byte's characters.
+ */
+ digits_end_1 = digits_start_1 + (BYTES_PER_LINE/2)*2 +
+ (BYTES_PER_LINE/2 - 1);
+
+ /*
+ * The column of the first hex digit in the second half.
+ * Add 2 for the 2 separating blanks between the halves.
+ */
+ digits_start_2 = digits_end_1 + 2;
+
+ /*
+ * The column of the last hex digit in the second half.
+ * Add the same value we used to get "digits_end_1" from
+ * "digits_start_1".
+ */
+ digits_end_2 = digits_start_2 + (BYTES_PER_LINE/2)*2 +
+ (BYTES_PER_LINE/2 - 1);
+
+ /*
+ * The column of the first "text dump" character in the first half.
+ * Add 3 for the 3 separating blanks between the hex and text dump.
+ */
+ text_start_1 = digits_end_2 + 3;
+
+ /*
+ * The column of the last "text dump" character in the first half.
+ * There are BYTES_PER_LINE/2 bytes displayed in the first
+ * half; there is 1 character per byte.
+ *
+ * Then subtract 1 to get the last column of the first half
+ * rather than the first column after the first half.
+ */
+ text_end_1 = text_start_1 + BYTES_PER_LINE/2 - 1;
+
+ /*
+ * The column of the first "text dump" character in the second half.
+ * Add back the 1 to get the first column after the first half,
+ * and then add 1 for the separating blank between the halves.
+ */
+ text_start_2 = text_end_1 + 2;
+
+ /*
+ * The column of the last "text dump" character in second half.
+ * Add the same value we used to get "text_end_1" from
+ * "text_start_1".
+ */
+ text_end_2 = text_start_2 + BYTES_PER_LINE/2 - 1;
+
+ /* Given the column and row, determine which byte offset
+ * the user clicked on. */
+ if (column >= digits_start_1 && column <= digits_end_1) {
+ byte = byte_num(column, digits_start_1);
+ if (byte == -1) {
+ return byte;
+ }
+ }
+ else if (column >= digits_start_2 && column <= digits_end_2) {
+ byte = byte_num(column, digits_start_2);
+ if (byte == -1) {
+ return byte;
+ }
+ byte += 8;
+ }
+ else if (column >= text_start_1 && column <= text_end_1) {
+ byte = column - text_start_1;
+ }
+ else if (column >= text_start_2 && column <= text_end_2) {
+ byte = 8 + column - text_start_2;
+ }
+ else {
+ /* The user didn't select a hex digit or
+ * text-dump character. */
+ return -1;
+ }
+
+ /* Add the number of bytes from the previous rows. */
+ byte += row * BYTES_PER_LINE;
+
+ return byte;
+}
+
+static int
+bit_view_get_byte(guint ndigits, int row, int column)
+{
+ int byte;
+ int digits_start;
+ int digits_end;
+ int text_start;
+ int text_end;
+
+ /*
+ * The column of the first bit digit.
+ * That starts after "ndigits" digits of offset and two
+ * separating blanks.
+ */
+ digits_start = ndigits + 2;
+
+ /*
+ * The column of the last bit digit.
+ * There are BITS_PER_LINE bytes displayed; there are
+ * 8 characters per byte, plus a separating blank
+ * after all but the last byte's characters.
+ */
+ digits_end = digits_start + (BITS_PER_LINE)*8 +
+ (BITS_PER_LINE - 1);
+
+ /*
+ * The column of the first "text dump" character.
+ * Add 3 for the 3 separating blanks between the bit and text dump.
+ */
+ text_start = digits_end + 3;
+
+ /*
+ * The column of the last "text dump" character.
+ * There are BITS_PER_LINE bytes displayed; there is 1 character per byte.
+ *
+ * Then subtract 1 to get the last column.
+ */
+ text_end = text_start + BITS_PER_LINE - 1;
+
+ /* Given the column and row, determine which byte offset
+ * the user clicked on. */
+ if (column >= digits_start && column <= digits_end) {
+ byte = bit_num(column, digits_start);
+ if (byte == -1) {
+ return byte;
+ }
+ }
+ else if (column >= text_start && column <= text_end) {
+ byte = column - text_start;
+ }
+ else {
+ /* The user didn't select a hex digit or
+ * text-dump character. */
+ return -1;
+ }
+
+ /* Add the number of bytes from the previous rows. */
+ byte += row * BITS_PER_LINE;
+
+ return byte;
+}
+
+/* If the user selected a certain byte in the byte view, try to find
+ * the item in the GUI proto_tree that corresponds to that byte, and:
+ *
+ * if we succeed, select it, and return TRUE;
+ * if we fail, return FALSE. */
+gboolean
+byte_view_select(GtkWidget *widget, GdkEventButton *event)
+{
+ GtkTextView *bv = GTK_TEXT_VIEW(widget);
+ proto_tree *tree;
+ GtkTreeView *tree_view;
+ GtkTextIter iter;
+ int row, column;
+ guint ndigits;
+ gint x, y;
+ int byte = -1;
+ tvbuff_t *tvb;
+
+ tree = g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TREE_PTR);
+ if (tree == NULL) {
+ /*
+ * Somebody clicked on the dummy byte view; do nothing.
+ */
+ return FALSE;
+ }
+ tree_view = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(widget),
+ E_BYTE_VIEW_TREE_VIEW_PTR));
+
+ /* get the row/column selected */
+ gtk_text_view_window_to_buffer_coords(bv,
+ gtk_text_view_get_window_type(bv, event->window),
+ (gint) event->x, (gint) event->y, &x, &y);
+ gtk_text_view_get_iter_at_location(bv, &iter, x, y);
+ row = gtk_text_iter_get_line(&iter);
+ column = gtk_text_iter_get_line_offset(&iter);
+
+ /*
+ * Get the number of digits of offset being displayed, and
+ * compute the byte position in the buffer.
+ */
+ ndigits = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY));
+
+ switch (recent.gui_bytes_view) {
+ case BYTES_HEX:
+ byte = hex_view_get_byte(ndigits, row, column);
+ break;
+ case BYTES_BITS:
+ byte = bit_view_get_byte(ndigits, row, column);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (byte == -1) {
+ return FALSE;
+ }
+
+ /* Get the data source tvbuff */
+ tvb = g_object_get_data(G_OBJECT(widget), E_BYTE_VIEW_TVBUFF_KEY);
+
+ return highlight_field(tvb, byte, tree_view, tree);
+}
+
+/* This highlights the field in the proto tree that is at position byte */
+gboolean
+highlight_field(tvbuff_t *tvb, gint byte, GtkTreeView *tree_view,
+ proto_tree *tree)
+{
+ GtkTreeModel *model = NULL;
+ GtkTreePath *first_path = NULL, *path = NULL;
+ GtkTreeIter parent;
+ field_info *finfo = NULL;
+ match_data mdata;
+ struct field_lookup_info fli;
+
+ if (cfile.search_in_progress && cfile.string && cfile.decode_data) {
+ /* The tree where the target string matched one of the labels was discarded in
+ match_protocol_tree() so we have to search again in the latest tree. (Uugh) */
+ if (cf_find_string_protocol_tree(&cfile, tree, &mdata)) {
+ finfo = mdata.finfo;
+ }
+ } else {
+ /* Find the finfo that corresponds to our byte. */
+ finfo = proto_find_field_from_offset(tree, byte, tvb);
+ }
+
+ if (!finfo) {
+ return FALSE;
+ }
+
+ model = gtk_tree_view_get_model(tree_view);
+ fli.fi = finfo;
+ gtk_tree_model_foreach(model, lookup_finfo, &fli);
+
+ /* Expand our field's row */
+ first_path = gtk_tree_model_get_path(model, &fli.iter);
+ gtk_tree_view_expand_row(tree_view, first_path, FALSE);
+ expand_tree(tree_view, &fli.iter, NULL, NULL);
+
+ /* ... and its parents */
+ while (gtk_tree_model_iter_parent(model, &parent, &fli.iter)) {
+ path = gtk_tree_model_get_path(model, &parent);
+ gtk_tree_view_expand_row(tree_view, path, FALSE);
+ expand_tree(tree_view, &parent, NULL, NULL);
+ fli.iter = parent;
+ gtk_tree_path_free(path);
+ }
+
+ /* Refresh the display so that the expanded trees are visible */
+ main_proto_tree_draw(tree);
+
+ /* select our field's row */
+ gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree_view),
+ first_path);
+
+ /* If the last search was a string or hex search within "Packet data", the entire field might
+ not be highlighted. If the user just clicked on one of the bytes comprising that field, the
+ above call didn't trigger a 'gtk_tree_view_get_selection' event. Call redraw_packet_bytes()
+ to make the highlighting of the entire field visible. */
+ if (!cfile.search_in_progress) {
+ if (cfile.hex || (cfile.string && cfile.packet_data)) {
+ redraw_packet_bytes(byte_nb_ptr_gbl, cfile.current_frame, cfile.finfo_selected);
+ }
+ }
+
+ /* And position the window so the selection is visible.
+ * Position the selection in the middle of the viewable
+ * pane. */
+ gtk_tree_view_scroll_to_cell(tree_view, first_path, NULL, TRUE, 0.5f, 0.0f);
+
+ gtk_tree_path_free(first_path);
+
+ return TRUE;
+}
+
+/* Calls functions for different mouse-button presses. */
+static gboolean
+byte_view_button_press_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ GdkEventButton *event_button = NULL;
+
+ if(widget == NULL || event == NULL || data == NULL) {
+ return FALSE;
+ }
+
+ if(event->type == GDK_BUTTON_PRESS) {
+ event_button = (GdkEventButton *) event;
+
+ /* To qoute the "Gdk Event Structures" doc:
+ * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
+ switch(event_button->button) {
+
+ case 1:
+ return byte_view_select(widget, event_button);
+ case 3:
+ return popup_menu_handler(widget, event, data);
+ default:
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+GtkWidget *
+byte_view_new(void)
+{
+ GtkWidget *byte_nb;
+
+ byte_nb = gtk_notebook_new();
+ gtk_notebook_set_tab_pos(GTK_NOTEBOOK(byte_nb), GTK_POS_BOTTOM);
+
+ /* this will only have an effect, if no tabs are shown */
+ gtk_notebook_set_show_border(GTK_NOTEBOOK(byte_nb), FALSE);
+
+ /* set the tabs scrollable, if they don't fit into the pane */
+ gtk_notebook_set_scrollable(GTK_NOTEBOOK(byte_nb), TRUE);
+
+ /* enable a popup menu containing the tab labels, will be helpful if tabs don't fit into the pane */
+ gtk_notebook_popup_enable(GTK_NOTEBOOK(byte_nb));
+
+ /* Add a placeholder byte view so that there's at least something
+ displayed in the byte view notebook. */
+ add_byte_tab(byte_nb, "", NULL, NULL, NULL);
+
+ return byte_nb;
+}
+
+static void
+byte_view_realize_cb(GtkWidget *bv, gpointer data _U_)
+{
+ const guint8 *byte_data;
+ guint byte_len;
+
+ byte_data = get_byte_view_data_and_length(bv, &byte_len);
+ if (byte_data == NULL) {
+ /* This must be the dummy byte view if no packet is selected. */
+ return;
+ }
+ packet_hex_print(bv, byte_data, cfile.current_frame, NULL, byte_len);
+}
+
+static GtkWidget *
+add_byte_tab(GtkWidget *byte_nb, const char *name, tvbuff_t *tvb,
+ proto_tree *tree, GtkWidget *tree_view)
+{
+ GtkWidget *byte_view, *byte_scrollw, *label;
+ GtkTextBuffer *buf;
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context;
+ GdkRGBA *rgba_bg_color;
+ GdkRGBA *rgba_fg_color;
+#if !GTK_CHECK_VERSION(3,2,0)
+ GdkColor bg_color;
+ GdkColor fg_color;
+#endif /* GTK_CHECK_VERSION(3,2,0) */
+#else
+ GtkStyle *style;
+#endif
+
+ /* Byte view. Create a scrolled window for the text. */
+ byte_scrollw = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(byte_scrollw),
+ GTK_SHADOW_IN);
+ /* Add scrolled pane to tabbed window */
+ label = gtk_label_new(name);
+ gtk_notebook_append_page(GTK_NOTEBOOK(byte_nb), byte_scrollw, label);
+
+ gtk_widget_show(byte_scrollw);
+
+ byte_view = gtk_text_view_new();
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(byte_view), FALSE);
+ gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(byte_view), FALSE);
+ buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(byte_view));
+#if GTK_CHECK_VERSION(3,0,0)
+ context = gtk_widget_get_style_context (GTK_WIDGET(byte_view));
+ gtk_style_context_get (context, GTK_STATE_FLAG_SELECTED,
+ GTK_STYLE_PROPERTY_BACKGROUND_COLOR, &rgba_bg_color,
+ GTK_STYLE_PROPERTY_COLOR, &rgba_fg_color,
+ NULL);
+#if GTK_CHECK_VERSION(3,2,0)
+ gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL);
+ gtk_text_buffer_create_tag(buf, "reverse",
+ "font-desc", user_font_get_regular(),
+ "foreground-gdk", rgba_fg_color,
+ "background-gdk", rgba_bg_color,
+ NULL);
+#else
+ /* Hack */
+ bg_color.red = rgba_bg_color->red * 65535;
+ bg_color.green = rgba_bg_color->green * 65535;
+ bg_color.blue = rgba_bg_color->blue * 65535;
+ fg_color.red = rgba_fg_color->red * 65535;
+ fg_color.green = rgba_fg_color->green * 65535;
+ fg_color.blue = rgba_fg_color->blue * 65535;
+
+ gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL);
+ gtk_text_buffer_create_tag(buf, "reverse",
+ "font-desc", user_font_get_regular(),
+ "foreground-gdk", &fg_color,
+ "background-gdk", &bg_color,
+ NULL);
+#endif /* GTK_CHECK_VERSION(3,2,0) */
+#else
+ style = gtk_widget_get_style(GTK_WIDGET(top_level));
+ gtk_text_buffer_create_tag(buf, "plain", "font-desc", user_font_get_regular(), NULL);
+ gtk_text_buffer_create_tag(buf, "reverse",
+ "font-desc", user_font_get_regular(),
+ "foreground-gdk", &style->text[GTK_STATE_SELECTED],
+ "background-gdk", &style->base[GTK_STATE_SELECTED],
+ NULL);
+#endif
+ gtk_text_buffer_create_tag(buf, "bold", "font-desc", user_font_get_bold(), NULL);
+ g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TVBUFF_KEY, tvb);
+ gtk_container_add(GTK_CONTAINER(byte_scrollw), byte_view);
+
+ g_signal_connect(byte_view, "show", G_CALLBACK(byte_view_realize_cb), NULL);
+ g_signal_connect(byte_view, "button_press_event", G_CALLBACK(byte_view_button_press_cb),
+ g_object_get_data(G_OBJECT(popup_menu_object), PM_BYTES_VIEW_KEY));
+
+ g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_PTR, tree);
+ g_object_set_data(G_OBJECT(byte_view), E_BYTE_VIEW_TREE_VIEW_PTR, tree_view);
+
+ gtk_widget_show(byte_view); /* triggers byte_view_realize_cb which calls packet_hex_print */
+
+ /* no tabs if this is the first page */
+ if (!(gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_scrollw)))
+ gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), FALSE);
+ else
+ gtk_notebook_set_show_tabs(GTK_NOTEBOOK(byte_nb), TRUE);
+
+ /* set this page */
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(byte_nb),
+ gtk_notebook_page_num(GTK_NOTEBOOK(byte_nb), byte_nb));
+
+ return byte_view;
+}
+
+void
+add_main_byte_views(epan_dissect_t *edt)
+{
+ add_byte_views(edt, tree_view_gbl, byte_nb_ptr_gbl);
+}
+
+void
+add_byte_views(epan_dissect_t *edt, GtkWidget *tree_view,
+ GtkWidget *byte_nb_ptr)
+{
+ GSList *src_le;
+ data_source *src;
+
+ /*
+ * Get rid of all the old notebook tabs.
+ */
+ while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0) != NULL)
+ gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
+
+ /*
+ * Add to the specified byte view notebook tabs for hex dumps
+ * of all the data sources for the specified frame.
+ */
+ for (src_le = edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
+ src = src_le->data;
+ add_byte_tab(byte_nb_ptr, get_data_source_name(src), src->tvb, edt->tree,
+ tree_view);
+ }
+
+ /*
+ * Initially select the first byte view.
+ */
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
+}
+
+
+
+static GtkWidget *savehex_dlg=NULL;
+
+static void
+savehex_dlg_destroy_cb(GtkWidget *w _U_, gpointer user_data _U_)
+{
+ savehex_dlg = NULL;
+}
+
+
+static void
+copy_hex_all_info(GString* copy_buffer, const guint8* data_p, int data_len, gboolean append_text)
+{
+ const int byte_line_length = 16; /* Print out data for 16 bytes on one line */
+ int i, j;
+ gboolean end_of_line = TRUE; /* Initial state is end of line */
+ int byte_line_part_length;
+
+ GString* hex_str;
+ GString* char_str;
+
+ /* Write hex data for a line, then ascii data, then concatenate and add to buffer */
+ hex_str = g_string_new("");
+ char_str= g_string_new("");
+
+ i = 0;
+ while (i<data_len) {
+ if(end_of_line) {
+ g_string_append_printf(hex_str,"%04x ",i); /* Offset - note that we _append_ here */
+ }
+
+ g_string_append_printf(hex_str," %02x",*data_p);
+ if(append_text) {
+ g_string_append_printf(char_str,"%c",isprint(*data_p) ? *data_p : '.');
+ }
+
+ ++data_p;
+
+ /* Look ahead to see if this is the end of the data */
+ byte_line_part_length = (++i) % byte_line_length;
+ if(i == data_len){
+ /* End of data - need to fill in spaces in hex string and then do "end of line".
+ *
+ */
+ for(j = 0; append_text && (j < (byte_line_length - byte_line_part_length)); ++j) {
+ g_string_append(hex_str," "); /* Three spaces for each missing byte */
+ }
+ end_of_line = TRUE;
+ } else {
+ end_of_line = (byte_line_part_length == 0 ? TRUE : FALSE);
+ }
+
+
+ if (end_of_line){
+ /* End of line */
+ g_string_append(copy_buffer, hex_str->str);
+ if(append_text) {
+ /* Two spaces between hex and text */
+ g_string_append_c(copy_buffer, ' ');
+ g_string_append_c(copy_buffer, ' ');
+ g_string_append(copy_buffer, char_str->str);
+ }
+ /* Setup ready for next line */
+ g_string_assign(char_str,"");
+ g_string_assign(hex_str, "\n");
+ }
+ }
+
+ g_string_free(hex_str, TRUE);
+ g_string_free(char_str, TRUE);
+}
+
+static int
+copy_hex_bytes_text_only(GString* copy_buffer, const guint8* data_p, int data_len _U_)
+{
+
+ gchar to_append;
+
+ /* Copy printable characters, newlines, and (horizontal) tabs. */
+ if(isprint(*data_p)) {
+ to_append = *data_p;
+ } else if(*data_p==0x0a) {
+ to_append = '\n';
+ } else if(*data_p==0x09) {
+ to_append = '\t';
+ } else {
+ return 1; /* Just ignore non-printable bytes */
+ }
+ g_string_append_c(copy_buffer,to_append);
+ return 1;
+}
+
+static
+int copy_hex_bytes_hex(GString* copy_buffer, const guint8* data_p, int data_len _U_)
+{
+ g_string_append_printf(copy_buffer, "%02x", *data_p);
+ return 1;
+}
+
+void
+copy_hex_cb(GtkWidget * w _U_, gpointer data _U_, copy_data_type data_type)
+{
+ GtkWidget *bv;
+
+ guint len = 0;
+ int bytes_consumed = 0;
+ int flags;
+
+ const guint8* data_p;
+
+ GString* copy_buffer = g_string_new(""); /* String to copy to clipboard */
+
+ bv = get_notebook_bv_ptr(byte_nb_ptr_gbl);
+ if (bv == NULL) {
+ /* shouldn't happen */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
+ return;
+ }
+
+ data_p = get_byte_view_data_and_length(bv, &len);
+ g_assert(data_p != NULL);
+
+ flags = data_type & CD_FLAGSMASK;
+ data_type = data_type & CD_TYPEMASK;
+
+ if(flags & CD_FLAGS_SELECTEDONLY) {
+ int start, end;
+
+ /* Get the start and end of the highlighted bytes. */
+ start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
+ end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
+
+ if(start >= 0 && end > start && (end - start <= (int)len)) {
+ len = end - start;
+ data_p += start;
+ }
+ }
+
+ switch(data_type) {
+ case(CD_ALLINFO):
+ /* This is too different from other text formats - handle separately */
+ copy_hex_all_info(copy_buffer, data_p, len, TRUE);
+ break;
+ case(CD_HEXCOLUMNS):
+ /* This could be done incrementally, but it is easier to mingle with the code for CD_ALLINFO */
+ copy_hex_all_info(copy_buffer, data_p, len, FALSE);
+ break;
+ case(CD_BINARY):
+ /* Completely different logic to text copies - leave copy buffer alone */
+ copy_binary_to_clipboard(data_p,len);
+ break;
+ default:
+ /* Incrementally write to text buffer in various formats */
+ while (len > 0){
+ switch(data_type) {
+ case (CD_TEXTONLY):
+ bytes_consumed = copy_hex_bytes_text_only(copy_buffer, data_p, len);
+ break;
+ case (CD_HEX):
+ bytes_consumed = copy_hex_bytes_hex(copy_buffer, data_p, len);
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+
+ g_assert(bytes_consumed>0);
+ data_p += bytes_consumed;
+ len -= bytes_consumed;
+ }
+ break;
+ }
+
+ if(copy_buffer->len > 0) {
+ copy_to_clipboard(copy_buffer);
+ }
+
+ g_string_free(copy_buffer, TRUE);
+}
+
+/* save the current highlighted hex data */
+static gboolean
+savehex_save_clicked_cb(GtkWidget * w _U_, gpointer data _U_)
+{
+ GtkWidget *bv;
+ int fd, start, end;
+ guint len;
+ const guint8 *data_p = NULL;
+ char *file;
+
+ file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(savehex_dlg));
+
+#if 0 /* Not req'd: GtkFileChooserWidget currently being used won't return with a Null filename */
+ if (!file ||! *file) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Please enter a filename!");
+ g_free(file);
+ return TRUE;
+ }
+#endif
+ if (test_for_directory(file) == EISDIR) {
+ /* It's a directory - set the file selection box to display that
+ directory, and leave the selection box displayed. */
+ set_last_open_dir(file);
+ g_free(file);
+ file_selection_set_current_folder(savehex_dlg, get_last_open_dir());
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(savehex_dlg), "");
+ return FALSE; /* do gtk_dialog_run again */
+ }
+
+ /* XXX: Must check if file name exists first */
+
+ bv = get_notebook_bv_ptr(byte_nb_ptr_gbl);
+ if (bv == NULL) {
+ /* shouldn't happen */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
+ g_free(file);
+ return TRUE;
+ }
+ /*
+ * Retrieve the info we need
+ */
+ start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
+ end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
+ data_p = get_byte_view_data_and_length(bv, &len);
+
+ if (data_p == NULL || start == -1 || start > end) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "No data selected to save!");
+ g_free(file);
+ return TRUE;
+ }
+
+ fd = ws_open(file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666);
+ if (fd == -1) {
+ open_failure_alert_box(file, errno, TRUE);
+ g_free(file);
+ return TRUE;
+ }
+ if (ws_write(fd, data_p + start, end - start) < 0) {
+ write_failure_alert_box(file, errno);
+ ws_close(fd);
+ g_free(file);
+ return TRUE;
+ }
+ if (ws_close(fd) < 0) {
+ write_failure_alert_box(file, errno);
+ g_free(file);
+ return TRUE;
+ }
+
+ /* Get rid of the dialog box */
+ g_free(file);
+#if 0 /* being handled by caller (for now) */
+ window_destroy(GTK_WIDGET(savehex_dlg));
+#endif
+ return TRUE;
+}
+
+/* Launch the dialog box to put up the file selection box etc */
+#ifdef _WIN32
+void
+savehex_cb(GtkWidget * w _U_, gpointer data _U_)
+{
+ win32_export_raw_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)));
+ return;
+}
+#else
+void
+savehex_cb(GtkWidget * w _U_, gpointer data _U_)
+{
+ int start, end;
+ guint len;
+ const guint8 *data_p = NULL;
+ gchar *label;
+ GtkWidget *bv;
+ GtkWidget *dlg_lb;
+
+ /* don't show up the dialog, if no data has to be saved */
+ bv = get_notebook_bv_ptr(byte_nb_ptr_gbl);
+ if (bv == NULL) {
+ /* shouldn't happen */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not find the corresponding text window!");
+ return;
+ }
+ start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
+ end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
+ data_p = get_byte_view_data_and_length(bv, &len);
+
+ if (data_p == NULL || start == -1 || start > end) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No data selected to save!");
+ return;
+ }
+
+#if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
+ /* if the window is already open, bring it to front */
+ if(savehex_dlg){
+ reactivate_window(savehex_dlg);
+ return;
+ }
+#endif
+ /*
+ * Build the dialog box we need.
+ */
+ savehex_dlg = file_selection_new("Wireshark: Export Selected Packet Bytes", FILE_SELECTION_SAVE);
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(savehex_dlg), TRUE);
+
+ /* label */
+ label = g_strdup_printf("Will save %u %s of raw binary data to specified file.",
+ end - start, plurality(end - start, "byte", "bytes"));
+ dlg_lb = gtk_label_new(label);
+ g_free(label);
+ file_selection_set_extra_widget(savehex_dlg, dlg_lb);
+ gtk_widget_show(dlg_lb);
+
+ g_signal_connect(savehex_dlg, "destroy", G_CALLBACK(savehex_dlg_destroy_cb), NULL);
+
+#if 0
+ if (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
+ savehex_save_clicked_cb(savehex_dlg, savehex_dlg);
+ } else {
+ window_destroy(savehex_dlg);
+ }
+#endif
+ /* "Run" the GtkFileChooserDialog. */
+ /* Upon exit: If "Accept" run the OK callback. */
+ /* If the OK callback returns with a FALSE status, re-run the dialog.*/
+ /* If not accept (ie: cancel) destroy the window. */
+ /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
+ /* return with a TRUE status so that the dialog window will be destroyed. */
+ /* Trying to re-run the dialog after popping up an alert box will not work */
+ /* since the user will not be able to dismiss the alert box. */
+ /* The (somewhat unfriendly) effect: the user must re-invoke the */
+ /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
+ /* */
+ /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
+ /* GtkFileChooserDialog. */
+ while (gtk_dialog_run(GTK_DIALOG(savehex_dlg)) == GTK_RESPONSE_ACCEPT) {
+ if (savehex_save_clicked_cb(NULL, savehex_dlg)) {
+ break; /* we're done */
+ }
+ }
+ window_destroy(savehex_dlg);
+}
+#endif
+
+static GtkTextMark *
+packet_hex_apply_reverse_tag(GtkTextBuffer *buf, int bstart, int bend, guint32 mask, int mask_le, int use_digits, int create_mark)
+{
+ GtkTextIter i_start, i_stop, iter;
+
+ GtkTextTag *revstyle_tag;
+ const char *revstyle;
+
+ int per_line = 0;
+ int per_one = 0;
+ int bits_per_one = 0;
+ int hex_offset, ascii_offset;
+
+ int start_line, start_line_pos;
+ int stop_line, stop_line_pos;
+
+ if (bstart == -1 || bend == -1)
+ return NULL;
+
+ /* Display with inverse video ? */
+ if (prefs.gui_hex_dump_highlight_style)
+ revstyle = "reverse";
+ else
+ revstyle = "bold";
+
+ revstyle_tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buf), revstyle);
+
+ switch (recent.gui_bytes_view) {
+ case BYTES_HEX:
+ per_line = BYTES_PER_LINE;
+ per_one = 2+1; /* "ff " */
+ bits_per_one = 4;
+ break;
+ case BYTES_BITS:
+ per_line = BITS_PER_LINE;
+ per_one = 8+1; /* "10101010 " */
+ bits_per_one = 1;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ start_line = bstart / per_line;
+ start_line_pos = bstart % per_line;
+
+ stop_line = bend / per_line;
+ stop_line_pos = bend % per_line;
+
+#define hex_fix(pos) hex_offset + (pos * per_one) + (pos / BYTE_VIEW_SEP) - (pos == per_line)
+#define ascii_fix(pos) ascii_offset + pos + (pos / BYTE_VIEW_SEP) - (pos == per_line)
+
+ hex_offset = use_digits + 2;
+ ascii_offset = hex_fix(per_line) + 2;
+
+ gtk_text_buffer_get_iter_at_line_index(buf, &iter, start_line, hex_fix(start_line_pos));
+
+ if (mask == 0x00) {
+ while (start_line <= stop_line) {
+ int line_pos_end = (start_line == stop_line) ? stop_line_pos : per_line;
+ int first_block_adjust = (recent.gui_bytes_view == BYTES_HEX) ? (line_pos_end == per_line/2) : 0;
+
+ if (start_line_pos == line_pos_end) break;
+
+ /* bits/hex */
+ gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, hex_fix(start_line_pos));
+ gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, hex_fix(line_pos_end)-1-first_block_adjust);
+ gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
+
+ /* ascii */
+ gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, ascii_fix(start_line_pos));
+ gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, ascii_fix(line_pos_end)-first_block_adjust);
+ gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
+
+ start_line_pos = 0;
+ start_line++;
+ }
+
+ } else if (mask_le) { /* LSB of mask first (little-endian) */
+ while (start_line <= stop_line) {
+ int line_pos_end = (start_line == stop_line) ? stop_line_pos : per_line;
+ int line_pos = start_line_pos;
+
+ while (line_pos < line_pos_end) {
+ int lop = 8 / bits_per_one;
+ int mask_per_one = (1 << bits_per_one) - 1;
+ int ascii_on = 0;
+
+ while (lop--) {
+ if ((mask & mask_per_one)) {
+ /* bits/hex */
+ gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, hex_fix(line_pos)+lop);
+ gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, hex_fix(line_pos)+lop+1);
+ gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
+
+ ascii_on = 1;
+ }
+ mask >>= bits_per_one;
+ }
+
+ /* at least one bit of ascii was one -> turn ascii on */
+ if (ascii_on) {
+ /* ascii */
+ gtk_text_buffer_get_iter_at_line_index(buf, &i_start, start_line, ascii_fix(line_pos));
+ gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, start_line, ascii_fix(line_pos)+1);
+ gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
+ }
+
+ if (!mask)
+ goto xend;
+
+ line_pos++;
+ }
+
+ start_line_pos = 0;
+ start_line++;
+ }
+ } else { /* mask starting from end (big-endian) */
+ while (start_line <= stop_line) {
+ int line_pos_start = (start_line == stop_line) ? start_line_pos : 0;
+ int line_pos = stop_line_pos-1;
+
+ while (line_pos >= line_pos_start) {
+ int lop = 8 / bits_per_one;
+ int mask_per_one = (1 << bits_per_one) - 1;
+ int ascii_on = 0;
+
+ while (lop--) {
+ if ((mask & mask_per_one)) {
+ /* bits/hex */
+ gtk_text_buffer_get_iter_at_line_index(buf, &i_start, stop_line, hex_fix(line_pos)+lop);
+ gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, stop_line, hex_fix(line_pos)+lop+1);
+ gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
+
+ ascii_on = 1;
+ }
+ mask >>= bits_per_one;
+ }
+
+ /* at least one bit of ascii was one -> turn ascii on */
+ if (ascii_on) {
+ /* ascii */
+ gtk_text_buffer_get_iter_at_line_index(buf, &i_start, stop_line, ascii_fix(line_pos));
+ gtk_text_buffer_get_iter_at_line_index(buf, &i_stop, stop_line, ascii_fix(line_pos)+1);
+ gtk_text_buffer_apply_tag(buf, revstyle_tag, &i_start, &i_stop);
+ }
+
+ if (!mask)
+ goto xend;
+
+ line_pos--;
+ }
+
+ stop_line_pos = per_line;
+ stop_line--;
+ }
+ }
+xend:
+ return (create_mark) ? gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE) : NULL;
+#undef hex_fix
+#undef ascii_fix
+}
+
+/* Update the progress bar this many times when reading a file. */
+#define N_PROGBAR_UPDATES 100
+/* The minimum packet length required to check if a progress bar is needed or not */
+#define MIN_PACKET_LENGTH 1024
+
+/*
+ * XXX - at least in GTK+ 2.x, this is not fast - in one capture with a
+ * 64K-or-so reassembled HTTP reply, it takes about 3 seconds to construct
+ * the hex dump pane on a 1.4 GHz G4 PowerMac on OS X 10.3.3. (That's
+ * presumably why there's a progress bar for it.)
+ *
+ * Perhaps what's needed is a custom widget (either one that lets you stuff
+ * text into it more quickly, or one that's a "virtual" widget so that the
+ * text for a row is constructed, via a callback, when the row is to be
+ * displayed). A custom widget might also let us treat the offset, hex
+ * data, and ASCII data as three columns, so you can select purely in
+ * the hex dump column.
+ */
+static void
+packet_hex_print_common(GtkTextBuffer *buf, GtkWidget *bv, const guint8 *pd, int len, int encoding)
+{
+ int i = 0, j, k = 0, b, cur;
+ guchar line[MAX_LINES_LEN + 1];
+ static guchar hexchars[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ static const guint8 bitmask[8] = {
+ 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
+ guchar c = '\0';
+ unsigned int use_digits;
+ GtkTextIter iter;
+
+ progdlg_t *progbar = NULL;
+ float progbar_val;
+ gboolean progbar_stop_flag;
+ GTimeVal progbar_start_time;
+ gchar progbar_status_str[100];
+ int progbar_nextstep;
+ int progbar_quantum;
+
+ gtk_text_buffer_set_text(buf, "", 0);
+ gtk_text_buffer_get_start_iter(buf, &iter);
+
+ /*
+ * How many of the leading digits of the offset will we supply?
+ * We always supply at least 4 digits, but if the maximum offset
+ * won't fit in 4 digits, we use as many digits as will be needed.
+ */
+ if (((len - 1) & 0xF0000000) != 0)
+ use_digits = 8; /* need all 8 digits */
+ else if (((len - 1) & 0x0F000000) != 0)
+ use_digits = 7; /* need 7 digits */
+ else if (((len - 1) & 0x00F00000) != 0)
+ use_digits = 6; /* need 6 digits */
+ else if (((len - 1) & 0x000F0000) != 0)
+ use_digits = 5; /* need 5 digits */
+ else
+ use_digits = 4; /* we'll supply 4 digits */
+
+ /* Record the number of digits in this text view. */
+ g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY, GUINT_TO_POINTER(use_digits));
+
+ /* Update the progress bar when it gets to this value. */
+ if (len > MIN_PACKET_LENGTH){
+ progbar_nextstep = 0;
+ }else{
+ /* If length =< MIN_PACKET_LENGTH
+ * there is no need to calculate the progress
+ */
+ progbar_nextstep = len+1;
+ }
+
+ /* When we reach the value that triggers a progress bar update,
+ bump that value by this amount. */
+ progbar_quantum = len/N_PROGBAR_UPDATES;
+ /* Progress so far. */
+ progbar_val = 0.0f;
+
+ progbar_stop_flag = FALSE;
+ g_get_current_time(&progbar_start_time);
+
+ cur = 0;
+ while (i < len) {
+ /* Create the progress bar if necessary.
+ We check on every iteration of the loop, so that it takes no
+ longer than the standard time to create it (otherwise, for a
+ large packet, we might take considerably longer than that standard
+ time in order to get to the next progress bar step). */
+ if ((progbar == NULL) && (len > MIN_PACKET_LENGTH))
+ progbar = delayed_create_progress_dlg("Processing", "Packet Details",
+ TRUE,
+ &progbar_stop_flag,
+ &progbar_start_time,
+ progbar_val);
+
+ /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
+ when we update it, we have to run the GTK+ main loop to get it
+ to repaint what's pending, and doing so may involve an "ioctl()"
+ to see if there's any pending input from an X server, and doing
+ that for every packet can be costly, especially on a big file. */
+ if (i >= progbar_nextstep) {
+
+ if (progbar != NULL) {
+ /* let's not divide by zero. I should never be started
+ * with count == 0, so let's assert that
+ */
+ g_assert(len > 0);
+ progbar_val = (gfloat) i / len;
+ g_snprintf(progbar_status_str, sizeof(progbar_status_str),
+ "%4u of %u bytes", i, len);
+ update_progress_dlg(progbar, progbar_val, progbar_status_str);
+ }
+
+ progbar_nextstep += progbar_quantum;
+ }
+
+ if (progbar_stop_flag) {
+ /* Well, the user decided to abort the operation. Just stop,
+ and arrange to return TRUE to our caller, so they know it
+ was stopped explicitly. */
+ break;
+ }
+
+ /* Print the line number */
+ j = use_digits;
+ do {
+ j--;
+ c = (i >> (j*4)) & 0xF;
+ line[cur++] = hexchars[c];
+ } while (j != 0);
+ line[cur++] = ' ';
+ line[cur++] = ' ';
+
+ j = i;
+ switch (recent.gui_bytes_view) {
+ case BYTES_HEX:
+ k = i + BYTES_PER_LINE;
+ break;
+ case BYTES_BITS:
+ k = i + BITS_PER_LINE;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ /* Print the hex bit */
+ while (i < k) {
+ if (i < len) {
+ switch (recent.gui_bytes_view) {
+ case BYTES_HEX:
+ line[cur++] = hexchars[(pd[i] & 0xf0) >> 4];
+ line[cur++] = hexchars[pd[i] & 0x0f];
+ break;
+ case BYTES_BITS:
+ for (b = 0; b < 8; b++) {
+ line[cur++] = (pd[i] & bitmask[b]) ? '1' : '0';
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ } else {
+ switch (recent.gui_bytes_view) {
+ case BYTES_HEX:
+ line[cur++] = ' '; line[cur++] = ' ';
+ break;
+ case BYTES_BITS:
+ for (b = 0; b < 8; b++) {
+ line[cur++] = ' ';
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ }
+ i++;
+ /* Inter byte space if not at end of line */
+ if (i < k) {
+ line[cur++] = ' ';
+ /* insert a space every BYTE_VIEW_SEP bytes */
+ if( ( i % BYTE_VIEW_SEP ) == 0 ) {
+ line[cur++] = ' ';
+ }
+ }
+ }
+
+ /* Print some space at the end of the line */
+ line[cur++] = ' '; line[cur++] = ' '; line[cur++] = ' ';
+
+ /* Print the ASCII bit */
+ i = j;
+
+ while (i < k) {
+ if (i < len) {
+ if (encoding == PACKET_CHAR_ENC_CHAR_ASCII) {
+ c = pd[i];
+ }
+ else if (encoding == PACKET_CHAR_ENC_CHAR_EBCDIC) {
+ c = EBCDIC_to_ASCII1(pd[i]);
+ }
+ else {
+ g_assert_not_reached();
+ }
+ line[cur++] = isprint(c) ? c : '.';
+ } else {
+ line[cur++] = ' ';
+ }
+ i++;
+ if (i < k) {
+ /* insert a space every BYTE_VIEW_SEP bytes */
+ if( ( i % BYTE_VIEW_SEP ) == 0 ) {
+ line[cur++] = ' ';
+ }
+ }
+ }
+ line[cur++] = '\n';
+ if (cur >= (MAX_LINES_LEN - MAX_LINE_LEN)) {
+ gtk_text_buffer_insert(buf, &iter, line, cur);
+ cur = 0;
+ }
+ }
+
+ /* We're done printing the packets; destroy the progress bar if
+ it was created. */
+ if (progbar != NULL)
+ destroy_progress_dlg(progbar);
+
+ if (cur) {
+ gtk_text_buffer_insert(buf, &iter, line, cur);
+ }
+}
+
+static void
+packet_hex_update(GtkWidget *bv, const guint8 *pd, int len, int bstart,
+ int bend, guint32 bmask, int bmask_le,
+ int astart, int aend, int encoding)
+{
+ GtkTextView *bv_text_view = GTK_TEXT_VIEW(bv);
+ GtkTextBuffer *buf = gtk_text_view_get_buffer(bv_text_view);
+ GtkTextMark *mark;
+ int ndigits;
+
+ GtkTextIter start, end;
+
+ g_object_ref(buf);
+
+#if 0
+ /* XXX: Setting the text_view buffer to NULL is apparently
+ * not a good idea; If a progress_bar is displayed below
+ * in delayed_create_progress_dlg() there will then be
+ * a crash internally in the gtk library.
+ * (It appears that gtk_text_view_set_buffer
+ * queues a callback to be run when this
+ * thread is next idle. Unfortunately the call to
+ * gtk_main_iteration() in delayed_create_progress_dlg()
+ * causes the internal callback to be run which then
+ * crashes (because the textview has no buffer ?))
+ */
+ gtk_text_view_set_buffer(bv_text_view, NULL);
+#endif
+
+ /* attach a dummy buffer in place of the real buffer.
+ * (XXX: Presumably this is done so there's no attempt
+ * to display the real buffer until it has been
+ * completely generated).
+ */
+ gtk_text_view_set_buffer(bv_text_view, gtk_text_buffer_new(NULL));
+
+ packet_hex_print_common(buf, bv, pd, len, encoding);
+
+ /* mark everything with "plain" tag */
+ gtk_text_buffer_get_start_iter(buf, &start);
+ gtk_text_buffer_get_end_iter(buf, &end);
+ gtk_text_buffer_apply_tag_by_name(buf, "plain", &start, &end);
+
+ ndigits = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_NDIGITS_KEY));
+
+ /* mark reverse tags */
+ mark = packet_hex_apply_reverse_tag(buf, bstart, bend, bmask, bmask_le, ndigits, 1);
+ packet_hex_apply_reverse_tag(buf, astart, aend, 0x00, 0, ndigits, 0);
+
+ gtk_text_view_set_buffer(bv_text_view, buf);
+
+ /* scroll text into position */
+ if (mark) {
+ gtk_text_view_scroll_to_mark(bv_text_view, mark, 0.0, TRUE, 1.0, 0.0);
+ gtk_text_buffer_delete_mark(buf, mark);
+ }
+ g_object_unref(buf);
+}
+
+void
+packet_hex_print(GtkWidget *bv, const guint8 *pd, frame_data *fd,
+ field_info *finfo, guint len)
+{
+ /* do the initial printing and save the information needed */
+ /* to redraw the display if preferences change. */
+
+ int bstart = -1, bend = -1, blen = -1;
+ guint32 bmask = 0x00; int bmask_le = 0;
+ int astart = -1, aend = -1, alen = -1;
+
+
+ if (finfo != NULL) {
+
+ if (cfile.search_in_progress && (cfile.hex || (cfile.string && cfile.packet_data))) {
+ /* In the hex view, only highlight the target bytes or string. The entire
+ field can then be displayed by clicking on any of the bytes in the field. */
+ if (cfile.hex) {
+ blen = (int)strlen(cfile.sfilter)/2;
+ } else {
+ blen = (int)strlen(cfile.sfilter);
+ }
+ bstart = cfile.search_pos - (blen-1);
+
+ } else {
+ blen = finfo->length;
+ bstart = finfo->start;
+ }
+
+ /* bmask = finfo->hfinfo->bitmask << finfo->hfinfo->bitshift; */ /* (value & mask) >> shift */
+ if (finfo->hfinfo) bmask = finfo->hfinfo->bitmask;
+ astart = finfo->appendix_start;
+ alen = finfo->appendix_length;
+
+ if (FI_GET_FLAG(finfo, FI_LITTLE_ENDIAN))
+ bmask_le = 1;
+ else if (FI_GET_FLAG(finfo, FI_BIG_ENDIAN))
+ bmask_le = 0;
+ else { /* unknown endianess - disable mask
+ bmask_le = (G_BYTE_ORDER == G_LITTLE_ENDIAN);
+ */
+ bmask = 0x00;
+ }
+
+ if (bmask == 0x00) {
+ int bito = FI_GET_BITS_OFFSET(finfo);
+ int bitc = FI_GET_BITS_SIZE(finfo);
+ int bitt = bito + bitc;
+
+ /* construct mask using bito & bitc */
+ /* XXX, mask has only 32 bit, later we can store bito&bitc, and use them (which should be faster) */
+ if (bitt > 0 && bitt < 32) {
+
+ bmask = ((1 << bitc) - 1) << (8-(bitt & 0x7)); /* always? */
+ bmask_le = 0; /* ? */
+ }
+ }
+ }
+
+ if (bstart >= 0 && blen > 0) {
+ bend = bstart + blen;
+ }
+ if (astart >= 0 && alen > 0) {
+ aend = astart + alen;
+ }
+
+ if (bend == -1 && aend != -1) {
+ bstart = astart;
+ bmask = 0x00;
+ bend = aend;
+ astart = aend = -1;
+ }
+
+ /* don't exceed the end of available data */
+ if (aend != -1 && (guint)aend > len) aend = len;
+ if (bend != -1 && (guint)bend > len) bend = len;
+
+ /* save the information needed to redraw the text */
+ /* should we save the fd & finfo pointers instead ?? */
+ g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bstart));
+ g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bend));
+ g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY, GINT_TO_POINTER(bmask));
+ g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY, GINT_TO_POINTER(bmask_le));
+ g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(astart));
+ g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(aend));
+ g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY,
+ GUINT_TO_POINTER((guint)fd->flags.encoding));
+
+ /* stig: it should be done only for bitview... */
+ if (recent.gui_bytes_view != BYTES_BITS)
+ bmask = 0x00;
+ packet_hex_update(bv, pd, len, bstart, bend, bmask, bmask_le, astart, aend, fd->flags.encoding);
+}
+
+void
+packet_hex_editor_print(GtkWidget *bv, const guint8 *pd, frame_data *fd, int offset, int bitoffset, guint len)
+{
+ /* do the initial printing and save the information needed */
+ /* to redraw the display if preferences change. */
+
+ int bstart = offset, bend = (bstart != -1) ? offset+1 : -1;
+ guint32 bmask; int bmask_le = 0;
+ int astart = -1, aend = -1;
+
+ switch (recent.gui_bytes_view) {
+ case BYTES_HEX:
+ bmask = (bitoffset == 0) ? 0xf0 : (bitoffset == 4) ? 0x0f : 0xff;
+ break;
+
+ case BYTES_BITS:
+ bmask = (1 << (7-bitoffset));
+ break;
+
+ default:
+ bmask = 0x00;
+ g_assert_not_reached();
+ break;
+ }
+
+ /* save the information needed to redraw the text */
+ g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY, GINT_TO_POINTER(bstart));
+ g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY, GINT_TO_POINTER(bend));
+ g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY, GINT_TO_POINTER(bmask));
+ g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY, GINT_TO_POINTER(bmask_le));
+ g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY, GINT_TO_POINTER(astart));
+ g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY, GINT_TO_POINTER(aend));
+ g_object_set_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY,
+ GUINT_TO_POINTER((guint)fd->flags.encoding));
+
+ packet_hex_update(bv, pd, len, bstart, bend, bmask, bmask_le, astart, aend, fd->flags.encoding);
+}
+
+/*
+ * Redraw the text using the saved information; usually called if
+ * the preferences have changed.
+ */
+void
+packet_hex_reprint(GtkWidget *bv)
+{
+ int start, end, mask, mask_le, encoding;
+ int astart, aend;
+ const guint8 *data;
+ guint len = 0;
+
+ start = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_START_KEY));
+ end = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_END_KEY));
+ mask = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASK_KEY));
+ mask_le = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_MASKLE_KEY));
+ astart = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_START_KEY));
+ aend = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_APP_END_KEY));
+ data = get_byte_view_data_and_length(bv, &len);
+ g_assert(data != NULL);
+ encoding = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(bv), E_BYTE_VIEW_ENCODE_KEY));
+
+ /* stig: it should be done only for bitview... */
+ if (recent.gui_bytes_view != BYTES_BITS)
+ mask = 0x00;
+ packet_hex_update(bv, data, len, start, end, mask, mask_le, astart, aend, encoding);
+}
+
+/* List of all protocol tree widgets, so we can globally set the selection
+ mode and font of all of them. */
+static GList *ptree_widgets;
+
+/* Add a protocol tree widget to the list of protocol tree widgets. */
+static void forget_ptree_widget(GtkWidget *ptreew, gpointer data);
+
+static void
+remember_ptree_widget(GtkWidget *ptreew)
+{
+ ptree_widgets = g_list_append(ptree_widgets, ptreew);
+
+ /* Catch the "destroy" event on the widget, so that we remove it from
+ the list when it's destroyed. */
+ g_signal_connect(ptreew, "destroy", G_CALLBACK(forget_ptree_widget), NULL);
+}
+
+/* Remove a protocol tree widget from the list of protocol tree widgets. */
+static void
+forget_ptree_widget(GtkWidget *ptreew, gpointer data _U_)
+{
+ ptree_widgets = g_list_remove(ptree_widgets, ptreew);
+}
+
+/* Set the selection mode of a given packet tree window. */
+static void
+set_ptree_sel_browse(GtkWidget *tree, gboolean val)
+{
+ GtkTreeSelection *selection;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
+ /* Yeah, GTK uses "browse" in the case where we do not, but oh well.
+ I think "browse" in Wireshark makes more sense than "SINGLE" in
+ GTK+ */
+ if (val) {
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+ }
+ else {
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_BROWSE);
+ }
+}
+
+static void
+set_ptree_sel_browse_cb(gpointer data, gpointer user_data)
+{
+ set_ptree_sel_browse((GtkWidget *)data, *(gboolean *)user_data);
+}
+
+/* Set the selection mode of all packet tree windows. */
+void
+set_ptree_sel_browse_all(gboolean val)
+{
+ g_list_foreach(ptree_widgets, set_ptree_sel_browse_cb, &val);
+}
+
+static void
+set_ptree_font_cb(gpointer data, gpointer user_data)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_font((GtkWidget *)data,
+ (PangoFontDescription *)user_data);
+#else
+ gtk_widget_modify_font((GtkWidget *)data,
+ (PangoFontDescription *)user_data);
+#endif
+}
+
+void
+set_ptree_font_all(PangoFontDescription *font)
+{
+ g_list_foreach(ptree_widgets, set_ptree_font_cb, font);
+}
+
+
+/*
+ * Each expert_color_* level below should match the light gradient
+ * colors in image/expert_indicators.svg.
+ */
+static gboolean colors_ok = FALSE;
+
+GdkColor expert_color_chat = { 0, 0x8080, 0xb7b7, 0xf7f7 }; /* light blue */
+GdkColor expert_color_note = { 0, 0xa0a0, 0xffff, 0xffff }; /* bright turquoise */
+GdkColor expert_color_warn = { 0, 0xf7f7, 0xf2f2, 0x5353 }; /* yellow */
+GdkColor expert_color_error = { 0, 0xffff, 0x5c5c, 0x5c5c }; /* pale red */
+GdkColor expert_color_foreground = { 0, 0x0000, 0x0000, 0x0000 }; /* black */
+GdkColor hidden_proto_item = { 0, 0x4444, 0x4444, 0x4444 }; /* gray */
+
+gchar *expert_color_chat_str;
+gchar *expert_color_note_str;
+gchar *expert_color_warn_str;
+gchar *expert_color_error_str;
+gchar *expert_color_foreground_str;
+
+void proto_draw_colors_init(void)
+{
+ if(colors_ok) {
+ return;
+ }
+#if 0
+ /* Allocating collor isn't necessary? */
+ get_color(&expert_color_chat);
+ get_color(&expert_color_note);
+ get_color(&expert_color_warn);
+ get_color(&expert_color_error);
+ get_color(&expert_color_foreground);
+#endif
+ expert_color_chat_str = gdk_color_to_string(&expert_color_chat);
+ expert_color_note_str = gdk_color_to_string(&expert_color_note);
+ expert_color_warn_str = gdk_color_to_string(&expert_color_warn);
+ expert_color_error_str = gdk_color_to_string(&expert_color_error);
+ expert_color_foreground_str = gdk_color_to_string(&expert_color_foreground);
+
+#if 0
+ get_color(&hidden_proto_item);
+#endif
+ colors_ok = TRUE;
+}
+
+
+static void
+tree_cell_renderer(GtkTreeViewColumn *tree_column _U_, GtkCellRenderer *cell,
+ GtkTreeModel *tree_model, GtkTreeIter *iter,
+ gpointer data _U_)
+{
+ field_info *fi;
+
+ gtk_tree_model_get(tree_model, iter, 1, &fi, -1);
+
+ if(!colors_ok) {
+ proto_draw_colors_init();
+ }
+
+ /* for the various possible attributes, see:
+ * http://developer.gnome.org/doc/API/2.0/gtk/GtkCellRendererText.html
+ *
+ * color definitions can be found at:
+ * http://cvs.gnome.org/viewcvs/gtk+/gdk-pixbuf/io-xpm.c?rev=1.42
+ * (a good color overview: http://www.computerhope.com/htmcolor.htm)
+ *
+ * some experiences:
+ * background-gdk: doesn't seem to work (probably the GdkColor must be allocated)
+ * weight/style: doesn't take any effect
+ */
+
+ /* for each field, we have to reset the renderer attributes */
+ g_object_set (cell, "foreground-set", FALSE, NULL);
+
+ g_object_set (cell, "background-set", FALSE, NULL);
+
+ g_object_set (cell, "underline", PANGO_UNDERLINE_NONE, NULL);
+ g_object_set (cell, "underline-set", FALSE, NULL);
+
+ /*g_object_set (cell, "style", PANGO_STYLE_NORMAL, NULL);
+ g_object_set (cell, "style-set", FALSE, NULL);*/
+
+ /*g_object_set (cell, "weight", PANGO_WEIGHT_NORMAL, NULL);
+ g_object_set (cell, "weight-set", FALSE, NULL);*/
+
+ if(FI_GET_FLAG(fi, FI_GENERATED)) {
+ /* we use "[...]" to mark generated items, no need to change things here */
+
+ /* as some fonts don't support italic, don't use this */
+ /*g_object_set (cell, "style", PANGO_STYLE_ITALIC, NULL);
+ g_object_set (cell, "style-set", TRUE, NULL);
+ */
+ /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
+ g_object_set (cell, "weight-set", TRUE, NULL);*/
+ }
+
+ if(FI_GET_FLAG(fi, FI_HIDDEN)) {
+ g_object_set (cell, "foreground-gdk", &hidden_proto_item, NULL);
+ g_object_set (cell, "foreground-set", TRUE, NULL);
+ }
+
+ if (fi && fi->hfinfo) {
+ if(fi->hfinfo->type == FT_PROTOCOL) {
+ g_object_set (cell, "background", "gray90", NULL);
+ g_object_set (cell, "background-set", TRUE, NULL);
+ g_object_set (cell, "foreground", "black", NULL);
+ g_object_set (cell, "foreground-set", TRUE, NULL);
+ /*g_object_set (cell, "weight", PANGO_WEIGHT_BOLD, NULL);
+ g_object_set (cell, "weight-set", TRUE, NULL);*/
+ }
+
+ if((fi->hfinfo->type == FT_FRAMENUM) ||
+ (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type))) {
+ render_as_url(cell);
+ }
+ }
+
+ if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
+ switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) {
+ case(PI_CHAT):
+ g_object_set (cell, "background-gdk", &expert_color_chat, NULL);
+ g_object_set (cell, "background-set", TRUE, NULL);
+ break;
+ case(PI_NOTE):
+ g_object_set (cell, "background-gdk", &expert_color_note, NULL);
+ g_object_set (cell, "background-set", TRUE, NULL);
+ break;
+ case(PI_WARN):
+ g_object_set (cell, "background-gdk", &expert_color_warn, NULL);
+ g_object_set (cell, "background-set", TRUE, NULL);
+ break;
+ case(PI_ERROR):
+ g_object_set (cell, "background-gdk", &expert_color_error, NULL);
+ g_object_set (cell, "background-set", TRUE, NULL);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ g_object_set (cell, "foreground", "black", NULL);
+ g_object_set (cell, "foreground-set", TRUE, NULL);
+ }
+}
+
+GtkWidget *
+main_tree_view_new(e_prefs *prefs_p, GtkWidget **tree_view_p)
+{
+ GtkWidget *tv_scrollw, *tree_view;
+ GtkTreeStore *store;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ gint col_offset;
+
+ /* Tree view */
+ tv_scrollw = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(tv_scrollw),
+ GTK_SHADOW_IN);
+
+ store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_POINTER);
+ tree_view = tree_view_new(GTK_TREE_MODEL(store));
+ g_object_unref(G_OBJECT(store));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE);
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set (renderer, "ypad", 0, NULL);
+ col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view),
+ -1, "Name", renderer,
+ "text", 0, NULL);
+ column = gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view),
+ col_offset - 1);
+ gtk_tree_view_column_set_cell_data_func(column, renderer, tree_cell_renderer,
+ NULL, NULL);
+
+ gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
+ GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ g_signal_connect(tree_view, "row-expanded", G_CALLBACK(expand_tree), NULL);
+ g_signal_connect(tree_view, "row-collapsed", G_CALLBACK(collapse_tree), NULL);
+ gtk_container_add( GTK_CONTAINER(tv_scrollw), tree_view );
+ set_ptree_sel_browse(tree_view, prefs_p->gui_ptree_sel_browse);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_font(tree_view, user_font_get_regular());
+#else
+ gtk_widget_modify_font(tree_view, user_font_get_regular());
+#endif
+ remember_ptree_widget(tree_view);
+
+ *tree_view_p = tree_view;
+
+ return tv_scrollw;
+}
+
+void
+expand_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
+{
+ int i;
+ for(i=0; i < num_tree_types; i++) {
+ tree_is_expanded[i] = TRUE;
+ }
+ gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view));
+}
+
+void
+collapse_all_tree(proto_tree *protocol_tree _U_, GtkWidget *tree_view)
+{
+ int i;
+ for(i=0; i < num_tree_types; i++) {
+ tree_is_expanded[i] = FALSE;
+ }
+ gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree_view));
+}
+
+
+struct proto_tree_draw_info {
+ GtkTreeView *tree_view;
+ GtkTreeIter *iter;
+};
+
+void
+main_proto_tree_draw(proto_tree *protocol_tree)
+{
+ proto_tree_draw(protocol_tree, tree_view_gbl);
+}
+
+
+static void
+tree_view_follow_link(field_info *fi)
+{
+ gchar *url;
+
+ if(fi->hfinfo->type == FT_FRAMENUM) {
+ cf_goto_frame(&cfile, fi->value.value.uinteger);
+ }
+ if(FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type)) {
+ url = fvalue_to_string_repr(&fi->value, FTREPR_DISPLAY, NULL);
+ if(url){
+ browser_open_url(url);
+ g_free(url);
+ }
+ }
+}
+
+
+/* If the user selected a position in the tree view, try to find
+ * the item in the GUI proto_tree that corresponds to that byte, and
+ * select it. */
+gboolean
+tree_view_select(GtkWidget *widget, GdkEventButton *event)
+{
+ GtkTreeSelection *sel;
+ GtkTreePath *path;
+
+ if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
+ (gint) (((GdkEventButton *)event)->x),
+ (gint) (((GdkEventButton *)event)->y),
+ &path, NULL, NULL, NULL))
+ {
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
+
+ /* if that's a doubleclick, try to follow the link */
+ if(event->type == GDK_2BUTTON_PRESS) {
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ field_info *fi;
+
+ if(gtk_tree_selection_get_selected (sel, &model, &iter)) {
+ gtk_tree_model_get(model, &iter, 1, &fi, -1);
+ tree_view_follow_link(fi);
+ }
+ }
+ else if (((GdkEventButton *)event)->button != 1) {
+ /* if button == 1 gtk_tree_selection_select_path is already (or will be) called by the widget */
+ gtk_tree_selection_select_path(sel, path);
+ }
+ } else {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* fill the whole protocol tree with the string values */
+void
+proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view)
+{
+ GtkTreeStore *store;
+ struct proto_tree_draw_info info;
+
+ info.tree_view = GTK_TREE_VIEW(tree_view);
+ info.iter = NULL;
+
+ store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view)));
+
+ /*
+ * Clear out any crud left over in the display of the protocol
+ * tree, by removing all nodes from the tree.
+ * This is how it's done in testgtk.c in GTK+.
+ */
+ gtk_tree_store_clear(store);
+
+ proto_tree_children_foreach(protocol_tree, proto_tree_draw_node, &info);
+}
+
+
+/* fill a single protocol tree item with the string value */
+static void
+proto_tree_draw_node(proto_node *node, gpointer data)
+{
+ struct proto_tree_draw_info info;
+ struct proto_tree_draw_info *parent_info = (struct proto_tree_draw_info*) data;
+
+ field_info *fi = PNODE_FINFO(node);
+ gchar label_str[ITEM_LABEL_LENGTH];
+ gchar *label_ptr;
+ gboolean is_leaf, is_expanded;
+ GtkTreeStore *store;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ g_assert(fi && "dissection with an invisible proto tree?");
+
+ if (PROTO_ITEM_IS_HIDDEN(node) && !prefs.display_hidden_proto_items)
+ return;
+
+ /* was a free format label produced? */
+ if (fi->rep) {
+ label_ptr = fi->rep->representation;
+ }
+ else { /* no, make a generic label */
+ label_ptr = label_str;
+ proto_item_fill_label(fi, label_str);
+ }
+
+ if (node->first_child != NULL) {
+ is_leaf = FALSE;
+ g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types);
+ if (tree_is_expanded[fi->tree_type]) {
+ is_expanded = TRUE;
+ }
+ else {
+ is_expanded = FALSE;
+ }
+ }
+ else {
+ is_leaf = TRUE;
+ is_expanded = FALSE;
+ }
+
+ if (PROTO_ITEM_IS_GENERATED(node)) {
+ if (PROTO_ITEM_IS_HIDDEN(node)) {
+ label_ptr = g_strdup_printf("<[%s]>", label_ptr);
+ } else {
+ label_ptr = g_strdup_printf("[%s]", label_ptr);
+ }
+ } else if (PROTO_ITEM_IS_HIDDEN(node)) {
+ label_ptr = g_strdup_printf("<%s>", label_ptr);
+ }
+
+ info.tree_view = parent_info->tree_view;
+ store = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(info.tree_view)));
+ gtk_tree_store_append(store, &iter, parent_info->iter);
+ gtk_tree_store_set(store, &iter, 0, label_ptr, 1, fi, -1);
+
+ if (PROTO_ITEM_IS_GENERATED(node) || PROTO_ITEM_IS_HIDDEN(node)) {
+ g_free(label_ptr);
+ }
+
+ if (!is_leaf) {
+ info.iter = &iter;
+ proto_tree_children_foreach(node, proto_tree_draw_node, &info);
+ path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
+ if (is_expanded)
+ gtk_tree_view_expand_to_path(info.tree_view, path);
+ else
+ gtk_tree_view_collapse_row(info.tree_view, path);
+ gtk_tree_path_free(path);
+ }
+}
+
+/*
+ * Clear the hex dump and protocol tree panes.
+ */
+void
+clear_tree_and_hex_views(void)
+{
+ /* Clear the hex dump by getting rid of all the byte views. */
+ while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr_gbl), 0) != NULL)
+ gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr_gbl), 0);
+
+ /* Add a placeholder byte view so that there's at least something
+ displayed in the byte view notebook. */
+ add_byte_tab(byte_nb_ptr_gbl, "", NULL, NULL, tree_view_gbl);
+
+ /* Clear the protocol tree by removing all nodes in the ctree.
+ This is how it's done in testgtk.c in GTK+ */
+ gtk_tree_store_clear(GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view_gbl))));
+}
+
+void
+select_bytes_view (GtkWidget *w _U_, gpointer data _U_, gint view)
+{
+ if (recent.gui_bytes_view != view) {
+ recent.gui_bytes_view = view;
+ redraw_packet_bytes_all();
+ }
+}
diff --git a/ui/gtk/main_proto_draw.h b/ui/gtk/main_proto_draw.h
new file mode 100644
index 0000000000..a4699f718c
--- /dev/null
+++ b/ui/gtk/main_proto_draw.h
@@ -0,0 +1,252 @@
+/* proto_draw.h
+ * Definitions for GTK+ packet display structures and routines
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MAIN_PROTO_DRAW_H__
+#define __MAIN_PROTO_DRAW_H__
+
+/** @file
+ * Packet tree and details panes.
+ * @ingroup main_window_group
+ */
+
+/** Create byte views in the main window.
+ */
+void add_main_byte_views(epan_dissect_t *edt);
+
+/** Display the protocol tree in the main window.
+ */
+void main_proto_tree_draw(proto_tree *protocol_tree);
+
+/** Clear the hex dump and protocol tree panes.
+ */
+void clear_tree_and_hex_views(void);
+
+
+/** Get the current text notebook page of the packet details notebook.
+ *
+ * @param nb_ptr the notebook widget
+ * @return the notebook page
+ */
+extern GtkWidget *get_notebook_bv_ptr(GtkWidget *nb_ptr);
+
+/**
+ * Get the data and length for a byte view, given the byte view page widget.
+ *
+ * @param byte_view the byte view to look at
+ * @param data_len set "*data_len" to the length
+ * @return the pointer, or NULL on error
+ */
+extern const guint8 *get_byte_view_data_and_length(GtkWidget *byte_view,
+ guint *data_len);
+
+/** Set the current text page of the notebook to the window that
+ * refers to a particular tvbuff.
+ *
+ * @param nb_ptr the byte view notebook
+ * @param tvb the tvbuff to look at
+ */
+extern void set_notebook_page(GtkWidget *nb_ptr, tvbuff_t *tvb);
+
+/** Redraw a given byte view window.
+ *
+ * @param nb_ptr the byte view notebook
+ * @param fd selected frame
+ * @param finfo selected field_info
+ */
+extern void redraw_packet_bytes(GtkWidget *nb_ptr, frame_data *fd, field_info *finfo);
+
+/** Redraw all byte view windows. */
+extern void redraw_packet_bytes_all(void);
+
+/** Create a new byte view (packet details pane).
+ *
+ * @return the new byte view
+ */
+extern GtkWidget *byte_view_new(void);
+
+/** Clear and fill all the byte view notebook tabs.
+ *
+ * @param edt current dissections
+ * @param tree_view the corresponding packet tree
+ * @param nb_ptr the byte view notebook
+ */
+extern void add_byte_views(epan_dissect_t *edt, GtkWidget *tree_view,
+ GtkWidget *nb_ptr);
+
+/** Gdk button click appeared, select the byte view from that position.
+ *
+ * @param widget the byte view
+ * @param event the button event clicked
+ * @return TRUE if could be selected
+ */
+extern gboolean byte_view_select(GtkWidget *widget, GdkEventButton *event);
+
+/** This highlights the field in the proto tree that is at position byte
+ *
+ * @param tvb the current tvbuff
+ * @param byte the byte offset within the packet to highlight
+ * @param tree_view the current tree_view
+ * @param tree the current tree
+ * @return TRUE if highlighting was successful
+ */
+gboolean
+highlight_field(tvbuff_t *tvb, gint byte, GtkTreeView *tree_view,
+ proto_tree *tree);
+
+/** Callback for "Export Selected Packet Bytes" operation.
+ *
+ * @param w unused
+ * @param data unused
+ */
+extern void savehex_cb(GtkWidget * w, gpointer data);
+
+/** Format of packet data to copy to clipboard.
+ * Lower nibble holds data type, next nibble holds flags.
+ */
+typedef enum {
+ CD_ALLINFO, /* All information - columated hex with text in separate column */
+ CD_TEXTONLY, /* Printable characters */
+ CD_HEX, /* Hex, space separated, no linebreaks */
+ CD_HEXCOLUMNS, /* Like "All Information" but with no ASCII */
+ CD_BINARY, /* Raw binary octets */
+
+ CD_TYPEMASK = 0x0000FFFF, /* Mask for extracting type */
+ CD_FLAGSMASK = 0xFFFF0000, /* Mask for extracting flags */
+
+ CD_FLAGS_SELECTEDONLY = 0x00010000 /* Copy only selected bytes */
+} copy_data_type;
+
+
+/** Callback for "Copy packet bytes to clipboard" operation.
+ *
+ * @param w unused
+ * @param data unused
+ * @param data_type copy_data_type
+ *
+ */
+extern void copy_hex_cb(GtkWidget * w, gpointer data, copy_data_type data_type);
+
+/** Redraw a given byte view window.
+ *
+ * @param bv the byte view
+ * @param pd the packet data
+ * @param fd the current fame
+ * @param finfo the current field info
+ * @param len the byte view length
+ */
+extern void packet_hex_print(GtkWidget *bv, const guint8 *pd, frame_data *fd,
+ field_info *finfo, guint len);
+
+extern void packet_hex_editor_print(GtkWidget *bv, const guint8 *pd, frame_data *fd,
+ int offset, int bitoffset, guint len);
+
+/**
+ * Redraw the text using the saved information. Usually called if
+ * the preferences have changed.
+ *
+ * @param bv the byte view
+ */
+extern void packet_hex_reprint(GtkWidget *bv);
+
+/** Set a new font for all protocol trees.
+ *
+ * @param font the new font
+ */
+extern void set_ptree_font_all(PangoFontDescription *font);
+
+/** Find field in tree view by field_info.
+ *
+ * @param tree_view the tree view to look at
+ * @param finfo the field info the look for
+ * @return the path to the field
+ */
+extern GtkTreePath *tree_find_by_field_info(GtkTreeView *tree_view, field_info *finfo);
+
+/** Create a new tree view (packet details).
+ *
+ * @param prefs current preferences
+ * @param tree_view_p fill in the new tree view
+ * @return the new scrolled window (parent of the tree view)
+ */
+extern GtkWidget * main_tree_view_new(e_prefs *prefs, GtkWidget **tree_view_p);
+
+/** Clear and redraw the whole tree view.
+ *
+ * @param protocol_tree the currently dissected protocol tree
+ * @param tree_view the tree view to redraw
+ */
+extern void proto_tree_draw(proto_tree *protocol_tree, GtkWidget *tree_view);
+
+/** Expand the whole tree view.
+ *
+ * @param protocol_tree the currently dissected protocol tree
+ * @param tree_view the tree view to redraw
+ */
+extern void expand_all_tree(proto_tree *protocol_tree, GtkWidget *tree_view);
+
+/** Collapse the whole tree view.
+ *
+ * @param protocol_tree the currently dissected protocol tree
+ * @param tree_view the tree view to redraw
+ */
+extern void collapse_all_tree(proto_tree *protocol_tree, GtkWidget *tree_view);
+
+/** Gdk button click appeared, select the byte view from that position.
+ *
+ * @param widget the tree view
+ * @param event the button event clicked
+ * @return TRUE if could be selected
+ */
+extern gboolean tree_view_select(GtkWidget *widget, GdkEventButton *event);
+
+/** Set the selection mode of all packet tree windows.
+ *
+ * @param val GTK_SELECTION_SINGLE if TRUE, GTK_SELECTION_BROWSE if FALSE
+ */
+extern void set_ptree_sel_browse_all(gboolean val);
+
+typedef enum {
+ BYTES_HEX,
+ BYTES_BITS
+} bytes_view_type;
+extern void select_bytes_view (GtkWidget *widget, gpointer data, gint view);
+
+/** init the expert colors */
+extern void proto_draw_colors_init(void);
+
+/** the expert colors */
+extern GdkColor expert_color_chat;
+extern GdkColor expert_color_note;
+extern GdkColor expert_color_warn;
+extern GdkColor expert_color_error;
+extern GdkColor expert_color_foreground;
+
+/* string representation of expert colors */
+extern gchar *expert_color_chat_str;
+extern gchar *expert_color_note_str;
+extern gchar *expert_color_warn_str;
+extern gchar *expert_color_error_str;
+extern gchar *expert_color_foreground_str;
+
+#endif /* __MAIN_PROTO_DRAW_H__ */
diff --git a/ui/gtk/main_statusbar.c b/ui/gtk/main_statusbar.c
new file mode 100644
index 0000000000..a52abfd69d
--- /dev/null
+++ b/ui/gtk/main_statusbar.c
@@ -0,0 +1,934 @@
+/* main_statusbar.c
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <gtk/gtk.h>
+
+#include <epan/epan.h>
+#include <epan/filesystem.h>
+#include <epan/epan_dissect.h>
+#include <epan/expert.h>
+#include <epan/prefs.h>
+
+#include "../cfile.h"
+#include "../file.h"
+#ifdef HAVE_LIBPCAP
+#include "../capture_opts.h"
+#include "../capture_ui_utils.h"
+#include "../capture.h"
+#endif
+
+#include "ui/gtk/recent.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/main_statusbar_private.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/expert_comp_dlg.h"
+#include "ui/gtk/profile_dlg.h"
+#include "ui/gtk/main_welcome.h"
+#include "ui/gtk/expert_indicators.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/menus.h"
+
+#include "main_statusbar.h"
+
+/*
+ * The order below defines the priority of info bar contexts.
+ */
+typedef enum {
+ STATUS_LEVEL_MAIN,
+ STATUS_LEVEL_FILE,
+ STATUS_LEVEL_FILTER,
+ STATUS_LEVEL_HELP,
+ NUM_STATUS_LEVELS
+} status_level_e;
+
+
+#ifdef HAVE_LIBPCAP
+#define DEF_READY_MESSAGE " Ready to load or capture"
+#else
+#define DEF_READY_MESSAGE " Ready to load file"
+#endif
+
+
+static GtkWidget *status_pane_left, *status_pane_right;
+static GtkWidget *info_bar, *info_bar_event, *packets_bar, *profile_bar, *profile_bar_event;
+static GtkWidget *expert_info_error, *expert_info_warn, *expert_info_note;
+static GtkWidget *expert_info_chat, *expert_info_none;
+
+static guint main_ctx, file_ctx, help_ctx, filter_ctx, packets_ctx, profile_ctx;
+static guint status_levels[NUM_STATUS_LEVELS];
+static GString *packets_str = NULL;
+static gchar *profile_str = NULL;
+
+
+static void info_bar_new(void);
+static void packets_bar_new(void);
+static void profile_bar_new(void);
+static void status_expert_new(void);
+
+/* Temporary message timeouts */
+#define TEMPORARY_MSG_TIMEOUT (7 * 1000)
+#define TEMPORARY_FLASH_TIMEOUT (1 * 1000)
+#define TEMPORARY_FLASH_INTERVAL (TEMPORARY_FLASH_TIMEOUT / 4)
+static gint flash_time;
+static gboolean flash_highlight = FALSE;
+
+static void
+statusbar_push_file_msg(const gchar *msg_format, ...)
+ G_GNUC_PRINTF(1, 2);
+
+/*
+ * Return TRUE if there are any higher priority status level messages pushed.
+ * Return FALSE otherwise.
+ */
+static gboolean
+higher_priority_status_level(int level)
+{
+ int i;
+
+ for (i = level + 1; i < NUM_STATUS_LEVELS; i++) {
+ if (status_levels[i])
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Push a formatted message referring to file access onto the statusbar.
+ */
+static void
+statusbar_push_file_msg(const gchar *msg_format, ...)
+{
+ va_list ap;
+ gchar *msg;
+
+ /*g_warning("statusbar_push: %s", msg);*/
+ if (higher_priority_status_level(STATUS_LEVEL_FILE))
+ return;
+ status_levels[STATUS_LEVEL_FILE]++;
+
+ va_start(ap, msg_format);
+ msg = g_strdup_vprintf(msg_format, ap);
+ va_end(ap);
+
+ gtk_statusbar_push(GTK_STATUSBAR(info_bar), file_ctx, msg);
+ g_free(msg);
+}
+
+/*
+ * Pop a message referring to file access off the statusbar.
+ */
+static void
+statusbar_pop_file_msg(void)
+{
+ /*g_warning("statusbar_pop");*/
+ if (status_levels[STATUS_LEVEL_FILE] > 0) {
+ status_levels[STATUS_LEVEL_FILE]--;
+ }
+ gtk_statusbar_pop(GTK_STATUSBAR(info_bar), file_ctx);
+}
+
+/*
+ * Push a formatted message referring to the currently-selected field onto
+ * the statusbar.
+ */
+void
+statusbar_push_field_msg(const gchar *msg_format, ...)
+{
+ va_list ap;
+ gchar *msg;
+
+ if (higher_priority_status_level(STATUS_LEVEL_HELP))
+ return;
+ status_levels[STATUS_LEVEL_HELP]++;
+
+ va_start(ap, msg_format);
+ msg = g_strdup_vprintf(msg_format, ap);
+ va_end(ap);
+
+ gtk_statusbar_push(GTK_STATUSBAR(info_bar), help_ctx, msg);
+ g_free(msg);
+}
+
+/*
+ * Pop a message referring to the currently-selected field off the statusbar.
+ */
+void
+statusbar_pop_field_msg(void)
+{
+ if (status_levels[STATUS_LEVEL_HELP] > 0) {
+ status_levels[STATUS_LEVEL_HELP]--;
+ }
+ gtk_statusbar_pop(GTK_STATUSBAR(info_bar), help_ctx);
+}
+
+/*
+ * Push a formatted message referring to the current filter onto the statusbar.
+ */
+void
+statusbar_push_filter_msg(const gchar *msg_format, ...)
+{
+ va_list ap;
+ gchar *msg;
+
+ if (higher_priority_status_level(STATUS_LEVEL_FILTER))
+ return;
+ status_levels[STATUS_LEVEL_FILTER]++;
+
+ va_start(ap, msg_format);
+ msg = g_strdup_vprintf(msg_format, ap);
+ va_end(ap);
+
+ gtk_statusbar_push(GTK_STATUSBAR(info_bar), filter_ctx, msg);
+ g_free(msg);
+}
+
+/*
+ * Pop a message referring to the current filter off the statusbar.
+ */
+void
+statusbar_pop_filter_msg(void)
+{
+ if (status_levels[STATUS_LEVEL_FILTER] > 0) {
+ status_levels[STATUS_LEVEL_FILTER]--;
+ }
+ gtk_statusbar_pop(GTK_STATUSBAR(info_bar), filter_ctx);
+}
+
+/*
+ * Timeout callbacks for statusbar_push_temporary_msg
+ */
+static gboolean
+statusbar_remove_temporary_msg(gpointer data)
+{
+ guint msg_id = GPOINTER_TO_UINT(data);
+
+ gtk_statusbar_remove(GTK_STATUSBAR(info_bar), main_ctx, msg_id);
+
+ return FALSE;
+}
+
+static gboolean
+statusbar_flash_temporary_msg(gpointer data _U_)
+{
+ gboolean retval = TRUE;
+
+ if (flash_time > 0) {
+ flash_highlight = !flash_highlight;
+ } else {
+ flash_highlight = FALSE;
+ retval = FALSE;
+ }
+
+ /*
+ * As of 2.18.3 gtk_drag_highlight just draws a border around the widget
+ * so we can abuse it here.
+ */
+ if (flash_highlight) {
+ gtk_drag_highlight(info_bar);
+ } else {
+ gtk_drag_unhighlight(info_bar);
+ }
+
+ flash_time -= TEMPORARY_FLASH_INTERVAL;
+
+ return retval;
+}
+
+/*
+ * Push a formatted temporary message onto the statusbar.
+ */
+void
+statusbar_push_temporary_msg(const gchar *msg_format, ...)
+{
+ va_list ap;
+ gchar *msg;
+ guint msg_id;
+
+ va_start(ap, msg_format);
+ msg = g_strdup_vprintf(msg_format, ap);
+ va_end(ap);
+
+ msg_id = gtk_statusbar_push(GTK_STATUSBAR(info_bar), main_ctx, msg);
+ g_free(msg);
+
+ flash_time = TEMPORARY_FLASH_TIMEOUT - 1;
+ g_timeout_add(TEMPORARY_FLASH_INTERVAL, statusbar_flash_temporary_msg, NULL);
+
+ g_timeout_add(TEMPORARY_MSG_TIMEOUT, statusbar_remove_temporary_msg, GUINT_TO_POINTER(msg_id));
+}
+
+
+GtkWidget *
+statusbar_new(void)
+{
+ GtkWidget *status_hbox;
+
+ /* Status hbox */
+ status_hbox = gtk_hbox_new(FALSE, 1);
+ gtk_container_set_border_width(GTK_CONTAINER(status_hbox), 0);
+
+ /* info (main) statusbar */
+ info_bar_new();
+
+ /* packets statusbar */
+ packets_bar_new();
+
+ /* profile statusbar */
+ profile_bar_new();
+
+ /* expert info indicator */
+ status_expert_new();
+
+ /* Pane for the statusbar */
+ status_pane_left = gtk_hpaned_new();
+ gtk_widget_show(status_pane_left);
+ status_pane_right = gtk_hpaned_new();
+ gtk_widget_show(status_pane_right);
+
+ return status_hbox;
+}
+
+void
+statusbar_load_window_geometry(void)
+{
+ if (recent.has_gui_geometry_status_pane && recent.gui_geometry_status_pane_left)
+ gtk_paned_set_position(GTK_PANED(status_pane_left), recent.gui_geometry_status_pane_left);
+ if (recent.has_gui_geometry_status_pane && recent.gui_geometry_status_pane_right)
+ gtk_paned_set_position(GTK_PANED(status_pane_right), recent.gui_geometry_status_pane_right);
+}
+
+void
+statusbar_save_window_geometry(void)
+{
+ recent.gui_geometry_status_pane_left = gtk_paned_get_position(GTK_PANED(status_pane_left));
+ recent.gui_geometry_status_pane_right = gtk_paned_get_position(GTK_PANED(status_pane_right));
+}
+
+
+/*
+ * Helper for statusbar_widgets_emptying()
+ */
+static void
+foreach_remove_a_child(GtkWidget *widget, gpointer data) {
+ gtk_container_remove(GTK_CONTAINER(data), widget);
+}
+
+void
+statusbar_widgets_emptying(GtkWidget *statusbar)
+{
+ g_object_ref(G_OBJECT(info_bar));
+ g_object_ref(G_OBJECT(info_bar_event));
+ g_object_ref(G_OBJECT(packets_bar));
+ g_object_ref(G_OBJECT(profile_bar));
+ g_object_ref(G_OBJECT(profile_bar_event));
+ g_object_ref(G_OBJECT(status_pane_left));
+ g_object_ref(G_OBJECT(status_pane_right));
+ g_object_ref(G_OBJECT(expert_info_error));
+ g_object_ref(G_OBJECT(expert_info_warn));
+ g_object_ref(G_OBJECT(expert_info_note));
+ g_object_ref(G_OBJECT(expert_info_chat));
+ g_object_ref(G_OBJECT(expert_info_none));
+
+ /* empty all containers participating */
+ gtk_container_foreach(GTK_CONTAINER(statusbar), foreach_remove_a_child, statusbar);
+ gtk_container_foreach(GTK_CONTAINER(status_pane_left), foreach_remove_a_child, status_pane_left);
+ gtk_container_foreach(GTK_CONTAINER(status_pane_right), foreach_remove_a_child, status_pane_right);
+}
+
+void
+statusbar_widgets_pack(GtkWidget *statusbar)
+{
+ gtk_box_pack_start(GTK_BOX(statusbar), expert_info_error, FALSE, FALSE, 2);
+ gtk_box_pack_start(GTK_BOX(statusbar), expert_info_warn, FALSE, FALSE, 2);
+ gtk_box_pack_start(GTK_BOX(statusbar), expert_info_note, FALSE, FALSE, 2);
+ gtk_box_pack_start(GTK_BOX(statusbar), expert_info_chat, FALSE, FALSE, 2);
+ gtk_box_pack_start(GTK_BOX(statusbar), expert_info_none, FALSE, FALSE, 2);
+ gtk_box_pack_start(GTK_BOX(statusbar), status_pane_left, TRUE, TRUE, 0);
+ gtk_paned_pack1(GTK_PANED(status_pane_left), info_bar_event, FALSE, FALSE);
+ gtk_paned_pack2(GTK_PANED(status_pane_left), status_pane_right, TRUE, FALSE);
+ gtk_paned_pack1(GTK_PANED(status_pane_right), packets_bar, TRUE, FALSE);
+ gtk_paned_pack2(GTK_PANED(status_pane_right), profile_bar_event, FALSE, FALSE);
+}
+
+void
+statusbar_widgets_show_or_hide(GtkWidget *statusbar)
+{
+ /*
+ * Show the status hbox if either:
+ *
+ * 1) we're showing the filter toolbar and we want it in the status
+ * line
+ *
+ * or
+ *
+ * 2) we're showing the status bar.
+ */
+ if ((recent.filter_toolbar_show && prefs.filter_toolbar_show_in_statusbar) ||
+ recent.statusbar_show) {
+ gtk_widget_show(statusbar);
+ } else {
+ gtk_widget_hide(statusbar);
+ }
+
+ if (recent.statusbar_show) {
+ gtk_widget_show(status_pane_left);
+ } else {
+ gtk_widget_hide(status_pane_left);
+ }
+}
+
+
+static void
+info_bar_new(void)
+{
+ info_bar_event = gtk_event_box_new();
+ info_bar = gtk_statusbar_new();
+ gtk_container_add(GTK_CONTAINER(info_bar_event), info_bar);
+ main_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "main");
+ file_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "file");
+ help_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "help");
+ filter_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(info_bar), "filter");
+#if !GTK_CHECK_VERSION(3,0,0)
+ gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(info_bar), FALSE);
+#endif
+ gtk_statusbar_push(GTK_STATUSBAR(info_bar), main_ctx, DEF_READY_MESSAGE);
+
+ memset(status_levels, 0, sizeof(status_levels));
+
+ gtk_widget_show(info_bar);
+ gtk_widget_show(info_bar_event);
+}
+
+static void
+packets_bar_new(void)
+{
+ /* tip: tooltips don't work on statusbars! */
+ packets_bar = gtk_statusbar_new();
+ packets_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(packets_bar), "packets");
+ packets_bar_update();
+#if !GTK_CHECK_VERSION(3,0,0)
+ gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(packets_bar), FALSE);
+#endif
+
+ gtk_widget_show(packets_bar);
+}
+
+static void
+profile_bar_new(void)
+{
+ profile_bar_event = gtk_event_box_new();
+ profile_bar = gtk_statusbar_new();
+ gtk_container_add(GTK_CONTAINER(profile_bar_event), profile_bar);
+ g_signal_connect(profile_bar_event, "button_press_event", G_CALLBACK(profile_show_popup_cb), NULL);
+ g_signal_connect(profile_bar_event, "button_press_event", G_CALLBACK(popup_menu_handler),
+ g_object_get_data(G_OBJECT(popup_menu_object), PM_STATUSBAR_PROFILES_KEY));
+ profile_ctx = gtk_statusbar_get_context_id(GTK_STATUSBAR(profile_bar), "profile");
+ gtk_widget_set_tooltip_text(profile_bar_event, "Click to change configuration profile");
+ profile_bar_update();
+
+ gtk_widget_show(profile_bar);
+ gtk_widget_show(profile_bar_event);
+}
+
+
+/*
+ * Update the packets statusbar to the current values
+ */
+void
+packets_bar_update(void)
+{
+ if(packets_bar) {
+ /* Remove old status */
+ if(packets_str) {
+ gtk_statusbar_pop(GTK_STATUSBAR(packets_bar), packets_ctx);
+ } else {
+ packets_str = g_string_new ("");
+ }
+
+ /* Do we have any packets? */
+ if(cfile.count) {
+ g_string_printf(packets_str, " Packets: %u Displayed: %u Marked: %u",
+ cfile.count, cfile.displayed_count, cfile.marked_count);
+ if(cfile.drops_known) {
+ g_string_append_printf(packets_str, " Dropped: %u", cfile.drops);
+ }
+ if(cfile.ignored_count > 0) {
+ g_string_append_printf(packets_str, " Ignored: %u", cfile.ignored_count);
+ }
+ if(!cfile.is_tempfile){
+ /* Loading an existing file */
+ gulong computed_elapsed = cf_get_computed_elapsed();
+ g_string_append_printf(packets_str, " Load time: %lu:%02lu.%03lu",
+ computed_elapsed/60000,
+ computed_elapsed%60000/1000,
+ computed_elapsed%1000);
+ }
+ } else {
+ g_string_printf(packets_str, " No Packets");
+ }
+ gtk_statusbar_push(GTK_STATUSBAR(packets_bar), packets_ctx, packets_str->str);
+ }
+}
+
+/*
+ * Update the packets statusbar to the current values
+ */
+void
+profile_bar_update(void)
+{
+ if (profile_bar) {
+ /* remove old status */
+ if(profile_str) {
+ g_free(profile_str);
+ gtk_statusbar_pop(GTK_STATUSBAR(profile_bar), profile_ctx);
+ }
+
+ profile_str = g_strdup_printf (" Profile: %s", get_profile_name ());
+ gtk_statusbar_push(GTK_STATUSBAR(profile_bar), profile_ctx, profile_str);
+
+ set_menus_for_profiles(is_default_profile());
+ }
+}
+
+static gboolean
+expert_comp_dlg_event_cb(GtkWidget *w _U_, GdkEventButton *event _U_, gpointer user_data _U_)
+{
+ expert_comp_dlg_launch();
+ return TRUE;
+}
+
+static void
+status_expert_new(void)
+{
+ GtkWidget *expert_image;
+
+ expert_image = pixbuf_to_widget(expert_error_pb_data);
+ gtk_widget_set_tooltip_text(expert_image, "ERROR is the highest expert info level");
+ gtk_widget_show(expert_image);
+ expert_info_error = gtk_event_box_new();
+ gtk_container_add(GTK_CONTAINER(expert_info_error), expert_image);
+ g_signal_connect(expert_info_error, "button_press_event", G_CALLBACK(expert_comp_dlg_event_cb), NULL);
+
+ expert_image = pixbuf_to_widget(expert_warn_pb_data);
+ gtk_widget_set_tooltip_text(expert_image, "WARNING is the highest expert info level");
+ gtk_widget_show(expert_image);
+ expert_info_warn = gtk_event_box_new();
+ gtk_container_add(GTK_CONTAINER(expert_info_warn), expert_image);
+ g_signal_connect(expert_info_warn, "button_press_event", G_CALLBACK(expert_comp_dlg_event_cb), NULL);
+
+ expert_image = pixbuf_to_widget(expert_note_pb_data);
+ gtk_widget_set_tooltip_text(expert_image, "NOTE is the highest expert info level");
+ gtk_widget_show(expert_image);
+ expert_info_note = gtk_event_box_new();
+ gtk_container_add(GTK_CONTAINER(expert_info_note), expert_image);
+ g_signal_connect(expert_info_note, "button_press_event", G_CALLBACK(expert_comp_dlg_event_cb), NULL);
+
+ expert_image = pixbuf_to_widget(expert_chat_pb_data);
+ gtk_widget_set_tooltip_text(expert_image, "CHAT is the highest expert info level");
+ gtk_widget_show(expert_image);
+ expert_info_chat = gtk_event_box_new();
+ gtk_container_add(GTK_CONTAINER(expert_info_chat), expert_image);
+ g_signal_connect(expert_info_chat, "button_press_event", G_CALLBACK(expert_comp_dlg_event_cb), NULL);
+
+ expert_image = pixbuf_to_widget(expert_none_pb_data);
+ gtk_widget_set_tooltip_text(expert_image, "No expert info");
+ gtk_widget_show(expert_image);
+ expert_info_none = gtk_event_box_new();
+ gtk_container_add(GTK_CONTAINER(expert_info_none), expert_image);
+ g_signal_connect(expert_info_none, "button_press_event", G_CALLBACK(expert_comp_dlg_event_cb), NULL);
+ gtk_widget_show(expert_info_none);
+}
+
+static void
+status_expert_hide(void)
+{
+ /* reset expert info indicator */
+ gtk_widget_hide(expert_info_error);
+ gtk_widget_hide(expert_info_warn);
+ gtk_widget_hide(expert_info_note);
+ gtk_widget_hide(expert_info_chat);
+ gtk_widget_hide(expert_info_none);
+}
+
+void
+status_expert_update(void)
+{
+ status_expert_hide();
+
+ switch(expert_get_highest_severity()) {
+ case(PI_ERROR):
+ gtk_widget_show(expert_info_error);
+ break;
+ case(PI_WARN):
+ gtk_widget_show(expert_info_warn);
+ break;
+ case(PI_NOTE):
+ gtk_widget_show(expert_info_note);
+ break;
+ case(PI_CHAT):
+ gtk_widget_show(expert_info_chat);
+ break;
+ default:
+ gtk_widget_show(expert_info_none);
+ break;
+ }
+}
+
+static void
+statusbar_set_filename(const char *file_name, gint64 file_length, nstime_t *file_elapsed_time)
+{
+ gchar *size_str;
+
+ /* expert info indicator */
+ status_expert_update();
+
+ /* statusbar */
+ /* convert file size */
+ if (file_length/1024/1024 > 10) {
+ size_str = g_strdup_printf("%" G_GINT64_MODIFIER "d MB", file_length/1024/1024);
+ } else if (file_length/1024 > 10) {
+ size_str = g_strdup_printf("%" G_GINT64_MODIFIER "d KB", file_length/1024);
+ } else {
+ size_str = g_strdup_printf("%" G_GINT64_MODIFIER "d Bytes", file_length);
+ }
+
+ statusbar_push_file_msg(" File: \"%s\" %s %02lu:%02lu:%02lu",
+ (file_name) ? file_name : "", size_str,
+ (long)file_elapsed_time->secs/3600,
+ (long)file_elapsed_time->secs%3600/60,
+ (long)file_elapsed_time->secs%60);
+ g_free(size_str);
+}
+
+
+static void
+statusbar_cf_file_closing_cb(capture_file *cf _U_)
+{
+ /* Clear any file-related status bar messages.
+ XXX - should be "clear *ALL* file-related status bar messages;
+ will there ever be more than one on the stack? */
+ statusbar_pop_file_msg();
+
+ /* reset expert info indicator */
+ status_expert_hide();
+ gtk_widget_show(expert_info_none);
+}
+
+
+static void
+statusbar_cf_file_closed_cb(capture_file *cf _U_)
+{
+ /* go back to "No packets" */
+ packets_bar_update();
+}
+
+
+static void
+statusbar_cf_file_read_started_cb(capture_file *cf)
+{
+ const gchar *name_ptr;
+
+ /* Ensure we pop any previous loaded filename */
+ statusbar_pop_file_msg();
+
+ name_ptr = get_basename(cf->filename);
+
+ statusbar_push_file_msg(" Loading: %s", name_ptr);
+}
+
+
+static void
+statusbar_cf_file_read_finished_cb(capture_file *cf)
+{
+ statusbar_pop_file_msg();
+ statusbar_set_filename(cf->filename, cf->f_datalen, &(cf->elapsed_time));
+}
+
+
+#ifdef HAVE_LIBPCAP
+static void
+statusbar_capture_prepared_cb(capture_options *capture_opts _U_)
+{
+ static const gchar msg[] = " Waiting for capture input data ...";
+ statusbar_push_file_msg(msg);
+ welcome_header_push_msg(msg);
+}
+
+static GString *
+statusbar_get_interface_names(capture_options *capture_opts)
+{
+ guint i;
+ GString *interface_names;
+
+ interface_names = g_string_new("");
+
+#ifdef _WIN32
+ if (capture_opts->ifaces->len < 2) {
+#else
+ if (capture_opts->ifaces->len < 4) {
+#endif
+ for (i = 0; i < capture_opts->ifaces->len; i++) {
+ if (i > 0) {
+ g_string_append_printf(interface_names, ", ");
+ }
+ g_string_append_printf(interface_names, "%s", get_iface_description_for_interface(capture_opts, i));
+ }
+ } else {
+ g_string_append_printf(interface_names, "%u interfaces", capture_opts->ifaces->len);
+ }
+ if (strlen (interface_names->str) > 0) {
+ g_string_append(interface_names, ":");
+ }
+ g_string_append(interface_names, " ");
+ return (interface_names);
+}
+
+static void
+statusbar_capture_update_started_cb(capture_options *capture_opts)
+{
+ GString *interface_names;
+
+ statusbar_pop_file_msg();
+ welcome_header_pop_msg();
+
+ interface_names = statusbar_get_interface_names(capture_opts);
+ statusbar_push_file_msg("%s<live capture in progress> to file: %s",
+ interface_names->str,
+ (capture_opts->save_file) ? capture_opts->save_file : "");
+ g_string_free(interface_names, TRUE);
+}
+
+static void
+statusbar_capture_update_continue_cb(capture_options *capture_opts)
+{
+ GString *interface_names;
+ capture_file *cf = capture_opts->cf;
+
+ status_expert_update();
+
+ statusbar_pop_file_msg();
+
+ interface_names = statusbar_get_interface_names(capture_opts);
+ if (cf->f_datalen/1024/1024 > 10) {
+ statusbar_push_file_msg("%s<live capture in progress> File: %s %" G_GINT64_MODIFIER "d MB",
+ interface_names->str,
+ capture_opts->save_file,
+ cf->f_datalen/1024/1024);
+ } else if (cf->f_datalen/1024 > 10) {
+ statusbar_push_file_msg("%s<live capture in progress> File: %s %" G_GINT64_MODIFIER "d KB",
+ interface_names->str,
+ capture_opts->save_file,
+ cf->f_datalen/1024);
+ } else {
+ statusbar_push_file_msg("%s<live capture in progress> File: %s %" G_GINT64_MODIFIER "d Bytes",
+ interface_names->str,
+ capture_opts->save_file,
+ cf->f_datalen);
+ }
+ g_string_free(interface_names, TRUE);
+}
+
+static void
+statusbar_capture_update_finished_cb(capture_options *capture_opts)
+{
+ capture_file *cf = capture_opts->cf;
+
+ /* Pop the "<live capture in progress>" message off the status bar. */
+ statusbar_pop_file_msg();
+ statusbar_set_filename(cf->filename, cf->f_datalen, &(cf->elapsed_time));
+ packets_bar_update();
+}
+
+static void
+statusbar_capture_fixed_started_cb(capture_options *capture_opts)
+{
+ GString *interface_names;
+
+ statusbar_pop_file_msg();
+
+ interface_names = statusbar_get_interface_names(capture_opts);
+ statusbar_push_file_msg("%s<live capture in progress> to file: %s",
+ interface_names->str,
+ (capture_opts->save_file) ? capture_opts->save_file : "");
+
+ gtk_statusbar_push(GTK_STATUSBAR(packets_bar), packets_ctx, " Packets: 0");
+ g_string_free(interface_names, TRUE);
+}
+
+static void
+statusbar_capture_fixed_continue_cb(capture_options *capture_opts)
+{
+ capture_file *cf = capture_opts->cf;
+ gchar *capture_msg;
+
+
+ gtk_statusbar_pop(GTK_STATUSBAR(packets_bar), packets_ctx);
+ capture_msg = g_strdup_printf(" Packets: %u", cf_get_packet_count(cf));
+ gtk_statusbar_push(GTK_STATUSBAR(packets_bar), packets_ctx, capture_msg);
+ g_free(capture_msg);
+}
+
+
+static void
+statusbar_capture_fixed_finished_cb(capture_options *capture_opts _U_)
+{
+#if 0
+ capture_file *cf = capture_opts->cf;
+#endif
+
+ /* Pop the "<live capture in progress>" message off the status bar. */
+ statusbar_pop_file_msg();
+ welcome_header_pop_msg();
+
+ /* Pop the "<capturing>" message off the status bar */
+ gtk_statusbar_pop(GTK_STATUSBAR(packets_bar), packets_ctx);
+}
+
+#endif /* HAVE_LIBPCAP */
+
+
+static void
+statusbar_cf_field_unselected_cb(capture_file *cf _U_)
+{
+ statusbar_pop_field_msg();
+}
+
+static void
+statusbar_cf_file_save_started_cb(gchar *filename)
+{
+ statusbar_pop_file_msg();
+ statusbar_push_file_msg(" Saving: %s...", get_basename(filename));
+}
+
+static void
+statusbar_cf_file_save_finished_cb(gpointer data _U_)
+{
+ /* Pop the "Saving:" message off the status bar. */
+ statusbar_pop_file_msg();
+}
+
+static void
+statusbar_cf_file_reload_finished_cb(capture_file *cf)
+{
+ statusbar_pop_file_msg();
+ statusbar_set_filename(cf->filename, cf->f_datalen, &(cf->elapsed_time));
+}
+
+
+static void
+statusbar_cf_file_save_failed_cb(gpointer data _U_)
+{
+ /* Pop the "Saving:" message off the status bar. */
+ statusbar_pop_file_msg();
+}
+
+
+
+void
+statusbar_cf_callback(gint event, gpointer data, gpointer user_data _U_)
+{
+ switch(event) {
+ case(cf_cb_file_closing):
+ statusbar_cf_file_closing_cb(data);
+ break;
+ case(cf_cb_file_closed):
+ statusbar_cf_file_closed_cb(data);
+ break;
+ case(cf_cb_file_read_started):
+ statusbar_cf_file_read_started_cb(data);
+ break;
+ case(cf_cb_file_read_finished):
+ statusbar_cf_file_read_finished_cb(data);
+ break;
+ case(cf_cb_packet_selected):
+ break;
+ case(cf_cb_packet_unselected):
+ break;
+ case(cf_cb_field_unselected):
+ statusbar_cf_field_unselected_cb(data);
+ break;
+ case(cf_cb_file_save_started):
+ statusbar_cf_file_save_started_cb(data);
+ break;
+ case(cf_cb_file_save_finished):
+ statusbar_cf_file_save_finished_cb(data);
+ break;
+ case(cf_cb_file_save_reload_finished):
+ statusbar_cf_file_reload_finished_cb(data);
+ break;
+ case(cf_cb_file_save_failed):
+ statusbar_cf_file_save_failed_cb(data);
+ break;
+ default:
+ g_warning("statusbar_cf_callback: event %u unknown", event);
+ g_assert_not_reached();
+ }
+}
+
+#ifdef HAVE_LIBPCAP
+void
+statusbar_capture_callback(gint event, capture_options *capture_opts,
+ gpointer user_data _U_)
+{
+ switch(event) {
+ case(capture_cb_capture_prepared):
+ statusbar_capture_prepared_cb(capture_opts);
+ break;
+ case(capture_cb_capture_update_started):
+ statusbar_capture_update_started_cb(capture_opts);
+ break;
+ case(capture_cb_capture_update_continue):
+ statusbar_capture_update_continue_cb(capture_opts);
+ break;
+ case(capture_cb_capture_update_finished):
+ statusbar_capture_update_finished_cb(capture_opts);
+ break;
+ case(capture_cb_capture_fixed_started):
+ statusbar_capture_fixed_started_cb(capture_opts);
+ break;
+ case(capture_cb_capture_fixed_continue):
+ statusbar_capture_fixed_continue_cb(capture_opts);
+ break;
+ case(capture_cb_capture_fixed_finished):
+ statusbar_capture_fixed_finished_cb(capture_opts);
+ break;
+ case(capture_cb_capture_stopping):
+ /* Beware: this state won't be called, if the capture child
+ * closes the capturing on it's own! */
+ break;
+ default:
+ g_warning("statusbar_capture_callback: event %u unknown", event);
+ g_assert_not_reached();
+ }
+}
+#endif
diff --git a/ui/gtk/main_statusbar_private.h b/ui/gtk/main_statusbar_private.h
new file mode 100644
index 0000000000..5467aac73c
--- /dev/null
+++ b/ui/gtk/main_statusbar_private.h
@@ -0,0 +1,42 @@
+/* main_statusbar_private.h
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MAIN_STATUSBAR_PRIVATE_H__
+#define __MAIN_STATUSBAR_PRIVATE_H__
+
+/*** PRIVATE INTERFACE BETWEEN main.c AND main_statusbar.c DON'T USE OR TOUCH :-)*/
+
+GtkWidget *statusbar_new(void);
+void statusbar_load_window_geometry(void);
+void statusbar_save_window_geometry(void);
+void statusbar_widgets_emptying(GtkWidget *statusbar);
+void statusbar_widgets_pack(GtkWidget *statusbar);
+void statusbar_widgets_show_or_hide(GtkWidget *statusbar);
+void statusbar_cf_callback(gint event, gpointer data, gpointer user_data);
+#ifdef HAVE_LIBPCAP
+void statusbar_capture_callback(gint event, capture_options *capture_opts,
+ gpointer user_data);
+#endif
+
+#endif /* __MAIN_STATUSBAR_PRIVATE_H__ */
+
diff --git a/ui/gtk/main_toolbar.c b/ui/gtk/main_toolbar.c
new file mode 100644
index 0000000000..1a0616a72d
--- /dev/null
+++ b/ui/gtk/main_toolbar.c
@@ -0,0 +1,426 @@
+/* toolbar.c
+ * The main toolbar
+ * Copyright 2003, Ulf Lamping <ulf.lamping@web.de>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * This file implements the "main" toolbar for Wireshark.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <epan/prefs.h>
+#include <epan/dfilter/dfilter.h>
+
+#include "../color_filters.h"
+
+#ifdef HAVE_LIBPCAP
+#include "ui/gtk/capture_dlg.h"
+#include "ui/gtk/capture_if_dlg.h"
+#endif /* HAVE_LIBPCAP */
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/capture_file_dlg.h"
+#include "ui/gtk/find_dlg.h"
+#include "ui/gtk/goto_dlg.h"
+#include "ui/gtk/color_dlg.h"
+#include "ui/gtk/prefs_dlg.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/menus.h"
+#include "ui/gtk/main_toolbar.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/recent.h"
+#include "ui/gtk/packet_history.h"
+#include "ui/gtk/new_packet_list.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+static gboolean toolbar_init = FALSE;
+
+#ifdef HAVE_LIBPCAP
+static GtkToolItem *capture_options_button, *new_button, *stop_button, *clear_button, *if_button;
+static GtkToolItem *capture_filter_button, *autoscroll_button;
+#endif /* HAVE_LIBPCAP */
+static GtkToolItem *open_button, *save_button, *close_button, *reload_button;
+static GtkToolItem *print_button, *find_button, *history_forward_button, *history_back_button;
+static GtkToolItem *go_to_button, *go_to_top_button, *go_to_bottom_button;
+static GtkToolItem *display_filter_button;
+static GtkToolItem *zoom_in_button, *zoom_out_button, *zoom_100_button, *colorize_button;
+static GtkToolItem *resize_columns_button;
+static GtkToolItem *color_display_button, *prefs_button, *help_button;
+
+#define SAVE_BUTTON_TOOLTIP_TEXT "Save this capture file..."
+#define SAVE_AS_BUTTON_TOOLTIP_TEXT "Save this capture file as..."
+
+
+/*
+ * Redraw all toolbars
+ */
+void
+toolbar_redraw_all(void)
+{
+ GtkWidget *main_tb;
+ GtkWidget *filter_tb;
+
+ main_tb = g_object_get_data(G_OBJECT(top_level), E_TB_MAIN_KEY);
+
+ gtk_toolbar_set_style(GTK_TOOLBAR(main_tb),
+ prefs.gui_toolbar_main_style);
+
+ filter_tb = g_object_get_data(G_OBJECT(top_level), E_TB_FILTER_KEY);
+
+ /* In case the filter toolbar hasn't been built */
+ if(filter_tb)
+ gtk_toolbar_set_style(GTK_TOOLBAR(filter_tb),
+ prefs.gui_toolbar_filter_style);
+}
+
+/* Enable or disable toolbar items based on whether you have a capture file
+ you've finished reading. */
+void set_toolbar_for_capture_file(gboolean have_capture_file) {
+ if (toolbar_init) {
+ gtk_widget_set_sensitive(GTK_WIDGET(save_button), have_capture_file);
+ gtk_widget_set_sensitive(GTK_WIDGET(close_button), have_capture_file);
+ gtk_widget_set_sensitive(GTK_WIDGET(reload_button), have_capture_file);
+ }
+}
+
+/* Enable or disable menu items based on whether you have an unsaved
+ capture file you've finished reading. */
+void set_toolbar_for_unsaved_capture_file(gboolean have_unsaved_capture_file) {
+
+ if (toolbar_init) {
+ if(have_unsaved_capture_file) {
+ gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(save_button),
+ GTK_STOCK_SAVE);
+ gtk_widget_set_tooltip_text(GTK_WIDGET(save_button),SAVE_BUTTON_TOOLTIP_TEXT);
+ g_object_set_data(G_OBJECT(save_button), "save", GINT_TO_POINTER(1));
+ } else {
+ gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(save_button),
+ GTK_STOCK_SAVE_AS);
+ gtk_widget_set_tooltip_text(GTK_WIDGET(save_button), SAVE_AS_BUTTON_TOOLTIP_TEXT);
+ g_object_set_data(G_OBJECT(save_button), "save", GINT_TO_POINTER(0));
+ }
+ /*gtk_widget_set_sensitive((GTK_WIDGET(save_button), have_unsaved_capture_file);
+ gtk_widget_set_sensitive(GTK_WIDGET(save_as_button), !have_unsaved_capture_file);*/
+ }
+}
+
+/* fudge to call correct file_save or file_save_as fcn based upon the
+ value of the "save" key associated with the save button
+*/
+
+static void file_save_or_save_as_cmd_cb(GtkWidget *w, gpointer data) {
+ if (GPOINTER_TO_INT(g_object_get_data(G_OBJECT(save_button),"save")) == 1) {
+ file_save_cmd_cb(w, data);
+ }
+ else {
+ file_save_as_cmd_cb(w, data);
+ }
+}
+
+/** The packet history has changed, we need to update the menu.
+ *
+ * @param back_history some back history entries available
+ * @param forward_history some forward history entries available
+ */
+void set_toolbar_for_packet_history(gboolean back_history, gboolean forward_history) {
+ gtk_widget_set_sensitive(GTK_WIDGET(history_back_button), back_history);
+ gtk_widget_set_sensitive(GTK_WIDGET(history_forward_button), forward_history);
+}
+
+
+/* set toolbar state "have a capture in progress" */
+void set_toolbar_for_capture_in_progress(gboolean capture_in_progress) {
+
+ if (toolbar_init) {
+#ifdef HAVE_LIBPCAP
+ gtk_widget_set_sensitive(GTK_WIDGET(capture_options_button), !capture_in_progress);
+ gtk_widget_set_sensitive(GTK_WIDGET(new_button), !capture_in_progress);
+ gtk_widget_set_sensitive(GTK_WIDGET(stop_button), capture_in_progress);
+ gtk_widget_set_sensitive(GTK_WIDGET(clear_button), capture_in_progress);
+ /*if (capture_in_progress) {
+ gtk_widget_hide(GTK_WIDGET(new_button));
+ gtk_widget_show(GTK_WIDGET(stop_button));
+ } else {
+ gtk_widget_show(GTK_WIDGET(new_button));
+ gtk_widget_hide(GTK_WIDGET(stop_button));
+ }*/
+#endif /* HAVE_LIBPCAP */
+ gtk_widget_set_sensitive(GTK_WIDGET(open_button), !capture_in_progress);
+ }
+}
+
+/* set toolbar state "have packets captured" */
+void set_toolbar_for_captured_packets(gboolean have_captured_packets) {
+
+ if (toolbar_init) {
+ gtk_widget_set_sensitive(GTK_WIDGET(print_button),
+ have_captured_packets);
+ gtk_widget_set_sensitive(GTK_WIDGET(find_button),
+ have_captured_packets);
+ gtk_widget_set_sensitive(GTK_WIDGET(history_back_button),
+ have_captured_packets);
+ gtk_widget_set_sensitive(GTK_WIDGET(history_forward_button),
+ have_captured_packets);
+ gtk_widget_set_sensitive(GTK_WIDGET(go_to_button),
+ have_captured_packets);
+ gtk_widget_set_sensitive(GTK_WIDGET(go_to_top_button),
+ have_captured_packets);
+ gtk_widget_set_sensitive(GTK_WIDGET(go_to_bottom_button),
+ have_captured_packets);
+ gtk_widget_set_sensitive(GTK_WIDGET(zoom_in_button),
+ have_captured_packets);
+ gtk_widget_set_sensitive(GTK_WIDGET(zoom_out_button),
+ have_captured_packets);
+ gtk_widget_set_sensitive(GTK_WIDGET(zoom_100_button),
+ have_captured_packets);
+ gtk_widget_set_sensitive(GTK_WIDGET(resize_columns_button),
+ have_captured_packets);
+
+ /* XXX - I don't see a reason why this should be done (as it is in the
+ * menus) */
+ /* gtk_widget_set_sensitive(GTK_WIDGET(color_display_button),
+ have_captured_packets);*/
+ }
+}
+
+
+/* helper function: add a separator to the toolbar */
+static void toolbar_append_separator(GtkWidget *toolbar) {
+ GtkToolItem *tool_item = gtk_separator_tool_item_new();
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), tool_item, -1);
+ gtk_widget_show(GTK_WIDGET(tool_item));
+}
+
+
+
+#define toolbar_item(new_item, toolbar, stock, tooltip_text, callback, user_data) { \
+ new_item = gtk_tool_button_new_from_stock(stock); \
+ gtk_widget_set_tooltip_text(GTK_WIDGET(new_item), tooltip_text); \
+ g_signal_connect(new_item, "clicked", G_CALLBACK(callback), user_data); \
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), new_item, -1); \
+ gtk_widget_show(GTK_WIDGET(new_item)); \
+ }
+
+#define toolbar_toggle_button(new_item, window, toolbar, stock, tooltip_text, callback, user_data) { \
+ new_item = gtk_toggle_tool_button_new_from_stock(stock); \
+ gtk_widget_set_tooltip_text(GTK_WIDGET(new_item), tooltip_text); \
+ g_signal_connect(new_item, "toggled", G_CALLBACK(callback), user_data); \
+ gtk_toolbar_insert(GTK_TOOLBAR(toolbar), new_item, -1); \
+ gtk_widget_show_all(GTK_WIDGET(new_item)); \
+ }
+
+#define TOGGLE_BUTTON GTK_TOGGLE_TOOL_BUTTON
+#define TOGGLE_BUTTON_GET_ACTIVE gtk_toggle_tool_button_get_active
+#define TOGGLE_BUTTON_SET_ACTIVE gtk_toggle_tool_button_set_active
+
+static void
+colorize_toggle_cb(GtkWidget *toggle_button, gpointer user_data _U_) {
+ menu_colorize_changed(TOGGLE_BUTTON_GET_ACTIVE(TOGGLE_BUTTON(toggle_button)));
+}
+
+void
+toolbar_colorize_changed(gboolean packet_list_colorize) {
+ if(TOGGLE_BUTTON_GET_ACTIVE(TOGGLE_BUTTON(colorize_button)) != packet_list_colorize) {
+ TOGGLE_BUTTON_SET_ACTIVE(TOGGLE_BUTTON(colorize_button), packet_list_colorize);
+ }
+}
+
+#ifdef HAVE_LIBPCAP
+static void
+auto_scroll_live_toggle_cb(GtkWidget *autoscroll_button_lcl, gpointer user_data _U_) {
+ menu_auto_scroll_live_changed(TOGGLE_BUTTON_GET_ACTIVE(TOGGLE_BUTTON(autoscroll_button_lcl)));
+}
+
+void
+toolbar_auto_scroll_live_changed(gboolean auto_scroll_live_lcl) {
+ if(TOGGLE_BUTTON_GET_ACTIVE(TOGGLE_BUTTON(autoscroll_button)) != auto_scroll_live_lcl) {
+ TOGGLE_BUTTON_SET_ACTIVE(TOGGLE_BUTTON(autoscroll_button), auto_scroll_live_lcl);
+ }
+}
+#endif
+
+
+/*
+ * Create all toolbars (currently only the main toolbar)
+ */
+GtkWidget *
+toolbar_new(void)
+{
+ GtkWidget *main_tb;
+ GtkWidget *window = top_level;
+
+ /* this function should be only called once! */
+ g_assert(!toolbar_init);
+
+ /* we need to realize the window because we use pixmaps for
+ * items on the toolbar in the context of it */
+ /* (coming from the gtk example, please don't ask me why ;-) */
+ gtk_widget_realize(window);
+
+ /* toolbar will be horizontal, with both icons and text (as default here) */
+ /* (this will usually be overwritten by the preferences setting) */
+ main_tb = gtk_toolbar_new();
+ gtk_orientable_set_orientation(GTK_ORIENTABLE(main_tb),
+ GTK_ORIENTATION_HORIZONTAL);
+
+ g_object_set_data(G_OBJECT(top_level), E_TB_MAIN_KEY, main_tb);
+
+
+#ifdef HAVE_LIBPCAP
+ toolbar_item(if_button, main_tb,
+ WIRESHARK_STOCK_CAPTURE_INTERFACES, "List the available capture interfaces...", capture_if_cb, NULL);
+
+ toolbar_item(capture_options_button, main_tb,
+ WIRESHARK_STOCK_CAPTURE_OPTIONS, "Show the capture options...", capture_prep_cb, NULL);
+
+ toolbar_item(new_button, main_tb,
+ WIRESHARK_STOCK_CAPTURE_START, "Start a new live capture", capture_start_cb, NULL);
+
+ toolbar_item(stop_button, main_tb,
+ WIRESHARK_STOCK_CAPTURE_STOP, "Stop the running live capture", capture_stop_cb, NULL);
+
+ toolbar_item(clear_button, main_tb,
+ WIRESHARK_STOCK_CAPTURE_RESTART, "Restart the running live capture", capture_restart_cb, NULL);
+
+ toolbar_append_separator(main_tb);
+#endif /* HAVE_LIBPCAP */
+
+ toolbar_item(open_button, main_tb,
+ GTK_STOCK_OPEN, "Open a capture file...", file_open_cmd_cb, NULL);
+
+ /* Only create a separate button in GTK < 2.4. With GTK 2.4+, we will
+ * just modify the save_button to read/show save or save as as needed.
+ * We'll also fudge in an object key ("save") for the save button with data which specifies
+ * whether the button is currently "save" (1)or "save as" (0).
+ * The fcn file_save_or_save_as_cmd_cb
+ * will then call the appropriate file_save_cmd_cb or file_save_as_cmd_cb
+ */
+
+ toolbar_item(save_button, main_tb,
+ GTK_STOCK_SAVE, SAVE_BUTTON_TOOLTIP_TEXT, file_save_or_save_as_cmd_cb, NULL);
+ g_object_set_data(G_OBJECT(save_button), "save", GINT_TO_POINTER(1));
+
+ toolbar_item(close_button, main_tb,
+ GTK_STOCK_CLOSE, "Close this capture file", file_close_cmd_cb, NULL);
+
+ toolbar_item(reload_button, main_tb,
+ GTK_STOCK_REFRESH, "Reload this capture file", file_reload_cmd_cb, NULL);
+
+ toolbar_item(print_button, main_tb,
+ GTK_STOCK_PRINT, "Print packet(s)...", file_print_cmd_cb, NULL);
+
+ toolbar_append_separator(main_tb);
+
+ toolbar_item(find_button, main_tb,
+ GTK_STOCK_FIND, "Find a packet...", find_frame_cb, NULL);
+
+ toolbar_item(history_back_button, main_tb,
+ GTK_STOCK_GO_BACK, "Go back in packet history", history_back_cb, NULL);
+
+ toolbar_item(history_forward_button, main_tb,
+ GTK_STOCK_GO_FORWARD, "Go forward in packet history", history_forward_cb, NULL);
+
+ toolbar_item(go_to_button, main_tb,
+ GTK_STOCK_JUMP_TO, "Go to the packet with number...", goto_frame_cb, NULL);
+
+ toolbar_item(go_to_top_button, main_tb,
+ GTK_STOCK_GOTO_TOP, "Go to the first packet", goto_top_frame_cb, NULL);
+
+ toolbar_item(go_to_bottom_button, main_tb,
+ GTK_STOCK_GOTO_BOTTOM, "Go to the last packet", goto_bottom_frame_cb, NULL);
+
+ toolbar_append_separator(main_tb);
+
+ toolbar_toggle_button(colorize_button, window, main_tb,
+ WIRESHARK_STOCK_COLORIZE, "Colorize Packet List", colorize_toggle_cb, NULL);
+
+#ifdef HAVE_LIBPCAP
+ toolbar_toggle_button(autoscroll_button, window, main_tb,
+ WIRESHARK_STOCK_AUTOSCROLL, "Auto Scroll Packet List in Live Capture", auto_scroll_live_toggle_cb, NULL);
+#endif
+
+ toolbar_append_separator(main_tb);
+
+ toolbar_item(zoom_in_button, main_tb,
+ GTK_STOCK_ZOOM_IN, "Zoom in", view_zoom_in_cb, NULL);
+
+ toolbar_item(zoom_out_button, main_tb,
+ GTK_STOCK_ZOOM_OUT, "Zoom out", view_zoom_out_cb, NULL);
+
+ toolbar_item(zoom_100_button, main_tb,
+ GTK_STOCK_ZOOM_100, "Zoom 100%", view_zoom_100_cb, NULL);
+
+ toolbar_item(resize_columns_button, main_tb,
+ WIRESHARK_STOCK_RESIZE_COLUMNS, "Resize All Columns", new_packet_list_resize_columns_cb, NULL);
+
+ toolbar_append_separator(main_tb);
+
+#ifdef HAVE_LIBPCAP
+ toolbar_item(capture_filter_button, main_tb,
+ WIRESHARK_STOCK_CAPTURE_FILTER, "Edit capture filter...", cfilter_dialog_cb, NULL);
+#endif /* HAVE_LIBPCAP */
+
+ toolbar_item(display_filter_button, main_tb,
+ WIRESHARK_STOCK_DISPLAY_FILTER, "Edit/apply display filter...", dfilter_dialog_cb, NULL);
+
+ toolbar_item(color_display_button, main_tb,
+ GTK_STOCK_SELECT_COLOR, "Edit coloring rules...", color_display_cb, NULL);
+
+ /* the preference button uses it's own Stock icon label "Prefs", as "Preferences" is too long */
+ toolbar_item(prefs_button, main_tb,
+ GTK_STOCK_PREFERENCES, "Edit preferences...", prefs_cb, NULL);
+
+ toolbar_append_separator(main_tb);
+
+ toolbar_item(help_button, main_tb,
+ GTK_STOCK_HELP, "Show some help...", topic_cb, GINT_TO_POINTER(HELP_CONTENT));
+
+ /* disable all "sensitive" items by default */
+ toolbar_init = TRUE;
+ set_toolbar_for_unsaved_capture_file(FALSE);
+ set_toolbar_for_captured_packets(FALSE);
+ set_toolbar_for_capture_file(FALSE);
+#ifdef HAVE_LIBPCAP
+ set_toolbar_for_capture_in_progress(FALSE);
+#endif /* HAVE_LIBPCAP */
+
+ /* make current preferences effective */
+ toolbar_redraw_all();
+
+ return main_tb;
+}
+
+void
+set_toolbar_object_data(gchar *key, gpointer data)
+{
+ g_object_set_data(G_OBJECT(open_button), key, data);
+ g_object_set_data(G_OBJECT(reload_button), key, data);
+}
diff --git a/ui/gtk/main_toolbar.h b/ui/gtk/main_toolbar.h
new file mode 100644
index 0000000000..6c040f9660
--- /dev/null
+++ b/ui/gtk/main_toolbar.h
@@ -0,0 +1,93 @@
+/* main_toolbar.h
+ * Definitions for toolbar utility routines
+ * Copyright 2003, Ulf Lamping <ulf.lamping@web.de>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __TOOLBAR_H__
+#define __TOOLBAR_H__
+
+
+/** @file
+ * The main toolbar.
+ * @ingroup main_window_group
+ */
+
+/** Create the main toolbar.
+ * @return the new toolbar
+ */
+GtkWidget *toolbar_new(void);
+
+/** Redraw the main toolbar. Used, when user changes preferences. */
+void toolbar_redraw_all(void);
+
+/** The "Colorize Packet List" option has changed.
+ */
+void toolbar_colorize_changed(gboolean packet_list_colorize);
+
+#ifdef HAVE_LIBPCAP
+/** The "Auto Scroll in Live Capture" option has changed.
+ */
+void toolbar_auto_scroll_live_changed(gboolean auto_scroll_live);
+#endif
+
+/** We have (or don't have) a capture file now.
+ *
+ * @param have_capture_file TRUE, if we have a capture file
+ */
+void set_toolbar_for_capture_file(gboolean have_capture_file);
+
+/** We have (or don't have) an unsaved capture file now.
+ *
+ * @param have_unsaved_capture_file TRUE, if we have an unsaved capture file
+ */
+void set_toolbar_for_unsaved_capture_file(gboolean have_unsaved_capture_file);
+
+/** We have (or don't have) a capture in progress now.
+ *
+ * @param have_capture_file TRUE, if we have a capture in progress file
+ */
+void set_toolbar_for_capture_in_progress(gboolean have_capture_file);
+
+/** We have (or don't have) captured packets now.
+ *
+ * @param have_captured_packets TRUE, if we have captured packets
+ */
+void set_toolbar_for_captured_packets(gboolean have_captured_packets);
+
+/** The packet history has changed, we need to update the menu.
+ *
+ * @param back_history some back history entries available
+ * @param forward_history some forward history entries available
+ */
+void set_toolbar_for_packet_history(gboolean back_history, gboolean forward_history);
+
+/** Set object data of some buttons (where needed). It's needed so callback
+ * functions can read back their required data. Acts like g_object_set_data()
+ * on multiple buttons.
+ *
+ * @param key the key
+ * @param data the data to set
+ */
+void set_toolbar_object_data(gchar *key, gpointer data);
+
+#endif /* __TOOLBAR_H__ */
diff --git a/ui/gtk/main_welcome.c b/ui/gtk/main_welcome.c
new file mode 100644
index 0000000000..31f1202751
--- /dev/null
+++ b/ui/gtk/main_welcome.c
@@ -0,0 +1,1469 @@
+/* main_welcome.c
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <time.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/prefs.h>
+
+#include "../color.h"
+#ifdef HAVE_LIBPCAP
+#include "capture.h"
+#include "capture-pcap-util.h"
+#include "capture_opts.h"
+#include "capture_ui_utils.h"
+#endif
+#include "simple_dialog.h"
+#include <wsutil/file_util.h>
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/color_utils.h"
+#include "ui/gtk/recent.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/menus.h"
+#include "ui/gtk/main_welcome.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/capture_file_dlg.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/utf8_entities.h"
+#ifdef HAVE_LIBPCAP
+#include "ui/gtk/capture_dlg.h"
+#include "ui/gtk/capture_if_dlg.h"
+#include "ui/gtk/capture_globals.h"
+#if GTK_CHECK_VERSION(2,18,0)
+#include "ui/gtk/webbrowser.h"
+#endif
+#endif /* HAVE_LIBPCAP */
+#include "../image/wssplash-dev.xpm"
+#include "../version_info.h"
+
+#ifdef _WIN32
+#include <tchar.h>
+#include <windows.h>
+#endif
+
+#ifdef HAVE_AIRPCAP
+#include "airpcap.h"
+#include "airpcap_loader.h"
+#include "airpcap_gui_utils.h"
+#endif
+#if defined(HAVE_AIRPCAP)
+#include "../image/toolbar/capture_airpcap_16.xpm"
+#endif
+#if defined(HAVE_PCAP_REMOTE)
+#include "ui/gtk/remote_icons.h"
+#endif
+
+/* XXX */
+extern gint if_list_comparator_alph (const void *first_arg, const void *second_arg);
+
+static GtkWidget *welcome_hb = NULL;
+static GtkWidget *header_lb = NULL;
+/* Foreground colors are set using Pango markup */
+#if GTK_CHECK_VERSION(3,0,0)
+static GdkRGBA rgba_welcome_bg = {0.901, 0.901, 0.901, 1.0 };
+static GdkRGBA rgba_header_bar_bg = { 0.094, 0.360, 0.792, 1.0 };
+static GdkRGBA rgba_topic_header_bg = { 0.004, 0.224, 0.745, 1.0 };
+static GdkRGBA rgba_topic_content_bg = { 1, 1, 1, 1.0 };
+static GdkRGBA rgba_topic_item_idle_bg;
+static GdkRGBA rgba_topic_item_entered_bg = { 0.827, 0.847, 0.854, 1.0 };
+#else
+static GdkColor welcome_bg = { 0, 0xe6e6, 0xe6e6, 0xe6e6 };
+static GdkColor header_bar_bg = { 0, 0x1818, 0x5c5c, 0xcaca };
+static GdkColor topic_header_bg = { 0, 0x0101, 0x3939, 0xbebe };
+static GdkColor topic_content_bg = { 0, 0xffff, 0xffff, 0xffff };
+static GdkColor topic_item_idle_bg;
+static GdkColor topic_item_entered_bg = { 0, 0xd3d3, 0xd8d8, 0xdada };
+#endif
+static GtkWidget *welcome_file_panel_vb = NULL;
+#ifdef HAVE_LIBPCAP
+static GtkWidget *welcome_if_panel_vb = NULL;
+static GtkWidget *if_view = NULL;
+static GtkWidget *swindow;
+static GArray *interfaces = NULL;
+#endif
+
+static GSList *status_messages = NULL;
+
+static GMutex *recent_mtx;
+
+/* The "scroll box dynamic" is a (complicated) pseudo widget to */
+/* place a vertically list of widgets in (currently the interfaces and recent files). */
+/* Once this list get's higher than a specified amount, */
+/* it is moved into a scrolled_window. */
+/* This is all complicated, the scrolled window is a bit ugly, */
+/* the sizes might not be the same on all systems, ... */
+/* ... but that's the best what we currently have */
+#define SCROLL_BOX_CHILD_BOX "ScrollBoxDynamic_ChildBox"
+#define SCROLL_BOX_MAX_CHILDS "ScrollBoxDynamic_MaxChilds"
+#define SCROLL_BOX_SCROLLW_Y_SIZE "ScrollBoxDynamic_Scrollw_Y_Size"
+#define SCROLL_BOX_SCROLLW "ScrollBoxDynamic_Scrollw"
+#define TREE_VIEW_INTERFACES "TreeViewInterfaces"
+
+static GtkWidget *
+scroll_box_dynamic_new(GtkWidget *child_box, guint max_childs, guint scrollw_y_size) {
+ GtkWidget * parent_box;
+
+
+ parent_box = gtk_vbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(parent_box), GTK_WIDGET(child_box), TRUE, TRUE, 0);
+ g_object_set_data(G_OBJECT(parent_box), SCROLL_BOX_CHILD_BOX, child_box);
+ g_object_set_data(G_OBJECT(parent_box), SCROLL_BOX_MAX_CHILDS, GINT_TO_POINTER(max_childs));
+ g_object_set_data(G_OBJECT(parent_box), SCROLL_BOX_SCROLLW_Y_SIZE, GINT_TO_POINTER(scrollw_y_size));
+ gtk_widget_show_all(parent_box);
+
+ return parent_box;
+}
+
+
+static GtkWidget *
+scroll_box_dynamic_add(GtkWidget *parent_box)
+{
+ GtkWidget *child_box;
+ GtkWidget *scrollw;
+ guint max_cnt;
+ guint curr_cnt;
+ guint scrollw_y_size;
+ GList *childs;
+
+ child_box = g_object_get_data(G_OBJECT(parent_box), SCROLL_BOX_CHILD_BOX);
+ max_cnt = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(parent_box), SCROLL_BOX_MAX_CHILDS));
+
+ /* get the current number of children */
+ childs = gtk_container_get_children(GTK_CONTAINER(child_box));
+ curr_cnt = g_list_length(childs);
+ g_list_free(childs);
+
+ /* have we just reached the max? */
+ if(curr_cnt == max_cnt) {
+ /* create the scrolled window */
+ /* XXX - there's no way to get rid of the shadow frame - except for creating an own widget :-( */
+ scrollw = scrolled_window_new(NULL, NULL);
+ scrollw_y_size = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(parent_box), SCROLL_BOX_SCROLLW_Y_SIZE));
+ gtk_widget_set_size_request(scrollw, -1, scrollw_y_size);
+
+ g_object_set_data(G_OBJECT(parent_box), SCROLL_BOX_SCROLLW, scrollw);
+ gtk_box_pack_start(GTK_BOX(parent_box), scrollw, TRUE, TRUE, 0);
+
+ /* move child_box from parent_box into scrolled window */
+ g_object_ref(child_box);
+ gtk_container_remove(GTK_CONTAINER(parent_box), child_box);
+ gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrollw),
+ child_box);
+ gtk_widget_show_all(scrollw);
+ }
+
+ return child_box;
+}
+
+
+static GtkWidget *
+scroll_box_dynamic_reset(GtkWidget *parent_box)
+{
+ GtkWidget *child_box, *scrollw;
+
+
+ child_box = g_object_get_data(G_OBJECT(parent_box), SCROLL_BOX_CHILD_BOX);
+ scrollw = g_object_get_data(G_OBJECT(parent_box), SCROLL_BOX_SCROLLW);
+
+ if(scrollw != NULL) {
+ /* move the child_box back from scrolled window into the parent_box */
+ g_object_ref(child_box);
+ gtk_container_remove(GTK_CONTAINER(parent_box), scrollw);
+ g_object_set_data(G_OBJECT(parent_box), SCROLL_BOX_SCROLLW, NULL);
+ gtk_box_pack_start(GTK_BOX(parent_box), child_box, TRUE, TRUE, 0);
+ }
+
+ return child_box;
+}
+
+
+/* mouse entered this widget - change background color */
+static gboolean
+welcome_item_enter_cb(GtkWidget *eb, GdkEventCrossing *event _U_, gpointer user_data _U_)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_background_color(eb, GTK_STATE_NORMAL, &rgba_topic_item_entered_bg);
+#else
+ gtk_widget_modify_bg(eb, GTK_STATE_NORMAL, &topic_item_entered_bg);
+#endif
+ return FALSE;
+}
+
+
+/* mouse has left this widget - change background color */
+static gboolean
+welcome_item_leave_cb(GtkWidget *eb, GdkEventCrossing *event _U_, gpointer user_data _U_)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_background_color(eb, GTK_STATE_NORMAL, &rgba_topic_item_idle_bg);
+#else
+ gtk_widget_modify_bg(eb, GTK_STATE_NORMAL, &topic_item_idle_bg);
+#endif
+ return FALSE;
+}
+
+
+typedef gboolean (*welcome_button_callback_t) (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data);
+
+/* create a "button widget" */
+static GtkWidget *
+welcome_button(const gchar *stock_item,
+ const gchar *title, const gchar *subtitle, const gchar *tooltip,
+ welcome_button_callback_t welcome_button_callback, gpointer welcome_button_callback_data)
+{
+ GtkWidget *eb, *w, *item_hb, *text_vb;
+ gchar *formatted_text;
+
+ item_hb = gtk_hbox_new(FALSE, 1);
+
+ /* event box (for background color and events) */
+ eb = gtk_event_box_new();
+ gtk_container_add(GTK_CONTAINER(eb), item_hb);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_background_color(eb, GTK_STATE_NORMAL, &rgba_topic_item_idle_bg);
+#else
+ gtk_widget_modify_bg(eb, GTK_STATE_NORMAL, &topic_item_idle_bg);
+#endif
+ if(tooltip != NULL) {
+ gtk_widget_set_tooltip_text(eb, tooltip);
+ }
+
+ g_signal_connect(eb, "enter-notify-event", G_CALLBACK(welcome_item_enter_cb), NULL);
+ g_signal_connect(eb, "leave-notify-event", G_CALLBACK(welcome_item_leave_cb), NULL);
+ g_signal_connect(eb, "button-release-event", G_CALLBACK(welcome_button_callback), welcome_button_callback_data);
+
+ /* icon */
+ w = gtk_image_new_from_stock(stock_item, GTK_ICON_SIZE_LARGE_TOOLBAR);
+ gtk_box_pack_start(GTK_BOX(item_hb), w, FALSE, FALSE, 5);
+
+ text_vb = gtk_vbox_new(FALSE, 3);
+
+ /* title */
+ w = gtk_label_new(title);
+ gtk_misc_set_alignment (GTK_MISC(w), 0.0f, 0.5f);
+ formatted_text = g_strdup_printf("<span weight=\"bold\" size=\"x-large\" foreground=\"black\">%s</span>", title);
+ gtk_label_set_markup(GTK_LABEL(w), formatted_text);
+ g_free(formatted_text);
+ gtk_box_pack_start(GTK_BOX(text_vb), w, FALSE, FALSE, 1);
+
+ /* subtitle */
+ w = gtk_label_new(subtitle);
+ gtk_misc_set_alignment (GTK_MISC(w), 0.0f, 0.5f);
+ formatted_text = g_strdup_printf("<span size=\"small\" foreground=\"black\">%s</span>", subtitle);
+ gtk_label_set_markup(GTK_LABEL(w), formatted_text);
+ g_free(formatted_text);
+ gtk_box_pack_start(GTK_BOX(text_vb), w, FALSE, FALSE, 1);
+
+ gtk_box_pack_start(GTK_BOX(item_hb), text_vb, TRUE, TRUE, 5);
+
+ return eb;
+}
+
+
+/* Hack to handle welcome-button "button-release-event" callback */
+/* 1. Dispatch to desired actual callback */
+/* 2. Return TRUE for the event callback. */
+/* user_data: actual (no arg) callback fcn to be invoked. */
+static gboolean
+welcome_button_callback_helper(GtkWidget *w, GdkEventButton *event _U_, gpointer user_data)
+{
+ void (*funct)(GtkWidget *, gpointer) = user_data;
+ (*funct)(w, NULL);
+ return TRUE;
+}
+
+
+void
+welcome_header_set_message(gchar *msg) {
+ GString *message;
+ time_t secs = time(NULL);
+ struct tm *now = localtime(&secs);
+
+ message = g_string_new("<span weight=\"bold\" size=\"x-large\" foreground=\"white\">");
+
+ if (msg) {
+ g_string_append(message, msg);
+ } else { /* Use our default header */
+ if ((now->tm_mon == 3 && now->tm_mday == 1) || (now->tm_mon == 6 && now->tm_mday == 14)) {
+ g_string_append(message, "Sniffing the glue that holds the Internet together");
+ } else {
+ g_string_append(message, prefs.gui_start_title);
+ }
+
+ if (prefs.gui_version_in_start_page) {
+ g_string_append_printf(message, "</span>\n<span size=\"large\" foreground=\"white\">Version " VERSION "%s",
+ wireshark_svnversion);
+ }
+ }
+
+ g_string_append(message, "</span>");
+
+ gtk_label_set_markup(GTK_LABEL(header_lb), message->str);
+ g_string_free(message, TRUE);
+}
+
+
+/* create the banner "above our heads" */
+static GtkWidget *
+welcome_header_new(void)
+{
+ GtkWidget *item_vb;
+ GtkWidget *item_hb;
+ GtkWidget *eb;
+ GtkWidget *icon;
+
+ item_vb = gtk_vbox_new(FALSE, 0);
+
+ /* colorize vbox */
+ eb = gtk_event_box_new();
+ gtk_container_add(GTK_CONTAINER(eb), item_vb);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_background_color(eb, GTK_STATE_NORMAL, &rgba_header_bar_bg);
+#else
+ gtk_widget_modify_bg(eb, GTK_STATE_NORMAL, &header_bar_bg);
+#endif
+ item_hb = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(item_vb), item_hb, FALSE, FALSE, 10);
+
+ /*icon = xpm_to_widget_from_parent(top_level, wssplash_xpm);*/
+ icon = xpm_to_widget(wssplash_xpm);
+ gtk_box_pack_start(GTK_BOX(item_hb), icon, FALSE, FALSE, 10);
+
+ header_lb = gtk_label_new(NULL);
+ welcome_header_set_message(NULL);
+ gtk_label_set_selectable(GTK_LABEL(header_lb), TRUE);
+ gtk_misc_set_alignment(GTK_MISC(header_lb), 0.0f, 0.5f);
+ gtk_box_pack_start(GTK_BOX(item_hb), header_lb, TRUE, TRUE, 5);
+
+ gtk_widget_show_all(eb);
+
+ return eb;
+}
+
+
+void
+welcome_header_push_msg(const gchar *msg) {
+ gchar *msg_copy = g_strdup(msg);
+
+ status_messages = g_slist_append(status_messages, msg_copy);
+
+ welcome_header_set_message(msg_copy);
+
+ gtk_widget_hide(welcome_hb);
+}
+
+
+void
+welcome_header_pop_msg(void) {
+ gchar *msg = NULL;
+
+ if (status_messages) {
+ g_free(status_messages->data);
+ status_messages = g_slist_delete_link(status_messages, status_messages);
+ }
+
+ if (status_messages) {
+ msg = status_messages->data;
+ }
+
+ welcome_header_set_message(msg);
+
+ if (!status_messages) {
+ gtk_widget_show(welcome_hb);
+ }
+}
+
+
+/* create a "topic header widget" */
+static GtkWidget *
+welcome_topic_header_new(const char *header)
+{
+ GtkWidget *w;
+ GtkWidget *eb;
+ gchar *formatted_message;
+
+
+ w = gtk_label_new(header);
+ formatted_message = g_strdup_printf("<span weight=\"bold\" size=\"x-large\" foreground=\"white\">%s</span>", header);
+ gtk_label_set_markup(GTK_LABEL(w), formatted_message);
+ g_free(formatted_message);
+
+ /* colorize vbox */
+ eb = gtk_event_box_new();
+ gtk_container_add(GTK_CONTAINER(eb), w);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_background_color(eb, GTK_STATE_NORMAL, &rgba_topic_header_bg);
+#else
+ gtk_widget_modify_bg(eb, GTK_STATE_NORMAL, &topic_header_bg);
+#endif
+ return eb;
+}
+
+
+/* create a "topic widget" */
+static GtkWidget *
+welcome_topic_new(const char *header, GtkWidget **to_fill)
+{
+ GtkWidget *topic_vb;
+ GtkWidget *layout_vb;
+ GtkWidget *topic_eb;
+ GtkWidget *topic_header;
+
+
+ topic_vb = gtk_vbox_new(FALSE, 0);
+
+ topic_header = welcome_topic_header_new(header);
+ gtk_box_pack_start(GTK_BOX(topic_vb), topic_header, FALSE, FALSE, 0);
+
+ layout_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(layout_vb), 10);
+ gtk_box_pack_start(GTK_BOX(topic_vb), layout_vb, FALSE, FALSE, 0);
+
+ /* colorize vbox (we need an event box for this!) */
+ topic_eb = gtk_event_box_new();
+ gtk_container_add(GTK_CONTAINER(topic_eb), topic_vb);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_background_color(topic_eb, GTK_STATE_NORMAL, &rgba_topic_content_bg);
+#else
+ gtk_widget_modify_bg(topic_eb, GTK_STATE_NORMAL, &topic_content_bg);
+#endif
+ *to_fill = layout_vb;
+
+ return topic_eb;
+}
+
+
+/* a file link was pressed */
+static gboolean
+welcome_filename_link_press_cb(GtkWidget *widget _U_, GdkEventButton *event _U_, gpointer data)
+{
+ menu_open_filename(data);
+
+ return FALSE;
+}
+
+typedef struct _recent_item_status {
+ gchar *filename;
+ GtkWidget *label;
+ GObject *menu_item;
+ GString *str;
+ gboolean stat_done;
+ int err;
+ guint timer;
+} recent_item_status;
+
+/*
+ * Fetch the status of a file.
+ * This function might be called as a thread. We can't use any drawing
+ * routines here: http://developer.gnome.org/gdk/2.24/gdk-Threads.html
+ */
+static void *get_recent_item_status(void *data)
+{
+ recent_item_status *ri_stat = (recent_item_status *) data;
+ ws_statb64 stat_buf;
+ int err;
+
+ if (!ri_stat) {
+ return NULL;
+ }
+
+ /*
+ * Add file size. We use binary prefixes instead of IEC because that's what
+ * most OSes use.
+ */
+ err = ws_stat64(ri_stat->filename, &stat_buf);
+ g_mutex_lock(recent_mtx);
+ ri_stat->err = err;
+ if(err == 0) {
+ if (stat_buf.st_size/1024/1024/1024 > 10) {
+ g_string_append_printf(ri_stat->str, " (%" G_GINT64_MODIFIER "d GB)", (gint64) (stat_buf.st_size/1024/1024/1024));
+ } else if (stat_buf.st_size/1024/1024 > 10) {
+ g_string_append_printf(ri_stat->str, " (%" G_GINT64_MODIFIER "d MB)", (gint64) (stat_buf.st_size/1024/1024));
+ } else if (stat_buf.st_size/1024 > 10) {
+ g_string_append_printf(ri_stat->str, " (%" G_GINT64_MODIFIER "d KB)", (gint64) (stat_buf.st_size/1024));
+ } else {
+ g_string_append_printf(ri_stat->str, " (%" G_GINT64_MODIFIER "d Bytes)", (gint64) (stat_buf.st_size));
+ }
+ /* pango format string */
+ g_string_prepend(ri_stat->str, "<span foreground='blue'>");
+ g_string_append(ri_stat->str, "</span>");
+ } else {
+ g_string_append(ri_stat->str, " [not found]");
+ }
+
+ if (!ri_stat->label) { /* The widget went away while we were busy. */
+ g_free(ri_stat->filename);
+ g_string_free(ri_stat->str, TRUE);
+ g_free(ri_stat);
+ } else {
+ ri_stat->stat_done = TRUE;
+ }
+ g_mutex_unlock(recent_mtx);
+
+ return NULL;
+}
+
+/* Timeout callback for recent items */
+static gboolean
+update_recent_items(gpointer data)
+{
+ recent_item_status *ri_stat = (recent_item_status *) data;
+ gboolean again = TRUE;
+
+ if (!ri_stat) {
+ return FALSE;
+ }
+
+ g_mutex_lock(recent_mtx);
+ if (ri_stat->stat_done) {
+ again = FALSE;
+ gtk_label_set_markup(GTK_LABEL(ri_stat->label), ri_stat->str->str);
+ if (ri_stat->err == 0) {
+ gtk_widget_set_sensitive(ri_stat->label, TRUE);
+ gtk_action_set_sensitive((GtkAction *) ri_stat->menu_item, TRUE);
+ }
+ ri_stat->timer = 0;
+ }
+ /* Else append some sort of Unicode or ASCII animation to the label? */
+ g_mutex_unlock(recent_mtx);
+ return again;
+}
+
+static void welcome_filename_destroy_cb(GtkWidget *w _U_, gpointer data) {
+ recent_item_status *ri_stat = (recent_item_status *) data;
+
+ if (!ri_stat) {
+ return;
+ }
+
+ g_mutex_lock(recent_mtx);
+ if (ri_stat->timer) {
+ g_source_remove(ri_stat->timer);
+ ri_stat->timer = 0;
+ }
+
+ g_object_unref(ri_stat->menu_item);
+
+ if (ri_stat->stat_done) {
+ g_free(ri_stat->filename);
+ g_string_free(ri_stat->str, TRUE);
+ g_free(ri_stat);
+ } else {
+ ri_stat->label = NULL;
+ }
+ g_mutex_unlock(recent_mtx);
+}
+
+/* create a "file link widget" */
+static GtkWidget *
+welcome_filename_link_new(const gchar *filename, GtkWidget **label, GObject *menu_item)
+{
+ GtkWidget *w;
+ GtkWidget *eb;
+ GString *str;
+ gchar *str_escaped;
+ glong uni_len;
+ gsize uni_start, uni_end;
+ const glong max = 60;
+ recent_item_status *ri_stat;
+
+ /* filename */
+ str = g_string_new(filename);
+ uni_len = g_utf8_strlen(str->str, str->len);
+
+ /* cut max filename length */
+ if (uni_len > max) {
+ uni_start = g_utf8_offset_to_pointer(str->str, 20) - str->str;
+ uni_end = g_utf8_offset_to_pointer(str->str, uni_len - max) - str->str;
+ g_string_erase(str, uni_start, uni_end);
+ g_string_insert(str, uni_start, " " UTF8_HORIZONTAL_ELLIPSIS " ");
+ }
+
+ /* escape the possibly shortened filename before adding pango language */
+ str_escaped=g_markup_escape_text(str->str, -1);
+ g_string_free(str, TRUE);
+
+ /* label */
+ w = gtk_label_new(str_escaped);
+ *label = w;
+ gtk_misc_set_padding(GTK_MISC(w), 5, 2);
+ gtk_misc_set_alignment (GTK_MISC(w), 0.0f, 0.0f);
+ gtk_widget_set_sensitive(w, FALSE);
+
+ ri_stat = g_malloc(sizeof(recent_item_status));
+ ri_stat->filename = g_strdup(filename);
+ ri_stat->label = w;
+ ri_stat->menu_item = menu_item;
+ ri_stat->str = g_string_new(str_escaped);
+ ri_stat->stat_done = FALSE;
+ ri_stat->timer = 0;
+ g_object_ref(G_OBJECT(menu_item));
+ g_signal_connect(w, "destroy", G_CALLBACK(welcome_filename_destroy_cb), ri_stat);
+ g_free(str_escaped);
+
+#if GLIB_CHECK_VERSION(2,31,0)
+ /* XXX - Add the filename here? */
+ g_thread_new("Recent item status", get_recent_item_status, ri_stat);
+#else
+ g_thread_create(get_recent_item_status, ri_stat, FALSE, NULL);
+#endif
+ ri_stat->timer = g_timeout_add(200, update_recent_items, ri_stat);
+
+ /* event box */
+ eb = gtk_event_box_new();
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_background_color(eb, GTK_STATE_NORMAL, &rgba_topic_item_idle_bg);
+#else
+ gtk_widget_modify_bg(eb, GTK_STATE_NORMAL, &topic_item_idle_bg);
+#endif
+ gtk_container_add(GTK_CONTAINER(eb), w);
+ gtk_widget_set_tooltip_text(eb, filename);
+
+ g_signal_connect(eb, "enter-notify-event", G_CALLBACK(welcome_item_enter_cb), w);
+ g_signal_connect(eb, "leave-notify-event", G_CALLBACK(welcome_item_leave_cb), w);
+ g_signal_connect(eb, "button-press-event", G_CALLBACK(welcome_filename_link_press_cb), (gchar *) filename);
+
+ return eb;
+}
+
+
+/* reset the list of recent files */
+void
+main_welcome_reset_recent_capture_files(void)
+{
+ GtkWidget *child_box;
+ GList* child_list;
+ GList* child_list_item;
+
+
+ if(welcome_file_panel_vb) {
+ child_box = scroll_box_dynamic_reset(welcome_file_panel_vb);
+ child_list = gtk_container_get_children(GTK_CONTAINER(child_box));
+ child_list_item = child_list;
+
+ while(child_list_item) {
+ gtk_container_remove(GTK_CONTAINER(child_box), child_list_item->data);
+ child_list_item = g_list_next(child_list_item);
+ }
+
+ g_list_free(child_list);
+ }
+}
+
+
+/* add a new file to the list of recent files */
+void
+main_welcome_add_recent_capture_file(const char *widget_cf_name, GObject *menu_item)
+{
+ GtkWidget *w;
+ GtkWidget *child_box;
+ GtkWidget *label;
+
+
+ w = welcome_filename_link_new(widget_cf_name, &label, menu_item);
+ child_box = scroll_box_dynamic_add(welcome_file_panel_vb);
+ gtk_box_pack_start(GTK_BOX(child_box), w, FALSE, FALSE, 0);
+ gtk_widget_show_all(w);
+ gtk_widget_show_all(child_box);
+}
+
+#ifdef HAVE_LIBPCAP
+static gboolean select_current_ifaces(GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer userdata)
+{
+ guint i;
+ gchar *if_name;
+ gboolean found = FALSE;
+
+ GtkTreeSelection *selection = (GtkTreeSelection *)userdata;
+ gtk_tree_model_get (model, iter, IFACE_NAME, &if_name, -1);
+ for (i = 0; i < global_capture_opts.ifaces->len; i++) {
+ if (strcmp(g_array_index(global_capture_opts.ifaces, interface_options, i).name, if_name) == 0) {
+ if (!gtk_tree_selection_path_is_selected(selection, path)) {
+ gtk_tree_selection_select_iter(selection, iter);
+ }
+ found = TRUE;
+ break;
+ }
+ }
+ if (!found) {
+ gtk_tree_selection_unselect_iter(selection, iter);
+ }
+ return FALSE;
+}
+
+gboolean on_selection_changed(GtkTreeSelection *selection _U_,
+ GtkTreeModel *model,
+ GtkTreePath *path,
+ gboolean path_currently_selected,
+ gpointer data _U_)
+{
+ GtkTreeIter iter;
+ gchar *if_name;
+ interface_options interface_opts;
+ guint i, j;
+ cap_settings_t cap_settings;
+ gboolean found = FALSE;
+ displayed_interface d_interface;
+
+ d_interface.name = NULL;
+ d_interface.descr = NULL;
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_tree_model_get (model, &iter, IFACE_NAME, &if_name, -1);
+ if (global_capture_opts.ifaces->len > 0) {
+ for (i = 0; i < global_capture_opts.ifaces->len; i++) {
+ if (strcmp(g_array_index(global_capture_opts.ifaces, interface_options, i).name, if_name) == 0) {
+ found = TRUE;
+ if (path_currently_selected) {
+ interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, i);
+ global_capture_opts.ifaces = g_array_remove_index(global_capture_opts.ifaces, i);
+ if (gtk_widget_is_focus(g_object_get_data(G_OBJECT(welcome_hb), TREE_VIEW_INTERFACES)) && interfaces_dialog_window_present()) {
+ update_selected_interface(g_strdup(interface_opts.name), FALSE);
+ }
+ if (gtk_widget_is_focus(g_object_get_data(G_OBJECT(welcome_hb), TREE_VIEW_INTERFACES)) && dlg_window_present()) {
+ enable_selected_interface(interface_opts.name, FALSE);
+ }
+ g_free(interface_opts.name);
+ g_free(interface_opts.descr);
+ g_free(interface_opts.cfilter);
+#ifdef HAVE_PCAP_REMOTE
+ g_free(interface_opts.remote_host);
+ g_free(interface_opts.remote_port);
+ g_free(interface_opts.auth_username);
+ g_free(interface_opts.auth_password);
+#endif
+ break;
+ }
+ }
+ }
+ }
+ if (!found && !path_currently_selected) {
+ for (j = 0; j < interfaces->len; j++) {
+ d_interface = g_array_index(interfaces, displayed_interface, j);
+ if (strcmp(d_interface.name, if_name) == 0) {
+ interface_opts.name = g_strdup(d_interface.name);
+ interface_opts.descr = g_strdup(d_interface.descr);
+ interface_opts.linktype = capture_dev_user_linktype_find(interface_opts.name);
+ interface_opts.cfilter = g_strdup(global_capture_opts.default_options.cfilter);
+ interface_opts.has_snaplen = global_capture_opts.default_options.has_snaplen;
+ interface_opts.snaplen = global_capture_opts.default_options.snaplen;
+ cap_settings = capture_get_cap_settings (interface_opts.name);;
+ interface_opts.promisc_mode = global_capture_opts.default_options.promisc_mode;
+#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
+ interface_opts.buffer_size = global_capture_opts.default_options.buffer_size;
+#endif
+ interface_opts.monitor_mode = cap_settings.monitor_mode;
+#ifdef HAVE_PCAP_REMOTE
+ if (d_interface.remote_opts.src_type == CAPTURE_IFREMOTE) {
+ interface_opts.src_type = d_interface.remote_opts.src_type;
+ interface_opts.remote_host = g_strdup(d_interface.remote_opts.remote_host_opts.remote_host);
+ interface_opts.remote_port = g_strdup(d_interface.remote_opts.remote_host_opts.remote_port);
+ interface_opts.auth_type = d_interface.remote_opts.remote_host_opts.auth_type;
+ interface_opts.auth_username = g_strdup(d_interface.remote_opts.remote_host_opts.auth_username);
+ interface_opts.auth_password = g_strdup(d_interface.remote_opts.remote_host_opts.auth_password);
+ interface_opts.datatx_udp = d_interface.remote_opts.remote_host_opts.datatx_udp;
+ interface_opts.nocap_rpcap = d_interface.remote_opts.remote_host_opts.nocap_rpcap;
+ interface_opts.nocap_local = d_interface.remote_opts.remote_host_opts.nocap_local;
+#ifdef HAVE_PCAP_SETSAMPLING
+ interface_opts.sampling_method = d_interface.remote_opts.sampling_method;
+ interface_opts.sampling_param = d_interface.remote_opts.sampling_param;
+#endif
+ } else {
+ interface_opts.src_type = global_capture_opts.default_options.src_type;
+ interface_opts.remote_host = g_strdup(global_capture_opts.default_options.remote_host);
+ interface_opts.remote_port = g_strdup(global_capture_opts.default_options.remote_port);
+ interface_opts.auth_type = global_capture_opts.default_options.auth_type;
+ interface_opts.auth_username = g_strdup(global_capture_opts.default_options.auth_username);
+ interface_opts.auth_password = g_strdup(global_capture_opts.default_options.auth_password);
+ interface_opts.datatx_udp = global_capture_opts.default_options.datatx_udp;
+ interface_opts.nocap_rpcap = global_capture_opts.default_options.nocap_rpcap;
+ interface_opts.nocap_local = global_capture_opts.default_options.nocap_local;
+#ifdef HAVE_PCAP_SETSAMPLING
+ interface_opts.sampling_method = global_capture_opts.default_options.sampling_method;
+ interface_opts.sampling_param = global_capture_opts.default_options.sampling_param;
+#endif
+ }
+#endif
+
+ g_array_append_val(global_capture_opts.ifaces, interface_opts);
+ if (gtk_widget_is_focus(g_object_get_data(G_OBJECT(welcome_hb), TREE_VIEW_INTERFACES)) && interfaces_dialog_window_present()) {
+ update_selected_interface(g_strdup(interface_opts.name), TRUE);
+ }
+ if (gtk_widget_is_focus(g_object_get_data(G_OBJECT(welcome_hb), TREE_VIEW_INTERFACES)) && dlg_window_present()) {
+ enable_selected_interface(interface_opts.name, TRUE);
+ }
+ break;
+ }
+ }
+ }
+ return TRUE;
+}
+
+static gboolean activate_ifaces(GtkTreeModel *model,
+ GtkTreePath *path _U_,
+ GtkTreeIter *iter,
+ gpointer userdata)
+{
+ gchar *if_name;
+ GtkWidget *view;
+ GtkTreeSelection *selection;
+ selected_name_t *entry = (selected_name_t *)userdata;
+
+ view = g_object_get_data(G_OBJECT(welcome_hb), TREE_VIEW_INTERFACES);
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
+ gtk_tree_model_get (model, iter, IFACE_NAME, &if_name, -1);
+ if (strcmp(if_name, entry->name) == 0) {
+ if (entry->activate) {
+ gtk_tree_selection_select_iter(selection, iter);
+ } else {
+ gtk_tree_selection_unselect_iter(selection, iter);
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void change_interface_selection(gchar* name, gboolean activate)
+{
+ GtkWidget *view;
+ GtkTreeModel *model;
+ selected_name_t entry;
+
+ view = g_object_get_data(G_OBJECT(welcome_hb), TREE_VIEW_INTERFACES);
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
+ entry.name = g_strdup(name);
+ entry.activate = activate;
+ gtk_tree_model_foreach(GTK_TREE_MODEL(model), (GtkTreeModelForeachFunc)(activate_ifaces), (gpointer) &entry);
+}
+
+void change_selection_for_all(gboolean enable)
+{
+ guint i;
+
+ if (interfaces) {
+ for (i = 0; i < interfaces->len; i++) {
+ change_interface_selection(g_array_index(interfaces, displayed_interface, i).name, enable);
+ }
+ }
+}
+#endif
+
+void
+select_ifaces(void)
+{
+#ifdef HAVE_LIBPCAP
+ GtkWidget *view;
+ GtkTreeModel *model;
+ GtkTreeSelection *entry;
+
+ if (global_capture_opts.ifaces->len > 0 && swindow) {
+ view = g_object_get_data(G_OBJECT(welcome_hb), TREE_VIEW_INTERFACES);
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
+ entry = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
+ gtk_tree_model_foreach(GTK_TREE_MODEL(model), select_current_ifaces, (gpointer) entry);
+ gtk_widget_grab_focus(view);
+ }
+#endif
+}
+
+#ifdef HAVE_PCAP_REMOTE
+void
+add_interface_to_list(gchar *name, gchar *descr, remote_options *remote_opts)
+{
+ GtkWidget *view, *icon;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gint size;
+ gchar *lines;
+ displayed_interface d_interface;
+
+ d_interface.name = g_strdup(name);
+ d_interface.descr = g_strdup(descr);
+ d_interface.remote_opts.src_type = remote_opts->src_type;
+ d_interface.remote_opts.remote_host_opts.remote_host = g_strdup(remote_opts->remote_host_opts.remote_host);
+ d_interface.remote_opts.remote_host_opts.remote_port = g_strdup(remote_opts->remote_host_opts.remote_port);
+ d_interface.remote_opts.remote_host_opts.auth_type = remote_opts->remote_host_opts.auth_type;
+ d_interface.remote_opts.remote_host_opts.auth_username = g_strdup(remote_opts->remote_host_opts.auth_username);
+ d_interface.remote_opts.remote_host_opts.auth_password = g_strdup(remote_opts->remote_host_opts.auth_password);
+ d_interface.remote_opts.remote_host_opts.datatx_udp = remote_opts->remote_host_opts.datatx_udp;
+ d_interface.remote_opts.remote_host_opts.nocap_rpcap = remote_opts->remote_host_opts.nocap_rpcap;
+ d_interface.remote_opts.remote_host_opts.nocap_local = remote_opts->remote_host_opts.nocap_local;
+#ifdef HAVE_PCAP_SETSAMPLING
+ d_interface.remote_opts.sampling_method = remote_opts->sampling_method;
+ d_interface.remote_opts.sampling_param = remote_opts->sampling_param;
+#endif
+ icon = pixbuf_to_widget(remote_sat_pb_data);
+ d_interface.icon = icon;
+ view = g_object_get_data(G_OBJECT(welcome_hb), TREE_VIEW_INTERFACES);
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
+ size = gtk_tree_model_iter_n_children(model, NULL);
+ lines = g_strdup_printf("%d", size-1);
+ g_array_append_val(interfaces, d_interface);
+ if (gtk_tree_model_get_iter_from_string(model, &iter, lines)) {
+ gtk_list_store_append (GTK_LIST_STORE(model), &iter);
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, ICON, gtk_image_get_pixbuf(GTK_IMAGE(icon)), IFACE_DESCR, descr, IFACE_NAME, name, -1);
+ }
+}
+#endif
+
+/* list the interfaces */
+void
+welcome_if_tree_load(void)
+{
+#ifdef HAVE_LIBPCAP
+ if_info_t *if_info;
+ GList *if_list;
+ int err;
+ guint i;
+ gchar *err_str = NULL;
+ GList *curr;
+ gchar *user_descr;
+ GtkListStore *store = NULL;
+ GtkTreeIter iter;
+ GtkWidget *icon, *view;
+ GtkTreeSelection *entry;
+ displayed_interface d_interface;
+
+ view = g_object_get_data(G_OBJECT(welcome_hb), TREE_VIEW_INTERFACES);
+ entry = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
+ gtk_tree_selection_unselect_all(entry);
+ store = gtk_list_store_new(NUMCOLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
+
+ /* LOAD THE INTERFACES */
+ if (interfaces && interfaces->len > 0) {
+ for (i = 0; i < interfaces->len; i++) {
+ d_interface = g_array_index(interfaces, displayed_interface, i);
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter, ICON, d_interface.icon, IFACE_DESCR, d_interface.descr, IFACE_NAME, d_interface.name, -1);
+ }
+ } else {
+ interfaces = g_array_new(TRUE, TRUE, sizeof(displayed_interface));
+ if_list = capture_interface_list(&err, &err_str);
+ if_list = g_list_sort (if_list, if_list_comparator_alph);
+ if (if_list == NULL &&
+ (err == CANT_GET_INTERFACE_LIST || err == DONT_HAVE_PCAP)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_str);
+ g_free(err_str);
+ return;
+ } else if (err_str) {
+ g_free(err_str);
+ }
+ if (g_list_length(if_list) > 0) {
+ /* List the interfaces */
+ for (curr = g_list_first(if_list); curr; curr = g_list_next(curr)) {
+ if_info = curr->data;
+ /* Continue if capture device is hidden */
+ if (prefs_is_capture_device_hidden(if_info->name)) {
+ continue;
+ }
+ gtk_list_store_append (store, &iter);
+ d_interface.name = g_strdup(if_info->name);
+#ifdef HAVE_PCAP_REMOTE
+ d_interface.remote_opts.src_type = CAPTURE_IFLOCAL;
+#endif
+#ifdef HAVE_AIRPCAP
+ if (get_airpcap_if_from_name(airpcap_if_list,if_info->name) != NULL)
+ icon = xpm_to_widget(capture_airpcap_16_xpm);
+ else
+ icon = capture_get_if_icon(if_info);
+#else
+ icon = capture_get_if_icon(if_info);
+#endif
+ d_interface.icon = icon;
+ user_descr = capture_dev_user_descr_find(if_info->name);
+ if (user_descr) {
+#ifndef _WIN32
+ gchar *comment = user_descr;
+ user_descr = g_strdup_printf("%s (%s)", comment, if_info->name);
+ g_free (comment);
+#endif
+ gtk_list_store_set(store, &iter, ICON, gtk_image_get_pixbuf(GTK_IMAGE(icon)), IFACE_DESCR, user_descr, IFACE_NAME, if_info->name, -1);
+ d_interface.descr = g_strdup(user_descr);
+ g_free (user_descr);
+ } else if (if_info->description) {
+ gtk_list_store_set (store, &iter, ICON, gtk_image_get_pixbuf(GTK_IMAGE(icon)), IFACE_DESCR, if_info->description, IFACE_NAME, if_info->name, -1);
+ d_interface.descr = g_strdup(if_info->description);
+ } else {
+ gtk_list_store_set (store, &iter, ICON, gtk_image_get_pixbuf(GTK_IMAGE(icon)), IFACE_DESCR, if_info->name, IFACE_NAME, if_info->name, -1);
+ d_interface.descr = g_strdup(if_info->name);
+ }
+ g_array_append_val(interfaces, d_interface);
+ }
+ }
+ free_interface_list(if_list);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(if_view), GTK_TREE_MODEL (store));
+ if (global_capture_opts.ifaces->len > 0) {
+ gtk_tree_model_foreach(GTK_TREE_MODEL(store), select_current_ifaces, (gpointer) entry);
+ gtk_widget_grab_focus(view);
+ }
+ gtk_tree_selection_set_select_function(entry, on_selection_changed, NULL, NULL);
+ }
+#endif /* HAVE_LIBPCAP */
+}
+
+
+/* reload the list of interfaces */
+void
+welcome_if_panel_reload(void)
+{
+#ifdef HAVE_LIBPCAP
+ GtkWidget *child_box;
+ GList* child_list;
+ GList* child_list_item;
+
+ if(welcome_if_panel_vb) {
+ child_box = scroll_box_dynamic_reset(welcome_if_panel_vb);
+ child_list = gtk_container_get_children(GTK_CONTAINER(child_box));
+ child_list_item = child_list;
+
+ while(child_list_item) {
+ gtk_container_remove(GTK_CONTAINER(child_box), child_list_item->data);
+ child_list_item = g_list_next(child_list_item);
+ }
+
+ g_list_free(child_list);
+ welcome_if_tree_load();
+ gtk_widget_show_all(welcome_if_panel_vb);
+ }
+#endif /* HAVE_LIBPCAP */
+}
+
+#ifdef HAVE_LIBPCAP
+static void capture_if_start(GtkWidget *w _U_, gpointer data _U_)
+{
+#ifdef HAVE_AIRPCAP
+ interface_options interface_opts;
+#endif
+
+ if (global_capture_opts.ifaces->len == 0) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "You didn't specify an interface on which to capture packets.");
+ return;
+ }
+
+ /* XXX - remove this? */
+ if (global_capture_opts.save_file) {
+ g_free(global_capture_opts.save_file);
+ global_capture_opts.save_file = NULL;
+ }
+#ifdef HAVE_AIRPCAP
+ interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, 0);
+ airpcap_if_active = get_airpcap_if_from_name(airpcap_if_list, interface_opts.name);
+ airpcap_if_selected = airpcap_if_active;
+ airpcap_set_toolbar_start_capture(airpcap_if_active);
+#endif
+ capture_start_cb(NULL, NULL);
+}
+#endif
+
+#ifdef HAVE_LIBPCAP
+#if GTK_CHECK_VERSION(2,18,0)
+static gboolean
+activate_link_cb(GtkLabel *label _U_, gchar *uri, gpointer user_data _U_)
+{
+ return browser_open_url(uri);
+}
+#endif
+#endif
+
+/* create the welcome page */
+GtkWidget *
+welcome_new(void)
+{
+ GtkWidget *welcome_scrollw;
+ GtkWidget *welcome_eb;
+ GtkWidget *welcome_vb;
+ GtkWidget *column_vb;
+ GtkWidget *item_hb;
+ GtkWidget *w;
+ GtkWidget *header;
+ GtkWidget *topic_vb;
+ GtkWidget *topic_to_fill;
+ GtkWidget *file_child_box;
+ gchar *label_text;
+#ifdef _WIN32
+ LONG reg_ret;
+ DWORD chimney_enabled = 0;
+ DWORD ce_size = sizeof(chimney_enabled);
+#endif
+#ifdef HAVE_LIBPCAP
+ GtkTreeSelection *selection;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GList *if_list;
+ int err;
+ gchar *err_str = NULL;
+#endif
+
+ /* prepare colors */
+#if 0
+ /* Allocating collor isn't necessary? */
+
+ /* "page" background */
+ get_color(&welcome_bg);
+
+ /* header bar background color */
+ get_color(&header_bar_bg);
+
+ /* topic header background color */
+ get_color(&topic_header_bg);
+
+ /* topic content background color */
+ get_color(&topic_content_bg);
+#endif
+#if GTK_CHECK_VERSION(3,0,0)
+ rgba_topic_item_idle_bg = rgba_topic_content_bg;
+#else
+ topic_item_idle_bg = topic_content_bg;
+#endif
+#if 0
+ /* Allocating collor isn't necessary? */
+ /* topic item entered color */
+ get_color(&topic_item_entered_bg);
+#endif
+ welcome_scrollw = scrolled_window_new(NULL, NULL);
+
+ welcome_vb = gtk_vbox_new(FALSE, 0);
+
+ welcome_eb = gtk_event_box_new();
+ gtk_container_add(GTK_CONTAINER(welcome_eb), welcome_vb);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_background_color(welcome_eb, GTK_STATE_NORMAL, &rgba_welcome_bg);
+#else
+ gtk_widget_modify_bg(welcome_eb, GTK_STATE_NORMAL, &welcome_bg);
+#endif
+ /* header */
+ header = welcome_header_new();
+ gtk_box_pack_start(GTK_BOX(welcome_vb), header, FALSE, FALSE, 0);
+
+ /* content */
+ welcome_hb = gtk_hbox_new(FALSE, 10);
+ gtk_container_set_border_width(GTK_CONTAINER(welcome_hb), 10);
+ gtk_box_pack_start(GTK_BOX(welcome_vb), welcome_hb, TRUE, TRUE, 0);
+
+
+ /* column capture */
+ column_vb = gtk_vbox_new(FALSE, 10);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_background_color(column_vb, GTK_STATE_NORMAL, &rgba_welcome_bg);
+#else
+ gtk_widget_modify_bg(column_vb, GTK_STATE_NORMAL, &welcome_bg);
+#endif
+ gtk_box_pack_start(GTK_BOX(welcome_hb), column_vb, TRUE, TRUE, 0);
+
+ /* capture topic */
+ topic_vb = welcome_topic_new("Capture", &topic_to_fill);
+ gtk_box_pack_start(GTK_BOX(column_vb), topic_vb, TRUE, TRUE, 0);
+
+#ifdef HAVE_LIBPCAP
+ if_list = capture_interface_list(&err, &err_str);
+ if (g_list_length(if_list) > 0) {
+ item_hb = welcome_button(WIRESHARK_STOCK_CAPTURE_INTERFACES,
+ "Interface List",
+ "Live list of the capture interfaces\n(counts incoming packets)",
+ "Same as Capture/Interfaces menu or toolbar item",
+ welcome_button_callback_helper, capture_if_cb);
+ gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
+
+ swindow = gtk_scrolled_window_new (NULL, NULL);
+ gtk_widget_set_size_request(swindow, FALSE, 100);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swindow), GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+
+ if_view = gtk_tree_view_new ();
+ g_object_set(G_OBJECT(if_view), "headers-visible", FALSE, NULL);
+ g_signal_connect(if_view, "row-activated", G_CALLBACK(options_interface_cb), (gpointer)welcome_hb);
+ g_object_set_data(G_OBJECT(welcome_hb), TREE_VIEW_INTERFACES, if_view);
+ renderer = gtk_cell_renderer_pixbuf_new();
+ column = gtk_tree_view_column_new_with_attributes ("",
+ GTK_CELL_RENDERER(renderer),
+ "pixbuf", ICON,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(if_view), column);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes ("",
+ GTK_CELL_RENDERER(renderer),
+ "text", IFACE_DESCR,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(if_view), column);
+ gtk_tree_view_column_set_resizable(gtk_tree_view_get_column(GTK_TREE_VIEW(if_view), 0), TRUE);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes ("",
+ GTK_CELL_RENDERER(renderer),
+ "text", IFACE_NAME,
+ NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(if_view), column);
+ gtk_tree_view_column_set_visible(column, FALSE);
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(if_view));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
+ item_hb = welcome_button(WIRESHARK_STOCK_CAPTURE_START,
+ "Start",
+ "Choose one or more interfaces to capture from, then <b>Start</b>",
+ "Same as Capture/Interfaces with default options",
+ (welcome_button_callback_t)capture_if_start, (gpointer)if_view);
+ gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
+ welcome_if_tree_load();
+ gtk_container_add (GTK_CONTAINER (swindow), if_view);
+ gtk_container_add(GTK_CONTAINER(topic_to_fill), swindow);
+
+ item_hb = welcome_button(WIRESHARK_STOCK_CAPTURE_OPTIONS,
+ "Capture Options",
+ "Start a capture with detailed options",
+ "Same as Capture/Options menu or toolbar item",
+ welcome_button_callback_helper, capture_prep_cb);
+ gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
+#ifdef _WIN32
+ /* Check for chimney offloading */
+ reg_ret = RegQueryValueEx(HKEY_LOCAL_MACHINE,
+ _T("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\EnableTCPChimney"),
+ NULL, NULL, (LPBYTE) &chimney_enabled, &ce_size);
+ if (reg_ret == ERROR_SUCCESS && chimney_enabled) {
+ item_hb = welcome_button(WIRESHARK_STOCK_WIKI,
+ "Offloading Detected",
+ "TCP Chimney offloading is enabled. You \nmight not capture much data.",
+ topic_online_url(ONLINEPAGE_CHIMNEY),
+ topic_menu_cb, GINT_TO_POINTER(ONLINEPAGE_CHIMNEY));
+ gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
+ }
+#endif /* _WIN32 */
+ } else {
+ if (if_list == NULL && err != NO_INTERFACES_FOUND) {
+ g_free(err_str);
+ if (err == CANT_GET_INTERFACE_LIST) {
+ label_text = g_strdup("No interface can be used for capturing in "
+ "this system with the current configuration.\n"
+ "\n"
+ "See Capture Help below for details.");
+ } else {
+ label_text = g_strdup("WinPcap doesn't appear to be installed. "
+ "In order to capture packets, WinPcap "
+ "must be installed; see\n"
+ "\n"
+#if GTK_CHECK_VERSION(2,18,0)
+ " <a href=\"http://www.winpcap.org/\">http://www.winpcap.org/</a>\n"
+#else
+ " http://www.winpcap.org/\n"
+#endif
+ "\n"
+ "or the mirror at\n"
+ "\n"
+#if GTK_CHECK_VERSION(2,18,0)
+ " <a href=\"http://www.mirrors.wiretapped.net/security/packet-capture/winpcap/\">http://www.mirrors.wiretapped.net/security/packet-capture/winpcap/</a>\n"
+#else
+ " http://www.mirrors.wiretapped.net/security/packet-capture/winpcap/\n"
+#endif
+ "\n"
+ "or the mirror at\n"
+ "\n"
+#if GTK_CHECK_VERSION(2,18,0)
+ " <a href=\"http://winpcap.cs.pu.edu.tw/\">http://winpcap.cs.pu.edu.tw/</a>\n"
+#else
+ " http://winpcap.cs.pu.edu.tw/\n"
+#endif
+ "\n"
+ "for a downloadable version of WinPcap "
+ "and for instructions on how to install "
+ "WinPcap.");
+ }
+ } else {
+ label_text = g_strdup("No interface can be used for capturing in "
+ "this system with the current configuration.\n"
+ "\n"
+ "See Capture Help below for details.");
+ }
+ w = gtk_label_new(label_text);
+ gtk_label_set_markup(GTK_LABEL(w), label_text);
+ gtk_label_set_line_wrap(GTK_LABEL(w), TRUE);
+ g_free (label_text);
+ gtk_misc_set_alignment (GTK_MISC(w), 0.0f, 0.0f);
+ gtk_box_pack_start(GTK_BOX(topic_to_fill), w, FALSE, FALSE, 5);
+#if GTK_CHECK_VERSION(2,18,0)
+ g_signal_connect(w, "activate-link", G_CALLBACK(activate_link_cb), NULL);
+#endif
+ }
+
+ free_interface_list(if_list);
+
+ /* capture help topic */
+ topic_vb = welcome_topic_new("Capture Help", &topic_to_fill);
+ gtk_box_pack_start(GTK_BOX(column_vb), topic_vb, TRUE, TRUE, 0);
+
+ item_hb = welcome_button(WIRESHARK_STOCK_WIKI,
+ "How to Capture",
+ "Step by step to a successful capture setup",
+ topic_online_url(ONLINEPAGE_CAPTURE_SETUP),
+ topic_menu_cb, GINT_TO_POINTER(ONLINEPAGE_CAPTURE_SETUP));
+ gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
+
+ item_hb = welcome_button(WIRESHARK_STOCK_WIKI,
+ "Network Media",
+ "Specific information for capturing on:\nEthernet, WLAN, ...",
+ topic_online_url(ONLINEPAGE_NETWORK_MEDIA),
+ topic_menu_cb, GINT_TO_POINTER(ONLINEPAGE_NETWORK_MEDIA));
+ gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
+#else
+ label_text = g_strdup("<span foreground=\"black\">Capturing is not compiled into\nthis version of Wireshark!</span>");
+ w = gtk_label_new(label_text);
+ gtk_label_set_markup(GTK_LABEL(w), label_text);
+ g_free (label_text);
+ gtk_misc_set_alignment (GTK_MISC(w), 0.0f, 0.0f);
+ gtk_box_pack_start(GTK_BOX(topic_to_fill), w, FALSE, FALSE, 5);
+#endif /* HAVE_LIBPCAP */
+
+ /* fill bottom space */
+ w = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(topic_to_fill), w, TRUE, TRUE, 0);
+
+
+ /* column files */
+ topic_vb = welcome_topic_new("Files", &topic_to_fill);
+ gtk_box_pack_start(GTK_BOX(welcome_hb), topic_vb, TRUE, TRUE, 0);
+
+ item_hb = welcome_button(GTK_STOCK_OPEN,
+ "Open",
+ "Open a previously captured file",
+ "Same as File/Open menu or toolbar item",
+ welcome_button_callback_helper, file_open_cmd_cb);
+ gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
+
+ /* prepare list of recent files (will be filled in later) */
+ label_text = g_strdup("<span foreground=\"black\">Open Recent:</span>");
+ w = gtk_label_new(label_text);
+ gtk_label_set_markup(GTK_LABEL(w), label_text);
+ g_free (label_text);
+ gtk_misc_set_alignment (GTK_MISC(w), 0.0f, 0.0f);
+ gtk_box_pack_start(GTK_BOX(topic_to_fill), w, FALSE, FALSE, 5);
+
+ file_child_box = gtk_vbox_new(FALSE, 1);
+ /* 17 file items or 300 pixels height is about the size */
+ /* that still fits on a screen of about 1000*700 */
+ welcome_file_panel_vb = scroll_box_dynamic_new(GTK_WIDGET(file_child_box), 17, 300);
+ gtk_box_pack_start(GTK_BOX(topic_to_fill), welcome_file_panel_vb, FALSE, FALSE, 0);
+
+ item_hb = welcome_button(WIRESHARK_STOCK_WIKI,
+ "Sample Captures",
+ "A rich assortment of example capture files on the wiki",
+ topic_online_url(ONLINEPAGE_SAMPLE_CAPTURES),
+ topic_menu_cb, GINT_TO_POINTER(ONLINEPAGE_SAMPLE_CAPTURES));
+ gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
+
+ /* fill bottom space */
+ w = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(topic_to_fill), w, TRUE, TRUE, 0);
+
+
+ /* column online */
+ column_vb = gtk_vbox_new(FALSE, 10);
+ gtk_box_pack_start(GTK_BOX(welcome_hb), column_vb, TRUE, TRUE, 0);
+
+ /* topic online */
+ topic_vb = welcome_topic_new("Online", &topic_to_fill);
+ gtk_box_pack_start(GTK_BOX(column_vb), topic_vb, TRUE, TRUE, 0);
+
+ item_hb = welcome_button(GTK_STOCK_HOME,
+ "Website",
+ "Visit the project's website",
+ topic_online_url(ONLINEPAGE_HOME),
+ topic_menu_cb, GINT_TO_POINTER(ONLINEPAGE_HOME));
+ gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
+
+#ifdef HHC_DIR
+ item_hb = welcome_button(GTK_STOCK_HELP,
+ "User's Guide",
+ "The User's Guide "
+ "(local version, if installed)",
+ "Locally installed (if installed) otherwise online version",
+ topic_menu_cb, GINT_TO_POINTER(HELP_CONTENT));
+#else
+ item_hb = welcome_button(GTK_STOCK_HELP,
+ "User's Guide",
+ "The User's Guide "
+ "(online version)",
+ topic_online_url(ONLINEPAGE_USERGUIDE),
+ topic_menu_cb, GINT_TO_POINTER(ONLINEPAGE_USERGUIDE));
+#endif
+ gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
+
+ item_hb = welcome_button(WIRESHARK_STOCK_WIKI,
+ "Security",
+ "Work with Wireshark as securely as possible",
+ topic_online_url(ONLINEPAGE_SECURITY),
+ topic_menu_cb, GINT_TO_POINTER(ONLINEPAGE_SECURITY));
+ gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
+
+#if 0
+ /* XXX - add this, once the Windows update functionality is implemented */
+ /* topic updates */
+ topic_vb = welcome_topic_new("Updates", &topic_to_fill);
+ gtk_box_pack_start(GTK_BOX(column_vb), topic_vb, TRUE, TRUE, 0);
+
+ label_text = g_strdup("<span foreground=\"black\">No updates available!</span>");
+ w = gtk_label_new(label_text);
+ gtk_label_set_markup(GTK_LABEL(w), label_text);
+ g_free (label_text);
+ gtk_box_pack_start(GTK_BOX(topic_to_fill), w, TRUE, TRUE, 0);
+#endif
+
+
+ /* the end */
+ gtk_widget_show_all(welcome_eb);
+
+ gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(welcome_scrollw),
+ welcome_eb);
+ gtk_widget_show_all(welcome_scrollw);
+
+#if GLIB_CHECK_VERSION(2,31,0)
+ recent_mtx = g_malloc(sizeof(GMutex));
+ g_mutex_init(recent_mtx);
+#else
+ recent_mtx = g_mutex_new();
+#endif
+
+ return welcome_scrollw;
+}
+
+GtkWidget* get_welcome_window(void)
+{
+ return welcome_hb;
+}
+
+#ifdef HAVE_LIBPCAP
+displayed_interface get_interface_data(gint index)
+{
+ return g_array_index(interfaces, displayed_interface, index);
+}
+#endif
diff --git a/ui/gtk/main_welcome.h b/ui/gtk/main_welcome.h
new file mode 100644
index 0000000000..23996e3723
--- /dev/null
+++ b/ui/gtk/main_welcome.h
@@ -0,0 +1,94 @@
+/* main_welcome.h
+ * Welcome "page"
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MAIN_WELCOME_H__
+#define __MAIN_WELCOME_H__
+
+#include "ui/gtk/capture_dlg.h"
+
+enum
+{
+ ICON = 0,
+ IFACE_DESCR,
+ IFACE_NAME,
+ NUMCOLUMNS
+};
+
+typedef struct selected_name_s {
+ gchar *name;
+ gboolean activate;
+} selected_name_t;
+
+typedef struct displayed_interface_s {
+ gchar *name;
+ gchar *descr;
+ GtkWidget *icon;
+#ifdef HAVE_PCAP_REMOTE
+ remote_options remote_opts;
+#endif
+} displayed_interface;
+
+GtkWidget *welcome_new(void);
+
+/* reset the list of recently used files */
+void main_welcome_reset_recent_capture_files(void);
+
+/* add a new file to the list of recently used files */
+void main_welcome_add_recent_capture_file(const char *widget_cf_name, GObject *menu_item);
+
+/* reload the list of interfaces */
+void welcome_if_panel_reload(void);
+
+/** Push a status message into the welcome screen header similar to
+ * statusbar_push_*_msg(). This hides everything under the header.
+ * If msg is dynamically allocated, it is up to the caller to free
+ * it. If msg is NULL, the default message will be shown.
+ *
+ * @param msg The message
+ */
+void welcome_header_push_msg(const gchar *msg);
+
+void welcome_header_set_message(gchar *msg);
+
+/** Pop a status message from the welcome screen. If there are no
+ * messages on the stack, the default message and the main columns
+ * will be shown.
+ */
+void welcome_header_pop_msg(void);
+
+void select_ifaces(void);
+
+GtkWidget* get_welcome_window(void);
+
+void change_interface_selection(gchar* name, gboolean activate);
+
+void change_selection_for_all(gboolean enable);
+
+#ifdef HAVE_PCAP_REMOTE
+void add_interface_to_list(gchar *name, gchar *descr, remote_options *remote_opts);
+#endif
+
+displayed_interface get_interface_data(gint index);
+
+#endif /* __MAIN_WELCOME_H__ */
diff --git a/ui/gtk/manual_addr_resolv.c b/ui/gtk/manual_addr_resolv.c
new file mode 100644
index 0000000000..9ac030fe3b
--- /dev/null
+++ b/ui/gtk/manual_addr_resolv.c
@@ -0,0 +1,178 @@
+/* manual_addr_resolv.c
+ * Dialog box for manual address resolve
+ * Copyright 2010 Stig Bjorlykke <stig@bjorlykke.org>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/addr_resolv.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/menus.h"
+#include "ui/gtk/manual_addr_resolv.h"
+#include "ui/gtk/old-gtk-compat.h"
+
+GtkWidget *man_addr_resolv_dlg = NULL;
+
+static void
+man_addr_ill_addr_cb (gpointer dialog _U_, gint btn _U_, gpointer data _U_)
+{
+ gtk_window_present (GTK_WINDOW(man_addr_resolv_dlg));
+}
+
+static void
+man_addr_resolv_ok (GtkWidget *w _U_, gpointer data _U_)
+{
+ GtkWidget *addr_cb, *name_te, *resolv_cb;
+ const gchar *addr, *name;
+ gboolean active, redissect = FALSE;
+ addr_cb = g_object_get_data (G_OBJECT(man_addr_resolv_dlg), "address");
+ name_te = g_object_get_data (G_OBJECT(man_addr_resolv_dlg), "name");
+
+ addr = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(addr_cb));
+ name = gtk_entry_get_text (GTK_ENTRY (name_te));
+
+ if (strlen (addr) && strlen (name)) {
+ if (!add_ip_name_from_string (addr, name)) {
+ GtkWidget *dialog = simple_dialog (ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Illegal IP address: \"%s\".", addr);
+ simple_dialog_set_cb (dialog, man_addr_ill_addr_cb, NULL);
+ return;
+ } else {
+ redissect = TRUE;
+ }
+ }
+
+ resolv_cb = g_object_get_data (G_OBJECT(man_addr_resolv_dlg), "resolv");
+ active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(resolv_cb));
+ if (!(gbl_resolv_flags & RESOLV_NETWORK) && active) {
+ /* Name resolution for Network Layer activated */
+ gbl_resolv_flags |= RESOLV_NETWORK;
+ menu_name_resolution_changed ();
+ redissect = TRUE;
+ }
+
+ if (redissect) {
+ redissect_packets ();
+ }
+ window_destroy (man_addr_resolv_dlg);
+ man_addr_resolv_dlg = NULL;
+}
+
+static void
+name_te_changed_cb(GtkWidget *name_te, GtkWidget *ok_bt)
+{
+ const gchar *name = gtk_entry_get_text (GTK_ENTRY (name_te));
+ gtk_widget_set_sensitive (ok_bt, strlen (name) > 0 ? TRUE : FALSE);
+}
+
+void
+manual_addr_resolv_dlg (GtkWidget *w _U_, gpointer data)
+{
+ GtkWidget *vbox, *bbox, *table, *sep;
+ GtkWidget *ok_bt, *close_bt, *help_bt;
+ GtkWidget *addr_lb, *addr_cb;
+ GtkWidget *name_lb, *name_te, *resolv_cb;
+ GList *addr_list = NULL;
+
+ man_addr_resolv_dlg = dlg_window_new ("Manual Address Resolve");
+ gtk_window_set_default_size (GTK_WINDOW(man_addr_resolv_dlg), 310, 80);
+
+ vbox = gtk_vbox_new (FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(man_addr_resolv_dlg), vbox);
+ gtk_container_set_border_width (GTK_CONTAINER(vbox), 6);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_container_add(GTK_CONTAINER(vbox), table);
+ addr_lb = gtk_label_new("Address:");
+ gtk_table_attach_defaults (GTK_TABLE (table), addr_lb, 0, 1, 0, 1);
+
+ addr_cb = gtk_combo_box_text_new_with_entry();
+ if (data) {
+ GList *addr_entry;
+ addr_list = get_ip_address_list_from_packet_list_row(data);
+ for (addr_entry = addr_list; addr_entry != NULL; addr_entry = g_list_next (addr_entry)) {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(addr_cb), addr_entry->data);
+ }
+ gtk_combo_box_set_active (GTK_COMBO_BOX(addr_cb), 0);
+ }
+ gtk_table_attach_defaults (GTK_TABLE (table), addr_cb, 1, 2, 0, 1);
+ g_object_set_data (G_OBJECT(man_addr_resolv_dlg), "address", addr_cb);
+
+ name_lb = gtk_label_new("Name:");
+ gtk_table_attach_defaults (GTK_TABLE (table), name_lb, 0, 1, 1, 2);
+
+ name_te = gtk_entry_new ();
+ gtk_table_attach_defaults (GTK_TABLE (table), name_te, 1, 2, 1, 2);
+ g_object_set_data (G_OBJECT(man_addr_resolv_dlg), "name", name_te);
+
+ sep = gtk_hseparator_new ();
+ gtk_container_add (GTK_CONTAINER(vbox), sep);
+
+ resolv_cb = gtk_check_button_new_with_mnemonic ("Enable network name resolution");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(resolv_cb), gbl_resolv_flags & RESOLV_NETWORK);
+ gtk_widget_set_sensitive (resolv_cb, !(gbl_resolv_flags & RESOLV_NETWORK));
+
+ gtk_widget_set_tooltip_text(resolv_cb, "Perform network layer name resolution.");
+ g_object_set_data (G_OBJECT(man_addr_resolv_dlg), "resolv", resolv_cb);
+ gtk_container_add (GTK_CONTAINER(vbox), resolv_cb);
+
+ /* Button row. */
+ bbox = dlg_button_row_new (GTK_STOCK_OK, GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_end (GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ ok_bt = g_object_get_data (G_OBJECT(bbox), GTK_STOCK_OK);
+ g_signal_connect (ok_bt, "clicked", G_CALLBACK(man_addr_resolv_ok), NULL);
+ gtk_widget_set_sensitive (ok_bt, FALSE);
+ g_signal_connect(name_te, "changed", G_CALLBACK(name_te_changed_cb), ok_bt);
+ dlg_set_activate(name_te, ok_bt);
+
+ close_bt = g_object_get_data (G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button (man_addr_resolv_dlg, close_bt, window_cancel_button_cb);
+
+ help_bt = g_object_get_data (G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect (help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_MANUAL_ADDR_RESOLVE_DIALOG);
+
+ gtk_widget_grab_default (ok_bt);
+ g_signal_connect (man_addr_resolv_dlg, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+
+ if (addr_list) {
+ /* We have column data, activate name box */
+ gtk_widget_grab_focus (name_te);
+ }
+ gtk_widget_show_all (man_addr_resolv_dlg);
+ window_present (man_addr_resolv_dlg);
+}
diff --git a/ui/gtk/manual_addr_resolv.h b/ui/gtk/manual_addr_resolv.h
new file mode 100644
index 0000000000..5d27201299
--- /dev/null
+++ b/ui/gtk/manual_addr_resolv.h
@@ -0,0 +1,31 @@
+/* manual_addr_resolv.h
+ * Dialog box for manual address resolve
+ * Copyright 2010 Stig Bjorlykke <stig@bjorlykke.org>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MANUAL_ADDR_RESOLV_H__
+#define __MANUAL_ADDR_RESOLV_H__
+
+void manual_addr_resolv_dlg (GtkWidget *w _U_, gpointer data);
+
+#endif /* __MANUAL_ADDR_RESOLV_H__ */
diff --git a/ui/gtk/mcast_stream.c b/ui/gtk/mcast_stream.c
new file mode 100644
index 0000000000..8a562e7959
--- /dev/null
+++ b/ui/gtk/mcast_stream.c
@@ -0,0 +1,469 @@
+/* mcast_stream.c
+ *
+ * Copyright 2006, Iskratel , Slovenia
+ * By Jakob Bratkovic <j.bratkovic@iskratel.si> and
+ * Miha Jemec <m.jemec@iskratel.si>
+ *
+ * $Id$
+ *
+ * based on rtp_stream.c
+ * Copyright 2003, Alcatel Business Systems
+ * By Lars Ruoff <lars.ruoff@gmx.net>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <glib.h>
+
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/epan.h>
+#include <epan/address.h>
+#include <epan/packet.h>
+#include <epan/tap.h>
+#include <epan/strutil.h>
+
+#include "../alert_box.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/mcast_stream.h"
+#include "ui/gtk/mcast_stream_dlg.h"
+#include "ui/gtk/main.h"
+
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+
+gint32 mcast_stream_trigger = 50; /* limit for triggering the burst alarm (in packets per second) */
+gint32 mcast_stream_bufferalarm = 10000; /* limit for triggernig the buffer alarm (in bytes) */
+guint16 mcast_stream_burstint = 100; /* burst interval in ms */
+gint32 mcast_stream_emptyspeed = 5000; /* outgoing speed for single stream (kbps)*/
+gint32 mcast_stream_cumulemptyspeed = 100000; /* outgoiong speed for all streams (kbps)*/
+
+/* sliding window and buffer usage */
+static gint32 buffsize = (int)((double)MAX_SPEED * 100 / 1000) * 2;
+static guint16 comparetimes(struct timeval *t1, struct timeval *t2, guint16 burstint_lcl);
+static void buffusagecalc(mcast_stream_info_t *strinfo, packet_info *pinfo, double emptyspeed_lcl);
+static void slidingwindow(mcast_stream_info_t *strinfo, packet_info *pinfo);
+
+
+/****************************************************************************/
+/* the one and only global mcaststream_tapinfo_t structure */
+static mcaststream_tapinfo_t the_tapinfo_struct =
+ {0, NULL, 0, NULL, 0, FALSE};
+
+
+/****************************************************************************/
+/* GCompareFunc style comparison function for _mcast_stream_info */
+static gint
+mcast_stream_info_cmp(gconstpointer aa, gconstpointer bb)
+{
+ const struct _mcast_stream_info* a = aa;
+ const struct _mcast_stream_info* b = bb;
+
+ if (a==b)
+ return 0;
+ if (a==NULL || b==NULL)
+ return 1;
+ if (ADDRESSES_EQUAL(&(a->src_addr), &(b->src_addr))
+ && (a->src_port == b->src_port)
+ && ADDRESSES_EQUAL(&(a->dest_addr), &(b->dest_addr))
+ && (a->dest_port == b->dest_port))
+ return 0;
+ else
+ return 1;
+
+}
+
+
+/****************************************************************************/
+/* when there is a [re]reading of packet's */
+void
+mcaststream_reset(mcaststream_tapinfo_t *tapinfo)
+{
+ GList* list;
+
+ /* free the data items first */
+ list = g_list_first(tapinfo->strinfo_list);
+ while (list)
+ {
+ /* XYZ I don't know how to clean this */
+ /*g_free(list->element.buff); */
+ g_free(list->data);
+ list = g_list_next(list);
+ }
+ g_list_free(tapinfo->strinfo_list);
+ tapinfo->strinfo_list = NULL;
+
+ /* XYZ and why does the line below causes a crach? */
+ /*g_free(tapinfo->allstreams->element.buff);*/
+ g_free(tapinfo->allstreams);
+ tapinfo->allstreams = NULL;
+
+ tapinfo->nstreams = 0;
+ tapinfo->npackets = 0;
+
+ ++(tapinfo->launch_count);
+
+ return;
+}
+
+static void
+mcaststream_reset_cb(void *arg)
+{
+ mcaststream_reset(arg);
+}
+
+/****************************************************************************/
+/* redraw the output */
+static void
+mcaststream_draw(void *arg _U_)
+{
+/* XXX: see mcaststream_on_update in mcast_streams_dlg.c for comments
+ g_signal_emit_by_name(top_level, "signal_mcaststream_update");
+*/
+ mcaststream_dlg_update(the_tapinfo_struct.strinfo_list);
+ return;
+}
+
+
+
+/****************************************************************************/
+/* whenever a udp packet is seen by the tap listener */
+static int
+mcaststream_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *arg2 _U_)
+{
+ mcaststream_tapinfo_t *tapinfo = arg;
+ mcast_stream_info_t tmp_strinfo;
+ mcast_stream_info_t *strinfo = NULL;
+ GList* list;
+ float deltatime;
+
+ /* gather infos on the stream this packet is part of */
+ COPY_ADDRESS(&(tmp_strinfo.src_addr), &(pinfo->src));
+ tmp_strinfo.src_port = pinfo->srcport;
+ COPY_ADDRESS(&(tmp_strinfo.dest_addr), &(pinfo->dst));
+ tmp_strinfo.dest_port = pinfo->destport;
+
+ /* first we ignore non multicast packets; we filter out only those ethernet packets
+ * which start with the 01:00:5E multicast address (for IPv4) and 33:33 multicast
+ * address (for IPv6).
+ */
+ if ((pinfo->dl_dst.type != AT_ETHER) ||
+ ((g_ascii_strncasecmp("01005E", bytes_to_str(pinfo->dl_dst.data, pinfo->dl_dst.len), 6) != 0) &&
+ (g_ascii_strncasecmp("3333", bytes_to_str(pinfo->dl_dst.data, pinfo->dl_dst.len), 4) != 0)) )
+ return 0;
+
+ /* check whether we already have a stream with these parameters in the list */
+ list = g_list_first(tapinfo->strinfo_list);
+ while (list)
+ {
+ if (mcast_stream_info_cmp(&tmp_strinfo, (mcast_stream_info_t*)(list->data))==0)
+ {
+ strinfo = (mcast_stream_info_t*)(list->data); /*found!*/
+ break;
+ }
+ list = g_list_next(list);
+ }
+
+ /* not in the list? then create a new entry */
+ if (!strinfo) {
+ /*printf("nov sip %s sp %d dip %s dp %d\n", g_strdup(get_addr_name(&(pinfo->src))),
+ pinfo->srcport, g_strdup(get_addr_name(&(pinfo->dst))), pinfo->destport);*/
+ tmp_strinfo.npackets = 0;
+ tmp_strinfo.apackets = 0;
+ tmp_strinfo.first_frame_num = pinfo->fd->num;
+ tmp_strinfo.start_sec = (guint32) pinfo->fd->abs_ts.secs;
+ tmp_strinfo.start_usec = pinfo->fd->abs_ts.nsecs/1000;
+ tmp_strinfo.start_rel_sec = (guint32) pinfo->fd->rel_ts.secs;
+ tmp_strinfo.start_rel_usec = pinfo->fd->rel_ts.nsecs/1000;
+ tmp_strinfo.vlan_id = 0;
+
+ /* reset Mcast stats */
+ tmp_strinfo.average_bw = 0;
+ tmp_strinfo.total_bytes = 0;
+
+ /* reset slidingwindow and buffer parameters */
+ tmp_strinfo.element.buff = (struct timeval *)g_malloc(buffsize * sizeof(struct timeval));
+ tmp_strinfo.element.first=0;
+ tmp_strinfo.element.last=0;
+ tmp_strinfo.element.burstsize=1;
+ tmp_strinfo.element.topburstsize=1;
+ tmp_strinfo.element.numbursts=0;
+ tmp_strinfo.element.burststatus=0;
+ tmp_strinfo.element.count=1;
+ tmp_strinfo.element.buffusage=pinfo->fd->pkt_len;
+ tmp_strinfo.element.topbuffusage=pinfo->fd->pkt_len;
+ tmp_strinfo.element.numbuffalarms=0;
+ tmp_strinfo.element.buffstatus=0;
+ tmp_strinfo.element.maxbw=0;
+
+ strinfo = g_malloc(sizeof(mcast_stream_info_t));
+ *strinfo = tmp_strinfo; /* memberwise copy of struct */
+ tapinfo->strinfo_list = g_list_append(tapinfo->strinfo_list, strinfo);
+ strinfo->element.buff = (struct timeval *)g_malloc(buffsize * sizeof(struct timeval));
+
+ /* set time with the first packet */
+ if (tapinfo->npackets == 0) {
+ tapinfo->allstreams = g_malloc(sizeof(mcast_stream_info_t));
+ tapinfo->allstreams->element.buff =
+ (struct timeval *)g_malloc(buffsize * sizeof(struct timeval));
+ tapinfo->allstreams->start_rel_sec = (guint32) pinfo->fd->rel_ts.secs;
+ tapinfo->allstreams->start_rel_usec = pinfo->fd->rel_ts.nsecs/1000;
+ tapinfo->allstreams->total_bytes = 0;
+ tapinfo->allstreams->element.first=0;
+ tapinfo->allstreams->element.last=0;
+ tapinfo->allstreams->element.burstsize=1;
+ tapinfo->allstreams->element.topburstsize=1;
+ tapinfo->allstreams->element.numbursts=0;
+ tapinfo->allstreams->element.burststatus=0;
+ tapinfo->allstreams->element.count=1;
+ tapinfo->allstreams->element.buffusage=pinfo->fd->pkt_len;
+ tapinfo->allstreams->element.topbuffusage=pinfo->fd->pkt_len;
+ tapinfo->allstreams->element.numbuffalarms=0;
+ tapinfo->allstreams->element.buffstatus=0;
+ tapinfo->allstreams->element.maxbw=0;
+ }
+ }
+
+ /* time between first and last packet in the group */
+ strinfo->stop_rel_sec = (guint32) pinfo->fd->rel_ts.secs;
+ strinfo->stop_rel_usec = pinfo->fd->rel_ts.nsecs/1000;
+ deltatime = ((float)((strinfo->stop_rel_sec * 1000000 + strinfo->stop_rel_usec)
+ - (strinfo->start_rel_sec*1000000 + strinfo->start_rel_usec)))/1000000;
+
+ /* calculate average bandwidth for this stream */
+ strinfo->total_bytes = strinfo->total_bytes + pinfo->fd->pkt_len;
+ if (deltatime > 0)
+ strinfo->average_bw = (((float)(strinfo->total_bytes*8) / deltatime) / 1000000);
+
+ /* increment the packets counter for this stream and calculate average pps */
+ ++(strinfo->npackets);
+ strinfo->apackets = (guint32) (strinfo->npackets / deltatime);
+
+ /* time between first and last packet in any group */
+ tapinfo->allstreams->stop_rel_sec = (guint32) pinfo->fd->rel_ts.secs;
+ tapinfo->allstreams->stop_rel_usec = pinfo->fd->rel_ts.nsecs/1000;
+ deltatime = ((float)((tapinfo->allstreams->stop_rel_sec * 1000000 + tapinfo->allstreams->stop_rel_usec)
+ - (tapinfo->allstreams->start_rel_sec*1000000 + tapinfo->allstreams->start_rel_usec)))/1000000;
+
+ /* increment the packets counter of all streams */
+ ++(tapinfo->npackets);
+
+ /* calculate average bandwidth for all streams */
+ tapinfo->allstreams->total_bytes = tapinfo->allstreams->total_bytes + pinfo->fd->pkt_len;
+ if (deltatime > 0)
+ tapinfo->allstreams->average_bw = (((float)(tapinfo->allstreams->total_bytes *8) / deltatime) / 1000000);
+
+ /* sliding window and buffercalc for this group*/
+ slidingwindow(strinfo, pinfo);
+ buffusagecalc(strinfo, pinfo, mcast_stream_emptyspeed*1000);
+ /* sliding window and buffercalc for all groups */
+ slidingwindow(tapinfo->allstreams, pinfo);
+ buffusagecalc(tapinfo->allstreams, pinfo, mcast_stream_cumulemptyspeed*1000);
+ /* end of sliding window */
+
+ return 1; /* refresh output */
+
+}
+
+/****************************************************************************/
+/* scan for Mcast streams */
+void
+mcaststream_scan(void)
+{
+ gboolean was_registered = the_tapinfo_struct.is_registered;
+ if (!the_tapinfo_struct.is_registered)
+ register_tap_listener_mcast_stream();
+
+ cf_retap_packets(&cfile);
+
+ if (!was_registered)
+ remove_tap_listener_mcast_stream();
+}
+
+
+/****************************************************************************/
+const mcaststream_tapinfo_t *
+mcaststream_get_info(void)
+{
+ return &the_tapinfo_struct;
+}
+
+
+/****************************************************************************/
+/* TAP INTERFACE */
+/****************************************************************************/
+
+/****************************************************************************/
+void
+remove_tap_listener_mcast_stream(void)
+{
+ if (the_tapinfo_struct.is_registered) {
+ protect_thread_critical_region();
+ remove_tap_listener(&the_tapinfo_struct);
+ unprotect_thread_critical_region();
+
+ the_tapinfo_struct.is_registered = FALSE;
+ }
+}
+
+
+/****************************************************************************/
+void
+register_tap_listener_mcast_stream(void)
+{
+ GString *error_string;
+ if (!the_tapinfo_struct.is_registered) {
+ error_string = register_tap_listener("udp", &the_tapinfo_struct,
+ NULL, 0, mcaststream_reset_cb, mcaststream_packet,
+ mcaststream_draw);
+
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+
+ the_tapinfo_struct.is_registered = TRUE;
+ }
+}
+
+/*******************************************************************************/
+/* sliding window and buffer calculations */
+
+/* compare two times */
+static guint16
+comparetimes(struct timeval *t1, struct timeval *t2, guint16 burstint_lcl)
+{
+ if(((t2->tv_sec - t1->tv_sec)*1000 + (t2->tv_usec - t1->tv_usec)/1000) > burstint_lcl){
+ return 1;
+ } else{
+ return 0;
+ }
+}
+
+/* calculate buffer usage */
+static void
+buffusagecalc(mcast_stream_info_t *strinfo, packet_info *pinfo, double emptyspeed_lcl)
+{
+ gint32 sec=0, usec=0, cur, prev;
+ struct timeval *buffer;
+ double timeelapsed;
+
+ buffer = strinfo->element.buff;
+ cur = strinfo->element.last;
+ if(cur == 0){
+ cur = buffsize - 1;
+ prev = cur - 1;
+ } else if(cur == 1){
+ prev = buffsize - 1;
+ cur = 0;
+ } else{
+ cur=cur-1;
+ prev=cur-1;
+ }
+
+ sec = buffer[cur].tv_sec - buffer[prev].tv_sec;
+ usec = buffer[cur].tv_usec - buffer[prev].tv_usec;
+ timeelapsed = (double)usec/1000000 + (double)sec;
+
+ /* bytes added to buffer */
+ strinfo->element.buffusage+=pinfo->fd->pkt_len;
+
+ /* bytes cleared from buffer */
+ strinfo->element.buffusage-= (guint32) (timeelapsed * emptyspeed_lcl / 8);
+
+ if(strinfo->element.buffusage < 0) strinfo->element.buffusage=0;
+ if(strinfo->element.buffusage > strinfo->element.topbuffusage)
+ strinfo->element.topbuffusage = strinfo->element.buffusage;
+ /* check for buffer losses */
+ if((strinfo->element.buffusage >= mcast_stream_bufferalarm) && (strinfo->element.buffstatus == 0)){
+ strinfo->element.buffstatus = 1;
+ strinfo->element.numbuffalarms++;
+ } else if(strinfo->element.buffusage < mcast_stream_bufferalarm){
+ strinfo->element.buffstatus = 0;
+ }
+
+ return;
+}
+
+/* sliding window calculation */
+static void
+slidingwindow(mcast_stream_info_t *strinfo, packet_info *pinfo)
+{
+ struct timeval *buffer;
+ gint32 diff;
+
+ buffer = strinfo->element.buff;
+
+ diff = strinfo->element.last - strinfo->element.first;
+ if(diff < 0) diff+=buffsize;
+
+ /* check if buffer is full */
+ if(diff >= (buffsize - 2)){
+ fprintf(stderr, "Warning: capture buffer full\n");
+ strinfo->element.first++;
+ if(strinfo->element.first >= buffsize) strinfo->element.first = strinfo->element.first % buffsize;
+ }
+
+ /* burst count */
+ buffer[strinfo->element.last].tv_sec = (guint32) pinfo->fd->rel_ts.secs;
+ buffer[strinfo->element.last].tv_usec = pinfo->fd->rel_ts.nsecs/1000;
+ while(comparetimes((struct timeval *)&(buffer[strinfo->element.first]),
+ (struct timeval *)&(buffer[strinfo->element.last]), mcast_stream_burstint)){
+ strinfo->element.first++;
+ if(strinfo->element.first >= buffsize) strinfo->element.first = strinfo->element.first % buffsize;
+ diff--;
+ }
+ strinfo->element.burstsize = diff;
+ if(strinfo->element.burstsize > strinfo->element.topburstsize) {
+ strinfo->element.topburstsize = strinfo->element.burstsize;
+ strinfo->element.maxbw = (float)(strinfo->element.topburstsize) * 1000 / mcast_stream_burstint * pinfo->fd->pkt_len * 8 / 1000000;
+ }
+
+ strinfo->element.last++;
+ if(strinfo->element.last >= buffsize) strinfo->element.last = strinfo->element.last % buffsize;
+ /* trigger check */
+ if((strinfo->element.burstsize >= mcast_stream_trigger) && (strinfo->element.burststatus == 0)){
+ strinfo->element.burststatus = 1;
+ strinfo->element.numbursts++;
+ } else if(strinfo->element.burstsize < mcast_stream_trigger){
+ strinfo->element.burststatus = 0;
+ }
+
+ strinfo->element.count++;
+}
+
diff --git a/ui/gtk/mcast_stream.h b/ui/gtk/mcast_stream.h
new file mode 100644
index 0000000000..0cf3d101a7
--- /dev/null
+++ b/ui/gtk/mcast_stream.h
@@ -0,0 +1,136 @@
+/* mcast_stream.h
+ *
+ * Copyright 2006, Iskratel , Slovenia
+ * By Jakob Bratkovic <j.bratkovic@iskratel.si> and
+ * Miha Jemec <m.jemec@iskratel.si>
+ *
+ * $Id$
+ *
+ * based on rtp_stream.h
+ * Copyright 2003, Alcatel Business Systems
+ * By Lars Ruoff <lars.ruoff@gmx.net>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MCAST_STREAM_H__
+#define __MCAST_STREAM_H__
+
+#define MAX_SPEED 200000
+
+/* typedefs for sliding window and buffer size */
+typedef struct buffer{
+ struct timeval *buff; /* packet times */
+ gint32 first; /* pointer to the first element */
+ gint32 last; /* pointer to the last element */
+ gint32 burstsize; /* current burst */
+ gint32 topburstsize; /* maximum burst in the refresh interval*/
+ gint32 count; /* packet counter */
+ gint32 burststatus; /* burst status */
+ gint32 numbursts; /* number of bursts */
+ gint32 buffusage; /* buffer usage */
+ gint32 buffstatus; /* buffer status */
+ gint32 numbuffalarms; /* number of alarms triggered by buffer underruns */
+ gint32 topbuffusage; /* top buffer usage in refresh interval */
+ float maxbw; /* maximum bandwidth usage */
+} t_buffer;
+
+
+/* defines an mcast stream */
+typedef struct _mcast_stream_info {
+ address src_addr;
+ guint16 src_port;
+ address dest_addr;
+ guint16 dest_port;
+ guint32 npackets;
+ guint32 apackets;
+ guint32 total_bytes;
+ float average_bw;
+
+ guint32 first_frame_num; /* frame number of first frame */
+ /* start of recording (GMT) of this stream */
+ guint32 start_sec; /* seconds */
+ guint32 start_usec; /* microseconds */
+ guint32 start_rel_sec; /* start stream rel seconds */
+ guint32 start_rel_usec; /* start stream rel microseconds */
+ guint32 stop_rel_sec; /* stop stream rel seconds */
+ guint32 stop_rel_usec; /* stop stream rel microseconds */
+ guint16 vlan_id;
+
+ /*for the sliding window */
+ t_buffer element;
+
+} mcast_stream_info_t;
+
+
+/* structure that holds the information about all detected streams */
+/* struct holding all information of the tap */
+typedef struct _mcaststream_tapinfo {
+ int nstreams; /* number of streams in the list */
+ GList* strinfo_list; /* list with all streams */
+ guint32 npackets; /* total number of mcast packets of all streams */
+ mcast_stream_info_t* allstreams; /* structure holding information common for all streams */
+
+ guint32 launch_count; /* number of times the tap has been run */
+ gboolean is_registered; /* if the tap listener is currently registered or not */
+} mcaststream_tapinfo_t;
+
+
+extern gint32 mcast_stream_trigger;
+extern gint32 mcast_stream_bufferalarm;
+extern guint16 mcast_stream_burstint;
+extern gint32 mcast_stream_emptyspeed;
+extern gint32 mcast_stream_cumulemptyspeed;
+
+/****************************************************************************/
+/* INTERFACE */
+
+/*
+* Registers the mcast_streams tap listener (if not already done).
+* From that point on, the Mcast streams list will be updated with every redissection.
+* This function is also the entry point for the initialization routine of the tap system.
+* So whenever mcast_stream.c is added to the list of WIRESHARK_TAP_SRCs, the tap will be registered on startup.
+* If not, it will be registered on demand by the mcast_streams and mcast_analysis functions that need it.
+*/
+void register_tap_listener_mcast_stream(void);
+
+/*
+* Removes the mcast_streams tap listener (if not already done)
+* From that point on, the Mcast streams list won't be updated any more.
+*/
+void remove_tap_listener_mcast_stream(void);
+
+/*
+* Retrieves a constant reference to the unique info structure of the mcast_streams tap listener.
+* The user should not modify the data pointed to.
+*/
+const mcaststream_tapinfo_t* mcaststream_get_info(void);
+
+/*
+* Cleans up memory of mcast streams tap.
+*/
+void mcaststream_reset(mcaststream_tapinfo_t *tapinfo);
+
+/*
+* Scans all packets for Mcast streams and updates the Mcast streams list.
+* (redissects all packets)
+*/
+void mcaststream_scan(void);
+
+#endif /* __MCAST_STREAM_H__ */
diff --git a/ui/gtk/mcast_stream_dlg.c b/ui/gtk/mcast_stream_dlg.c
new file mode 100644
index 0000000000..b746cdf1ee
--- /dev/null
+++ b/ui/gtk/mcast_stream_dlg.c
@@ -0,0 +1,801 @@
+/* mcast_stream_dlg.c
+ *
+ * Copyright 2006, Iskratel , Slovenia
+ * By Jakob Bratkovic <j.bratkovic@iskratel.si> and
+ * Miha Jemec <m.jemec@iskratel.si>
+ *
+ * $Id$
+ *
+ * based on rtp_stream_dlg.c
+ * Copyright 2003, Alcatel Business Systems
+ * By Lars Ruoff <lars.ruoff@gmx.net>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+#include <locale.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/filesystem.h"
+#include <epan/address.h>
+#include <epan/addr_resolv.h>
+#include <epan/strutil.h>
+
+#include "../globals.h"
+#include "../stat_menu.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/mcast_stream_dlg.h"
+#include "ui/gtk/mcast_stream.h"
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/gtkglobals.h"
+
+/* Capture callback data keys */
+#define E_MCAST_ENTRY_1 "burst_interval"
+#define E_MCAST_ENTRY_2 "burst_alarm"
+#define E_MCAST_ENTRY_3 "buffer_alarm"
+#define E_MCAST_ENTRY_4 "stream_speed"
+#define E_MCAST_ENTRY_5 "total_speed"
+
+static const gchar FWD_LABEL_TEXT[] = "Select a stream with left mouse button";
+static const gchar PAR_LABEL_TEXT[] = "\nBurst int: ms Burst alarm: pps Buffer alarm: KB Stream empty speed: Mbps Total empty speed: Mbps\n";
+
+/****************************************************************************/
+static GtkWidget *mcast_stream_dlg = NULL;
+static GtkWidget *mcast_params_dlg = NULL;
+
+static GtkListStore *list_store = NULL;
+static GtkTreeIter list_iter;
+static GtkWidget *list_w = NULL;
+static GtkWidget *top_label = NULL;
+static GtkWidget *label_fwd = NULL;
+static GtkWidget *label_par = NULL;
+static GtkWidget *bt_filter = NULL;
+
+static mcast_stream_info_t* selected_stream_fwd = NULL; /* current selection */
+static GList *last_list = NULL;
+
+static guint32 streams_nb = 0; /* number of displayed streams */
+
+enum
+{
+ MC_COL_SRC_ADDR,
+ MC_COL_SRC_PORT,
+ MC_COL_DST_ADDR,
+ MC_COL_DST_PORT,
+ MC_COL_PACKETS,
+ MC_COL_PPS,
+ MC_COL_AVG_BW,
+ MC_COL_MAX_BW,
+ MC_COL_MAX_BURST,
+ MC_COL_BURST_ALARM,
+ MC_COL_MAX_BUFFER,
+ MC_COL_BUFFER_ALARM,
+ MC_COL_DATA,
+ NUM_COLS /* The number of columns */
+};
+
+/****************************************************************************/
+/* CALLBACKS */
+/****************************************************************************/
+static void
+mcaststream_on_destroy(GObject *object _U_, gpointer user_data _U_)
+{
+ /* Remove the stream tap listener */
+ remove_tap_listener_mcast_stream();
+
+ /* Is there a params window open? */
+ if (mcast_params_dlg != NULL)
+ window_destroy(mcast_params_dlg);
+
+ /* Clean up memory used by stream tap */
+ mcaststream_reset((mcaststream_tapinfo_t*)mcaststream_get_info());
+
+ /* Note that we no longer have a "Mcast Streams" dialog box. */
+ mcast_stream_dlg = NULL;
+}
+
+
+/****************************************************************************/
+static void
+mcaststream_on_unselect(GtkButton *button _U_, gpointer user_data _U_)
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list_w));
+ gtk_tree_selection_unselect_all(selection);
+
+ selected_stream_fwd = NULL;
+ gtk_label_set_text(GTK_LABEL(label_fwd), FWD_LABEL_TEXT);
+ gtk_widget_set_sensitive(bt_filter, FALSE);
+}
+
+
+/****************************************************************************/
+static void
+mcaststream_on_filter(GtkButton *button _U_, gpointer user_data _U_)
+{
+ gchar *filter_string_fwd = NULL;
+ gchar ip_version[3];
+
+ if (selected_stream_fwd==NULL)
+ return;
+
+ if (selected_stream_fwd->src_addr.type==AT_IPv6){
+ g_strlcpy(ip_version,"v6",sizeof(ip_version));
+ } else {
+ ip_version[0] = '\0';
+ }
+ filter_string_fwd = g_strdup_printf(
+ "(ip%s.src==%s && udp.srcport==%u && ip%s.dst==%s && udp.dstport==%u)",
+ ip_version,
+ ep_address_to_str(&(selected_stream_fwd->src_addr)),
+ selected_stream_fwd->src_port,
+ ip_version,
+ ep_address_to_str(&(selected_stream_fwd->dest_addr)),
+ selected_stream_fwd->dest_port);
+
+ gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), filter_string_fwd);
+ g_free(filter_string_fwd);
+
+/*
+ main_filter_packets(&cfile, filter_string, FALSE);
+ mcaststream_dlg_update(mcaststream_get_info()->strinfo_list);
+*/
+}
+
+/****************************************************************************/
+/* when the user selects a row in the stream list */
+static void
+mcaststream_on_select_row(GtkTreeSelection *selection, gpointer data _U_)
+{
+ gchar label_text[80];
+
+ if (gtk_tree_selection_get_selected(selection, NULL, &list_iter))
+ {
+ gtk_tree_model_get(GTK_TREE_MODEL(list_store), &list_iter, MC_COL_DATA, &selected_stream_fwd, -1);
+ g_snprintf(label_text, sizeof(label_text), "Selected: %s:%u -> %s:%u",
+ get_addr_name(&(selected_stream_fwd->src_addr)),
+ selected_stream_fwd->src_port,
+ get_addr_name(&(selected_stream_fwd->dest_addr)),
+ selected_stream_fwd->dest_port
+ );
+ gtk_label_set_text(GTK_LABEL(label_fwd), label_text);
+ gtk_widget_set_sensitive(bt_filter, TRUE);
+ } else {
+ selected_stream_fwd = NULL;
+ gtk_label_set_text(GTK_LABEL(label_fwd), FWD_LABEL_TEXT);
+ gtk_widget_set_sensitive(bt_filter, FALSE);
+ }
+}
+
+
+/****************************************************************************/
+/* INTERFACE */
+/****************************************************************************/
+static void
+mcast_params_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+ /* Note that we no longer have a mcast params dialog box. */
+ mcast_params_dlg = NULL;
+}
+
+
+static void
+mcast_params_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w)
+{
+ GtkWidget *fnumber_te;
+ const gchar *fnumber_text;
+ gint32 fnumber;
+ char *p;
+
+ fnumber_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_MCAST_ENTRY_1);
+ fnumber_text = gtk_entry_get_text(GTK_ENTRY(fnumber_te));
+ fnumber = strtoul(fnumber_text, &p, 10);
+ if ( (p == fnumber_text || *p != '\0') || (fnumber <= 0) || (fnumber > 1000) ) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The burst interval should be between 1 and 1000 ms.");
+ return;
+ }
+ mcast_stream_burstint = fnumber;
+
+ fnumber_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_MCAST_ENTRY_2);
+ fnumber_text = gtk_entry_get_text(GTK_ENTRY(fnumber_te));
+ fnumber = strtoul(fnumber_text, &p, 10);
+ if ( (p == fnumber_text || *p != '\0') || (fnumber <= 0) ) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The burst alarm threshold you entered isn't valid.");
+ return;
+ }
+ mcast_stream_trigger = fnumber;
+
+ fnumber_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_MCAST_ENTRY_3);
+ fnumber_text = gtk_entry_get_text(GTK_ENTRY(fnumber_te));
+ fnumber = strtoul(fnumber_text, &p, 10);
+ if ( (p == fnumber_text || *p != '\0') || (fnumber <= 0) ) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The buffer alarm threshold you entered isn't valid.");
+ return;
+ }
+ mcast_stream_bufferalarm = fnumber;
+
+ fnumber_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_MCAST_ENTRY_4);
+ fnumber_text = gtk_entry_get_text(GTK_ENTRY(fnumber_te));
+ fnumber = strtoul(fnumber_text, &p, 10);
+ if ( (p == fnumber_text || *p != '\0') || (fnumber <= 0) || (fnumber > 10000000) ) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The stream empty speed should be between 1 and 10000000");
+ return;
+ }
+ mcast_stream_emptyspeed = fnumber;
+
+ fnumber_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_MCAST_ENTRY_5);
+ fnumber_text = gtk_entry_get_text(GTK_ENTRY(fnumber_te));
+ fnumber = strtoul(fnumber_text, &p, 10);
+ if ( (p == fnumber_text || *p != '\0') || (fnumber <= 0) || (fnumber > 10000000) ) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "The total empty speed should be between 1 and 10000000");
+ return;
+ }
+ mcast_stream_cumulemptyspeed = fnumber;
+
+ window_destroy(GTK_WIDGET(parent_w));
+
+ /* Clean up memory used by stream tap */
+ mcaststream_reset((mcaststream_tapinfo_t*)mcaststream_get_info());
+ /* retap all packets */
+ cf_retap_packets(&cfile);
+
+}
+
+
+static void
+mcast_on_params(GtkButton *button _U_, gpointer data _U_)
+{
+ GtkWidget *main_vb;
+ GtkWidget *label, *hbuttonbox, *table;
+ GtkWidget *ok_bt, *cancel_bt;
+ GtkWidget *entry1, *entry2, *entry3, *entry4, *entry5;
+ gchar label_text[51];
+
+ if (mcast_params_dlg != NULL) {
+ /* There's already a Params dialog box; reactivate it. */
+ reactivate_window(mcast_params_dlg);
+ return;
+ }
+
+ mcast_params_dlg = dlg_window_new("Wireshark: Set parameters for Multicast Stream Analysis");
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(mcast_params_dlg), TRUE);
+ gtk_window_set_default_size(GTK_WINDOW(mcast_params_dlg), 210, 210);
+
+ gtk_widget_show(mcast_params_dlg);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
+ gtk_container_add(GTK_CONTAINER(mcast_params_dlg), main_vb);
+ gtk_widget_show(main_vb);
+
+ table = gtk_table_new(6, 2, FALSE);
+ gtk_container_add (GTK_CONTAINER (main_vb), table);
+
+ label = gtk_label_new(" Burst measurement interval (ms) ");
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
+ entry1 = gtk_entry_new();
+ g_snprintf(label_text, sizeof(label_text), "%u", mcast_stream_burstint);
+ gtk_entry_set_text(GTK_ENTRY(entry1), label_text);
+ gtk_table_attach_defaults(GTK_TABLE(table), entry1, 1, 2, 0, 1);
+ label = gtk_label_new(" Burst alarm threshold (packets) ");
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
+ entry2 = gtk_entry_new();
+ g_snprintf(label_text, sizeof(label_text), "%u", mcast_stream_trigger);
+ gtk_entry_set_text(GTK_ENTRY(entry2), label_text);
+ gtk_table_attach_defaults(GTK_TABLE(table), entry2, 1, 2, 1, 2);
+ label = gtk_label_new(" Buffer alarm threshold (bytes) ");
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
+ entry3 = gtk_entry_new();
+ g_snprintf(label_text, sizeof(label_text), "%u", mcast_stream_bufferalarm);
+ gtk_entry_set_text(GTK_ENTRY(entry3), label_text);
+ gtk_table_attach_defaults(GTK_TABLE(table), entry3, 1, 2, 2, 3);
+ label = gtk_label_new(" Stream empty speed (kbit/s) ");
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
+ entry4 = gtk_entry_new();
+ g_snprintf(label_text, sizeof(label_text), "%u", mcast_stream_emptyspeed);
+ gtk_entry_set_text(GTK_ENTRY(entry4), label_text);
+ gtk_table_attach_defaults(GTK_TABLE(table), entry4, 1, 2, 3, 4);
+ label = gtk_label_new(" Total empty speed (kbit/s) ");
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 4, 5);
+ entry5 = gtk_entry_new();
+ g_snprintf(label_text, sizeof(label_text), "%u", mcast_stream_cumulemptyspeed);
+ gtk_entry_set_text(GTK_ENTRY(entry5), label_text);
+ gtk_table_attach_defaults(GTK_TABLE(table), entry5, 1, 2, 4, 5);
+
+ gtk_widget_show (table);
+
+ /* button row */
+ hbuttonbox = gtk_hbutton_box_new();
+ gtk_table_attach_defaults(GTK_TABLE(table), hbuttonbox, 0, 2, 5, 6);
+ ok_bt = gtk_button_new_from_stock(GTK_STOCK_OK);
+ gtk_container_add (GTK_CONTAINER(hbuttonbox), ok_bt);
+ cancel_bt = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+ gtk_container_add (GTK_CONTAINER(hbuttonbox), cancel_bt);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(cancel_bt, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
+#endif
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(hbuttonbox), GTK_BUTTONBOX_END);
+ gtk_box_set_spacing(GTK_BOX(hbuttonbox), 0);
+
+ g_signal_connect(mcast_params_dlg, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(mcast_params_dlg, "destroy", G_CALLBACK(mcast_params_destroy_cb), NULL);
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(mcast_params_ok_cb), mcast_params_dlg);
+ window_set_cancel_button(mcast_params_dlg, cancel_bt, window_cancel_button_cb);
+
+ /* Attach pointers to needed widgets */
+ g_object_set_data(G_OBJECT(mcast_params_dlg), E_MCAST_ENTRY_1, entry1);
+ g_object_set_data(G_OBJECT(mcast_params_dlg), E_MCAST_ENTRY_2, entry2);
+ g_object_set_data(G_OBJECT(mcast_params_dlg), E_MCAST_ENTRY_3, entry3);
+ g_object_set_data(G_OBJECT(mcast_params_dlg), E_MCAST_ENTRY_4, entry4);
+ g_object_set_data(G_OBJECT(mcast_params_dlg), E_MCAST_ENTRY_5, entry5);
+
+ gtk_widget_show_all(mcast_params_dlg);
+ window_present(mcast_params_dlg);
+}
+
+
+/****************************************************************************/
+/* append a line to list */
+static void
+add_to_list_store(mcast_stream_info_t* strinfo)
+{
+ gchar label_text[256];
+ gchar *data[NUM_COLS];
+ int i;
+ char *savelocale;
+
+ /* save the current locale */
+ savelocale = setlocale(LC_NUMERIC, NULL);
+ /* switch to "C" locale to avoid problems with localized decimal separators
+ in g_snprintf("%f") functions */
+ setlocale(LC_NUMERIC, "C");
+ data[0] = g_strdup(get_addr_name(&(strinfo->src_addr)));
+ data[1] = g_strdup_printf("%u", strinfo->src_port);
+ data[2] = g_strdup(get_addr_name(&(strinfo->dest_addr)));
+ data[3] = g_strdup_printf("%u", strinfo->dest_port);
+ data[4] = g_strdup_printf("%u", strinfo->npackets);
+ data[5] = g_strdup_printf("%u /s", strinfo->apackets);
+ data[6] = g_strdup_printf("%2.1f Mbps", strinfo->average_bw);
+ data[7] = g_strdup_printf("%2.1f Mbps", strinfo->element.maxbw);
+ data[8] = g_strdup_printf("%u / %dms", strinfo->element.topburstsize, mcast_stream_burstint);
+ data[9] = g_strdup_printf("%u", strinfo->element.numbursts);
+ data[10] = g_strdup_printf("%.1f KB", (float)strinfo->element.topbuffusage/1000);
+ data[11] = g_strdup_printf("%u", strinfo->element.numbuffalarms);
+
+ /* restore previous locale setting */
+ setlocale(LC_NUMERIC, savelocale);
+
+ /* Acquire an iterator */
+ gtk_list_store_append(list_store, &list_iter);
+
+ /* Fill the new row */
+ gtk_list_store_set(list_store, &list_iter,
+ MC_COL_SRC_ADDR, data[0],
+ MC_COL_SRC_PORT, data[1],
+ MC_COL_DST_ADDR, data[2],
+ MC_COL_DST_PORT, data[3],
+ MC_COL_PACKETS, data[4],
+ MC_COL_PPS, data[5],
+ MC_COL_AVG_BW, data[6],
+ MC_COL_MAX_BW, data[7],
+ MC_COL_MAX_BURST, data[8],
+ MC_COL_BURST_ALARM, data[9],
+ MC_COL_MAX_BUFFER, data[10],
+ MC_COL_BUFFER_ALARM, data[11],
+ MC_COL_DATA, strinfo,
+ -1);
+
+ for (i = 0; i < NUM_COLS-1; i++)
+ g_free(data[i]);
+
+ /* Update the top label with the number of detected streams */
+ g_snprintf(label_text, sizeof(label_text),
+ "Detected %d Multicast streams, Average Bw: %.1f Mbps Max Bw: %.1f Mbps Max burst: %d / %dms Max buffer: %.1f KB",
+ ++streams_nb,
+ mcaststream_get_info()->allstreams->average_bw, mcaststream_get_info()->allstreams->element.maxbw,
+ mcaststream_get_info()->allstreams->element.topburstsize, mcast_stream_burstint,
+ (float)(mcaststream_get_info()->allstreams->element.topbuffusage)/1000);
+ gtk_label_set_text(GTK_LABEL(top_label), label_text);
+
+ g_snprintf(label_text, sizeof(label_text), "\nBurst int: %u ms Burst alarm: %u pps Buffer alarm: %u Bytes Stream empty speed: %u Kbps Total empty speed: %u Kbps\n",
+ mcast_stream_burstint, mcast_stream_trigger, mcast_stream_bufferalarm, mcast_stream_emptyspeed, mcast_stream_cumulemptyspeed);
+ gtk_label_set_text(GTK_LABEL(label_par), label_text);
+}
+
+/****************************************************************************/
+/* Create list view */
+static void
+create_list_view(void)
+{
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSortable *sortable;
+ GtkTreeView *list_view;
+ GtkTreeSelection *selection;
+
+ /* Create the store */
+ list_store = gtk_list_store_new(NUM_COLS, /* Total number of columns */
+ G_TYPE_STRING, /* Source address */
+ G_TYPE_STRING, /* Source port */
+ G_TYPE_STRING, /* Destination address */
+ G_TYPE_STRING, /* Destination port */
+ G_TYPE_STRING, /* Packets */
+ G_TYPE_STRING, /* Packets per second */
+ G_TYPE_STRING, /* Average bandwidth */
+ G_TYPE_STRING, /* Max. bandwidth */
+ G_TYPE_STRING, /* Max. burst */
+ G_TYPE_STRING, /* Burst alarms */
+ G_TYPE_STRING, /* Max. buffers */
+ G_TYPE_STRING, /* Buffer alarms */
+ G_TYPE_POINTER /* Data */
+ );
+
+ /* Create a view */
+ list_w = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store));
+
+ list_view = GTK_TREE_VIEW(list_w);
+ sortable = GTK_TREE_SORTABLE(list_store);
+
+ /* Speed up the list display */
+ gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
+
+ /* Setup the sortable columns */
+ gtk_tree_sortable_set_sort_column_id(sortable, MC_COL_SRC_ADDR, GTK_SORT_ASCENDING);
+ gtk_tree_view_set_headers_clickable(list_view, FALSE);
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref(G_OBJECT(list_store));
+
+ /*
+ * Create the first column packet, associating the "text" attribute of the
+ * cell_renderer to the first column of the model
+ */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Src IP addr", renderer,
+ "text", MC_COL_SRC_ADDR,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, MC_COL_SRC_ADDR);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+ gtk_tree_view_column_set_fixed_width(column, 100);
+ /* Add the column to the view. */
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Source port */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Src port", renderer,
+ "text", MC_COL_SRC_PORT,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, MC_COL_SRC_PORT);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+ gtk_tree_view_column_set_fixed_width(column, 80);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Destination address */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Dst IP addr", renderer,
+ "text", MC_COL_DST_ADDR,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, MC_COL_DST_ADDR);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+ gtk_tree_view_column_set_fixed_width(column, 100);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Destination port */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Dst port", renderer,
+ "text", MC_COL_DST_PORT,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, MC_COL_DST_PORT);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+ gtk_tree_view_column_set_fixed_width(column, 80);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Packets */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Packets", renderer,
+ "text", MC_COL_PACKETS,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, MC_COL_PACKETS);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+ gtk_tree_view_column_set_fixed_width(column, 80);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Packets/s */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Packets/s", renderer,
+ "text", MC_COL_PPS,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, MC_COL_PPS);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+ gtk_tree_view_column_set_fixed_width(column, 90);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Average bandwidth */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Avg Bw", renderer,
+ "text", MC_COL_AVG_BW,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, MC_COL_AVG_BW);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 70);
+ gtk_tree_view_column_set_fixed_width(column, 80);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Max. bandwidth */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Max Bw", renderer,
+ "text", MC_COL_MAX_BW,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, MC_COL_MAX_BW);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 70);
+ gtk_tree_view_column_set_fixed_width(column, 80);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Max. bursts */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Max bursts", renderer,
+ "text", MC_COL_MAX_BURST,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, MC_COL_MAX_BURST);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 70);
+ gtk_tree_view_column_set_fixed_width(column, 100);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Burst alarms*/
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Burst alarms", renderer,
+ "text", MC_COL_BURST_ALARM,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, MC_COL_BURST_ALARM);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 90);
+ gtk_tree_view_column_set_fixed_width(column, 110);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Max. buffers */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Max buffers", renderer,
+ "text", MC_COL_MAX_BUFFER,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, MC_COL_MAX_BUFFER);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 90);
+ gtk_tree_view_column_set_fixed_width(column, 100);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Buffer alarms */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Buffer alarms", renderer,
+ "text", MC_COL_BUFFER_ALARM,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, MC_COL_BUFFER_ALARM);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 90);
+ gtk_tree_view_column_set_fixed_width(column, 120);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Now enable the sorting of each column */
+ gtk_tree_view_set_rules_hint(list_view, TRUE);
+ gtk_tree_view_set_headers_clickable(list_view, TRUE);
+
+ /* Setup the selection handler */
+ selection = gtk_tree_view_get_selection(list_view);
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+
+ g_signal_connect(G_OBJECT(selection), "changed", /* (un)select_row */
+ G_CALLBACK(mcaststream_on_select_row),
+ NULL);
+
+}
+
+
+/****************************************************************************/
+/* Create dialog */
+static void
+mcaststream_dlg_create(void)
+{
+ GtkWidget *mcaststream_dlg_w;
+ GtkWidget *main_vb;
+ GtkWidget *scrolledwindow;
+ GtkWidget *hbuttonbox;
+ /*GtkWidget *bt_unselect;*/
+ GtkWidget *bt_params;
+ GtkWidget *bt_close;
+
+ const gchar *title_name_ptr;
+ gchar *win_name;
+
+ title_name_ptr = cf_get_display_name(&cfile);
+ win_name = g_strdup_printf("%s - UDP Multicast Streams", title_name_ptr);
+ mcaststream_dlg_w = dlg_window_new(win_name);
+
+ gtk_window_set_default_size(GTK_WINDOW(mcaststream_dlg_w), 620, 400);
+
+ main_vb = gtk_vbox_new (FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(mcaststream_dlg_w), main_vb);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vb), 12);
+
+ top_label = gtk_label_new ("Detected 0 Multicast streams");
+ gtk_box_pack_start (GTK_BOX (main_vb), top_label, FALSE, FALSE, 8);
+
+ scrolledwindow = scrolled_window_new (NULL, NULL);
+ gtk_box_pack_start (GTK_BOX (main_vb), scrolledwindow, TRUE, TRUE, 0);
+
+ create_list_view();
+ gtk_container_add(GTK_CONTAINER(scrolledwindow), list_w);
+
+ gtk_widget_show(mcaststream_dlg_w);
+
+ label_fwd = gtk_label_new (FWD_LABEL_TEXT);
+ gtk_box_pack_start (GTK_BOX (main_vb), label_fwd, FALSE, FALSE, 0);
+
+ label_par = gtk_label_new (PAR_LABEL_TEXT);
+ gtk_box_pack_start (GTK_BOX (main_vb), label_par, FALSE, FALSE, 0);
+
+ /* button row */
+ hbuttonbox = gtk_hbutton_box_new ();
+ gtk_box_pack_start (GTK_BOX (main_vb), hbuttonbox, FALSE, FALSE, 0);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_END);
+ gtk_box_set_spacing (GTK_BOX (hbuttonbox), 0);
+
+ /*bt_unselect = gtk_button_new_with_label ("Unselect");
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_unselect);
+ gtk_widget_set_tooltip_text (bt_unselect, "Undo stream selection");*/
+
+ bt_params = gtk_button_new_with_label ("Set parameters");
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_params);
+ gtk_widget_set_tooltip_text (bt_params, "Set buffer, limit and speed parameters");
+
+ bt_filter = gtk_button_new_with_label ("Prepare Filter");
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_filter);
+ gtk_widget_set_tooltip_text (bt_filter, "Prepare a display filter of the selected stream");
+
+ bt_close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_close);
+ gtk_widget_set_tooltip_text (bt_close, "Close this dialog");
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(bt_close, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(bt_close, GTK_CAN_DEFAULT);
+#endif
+
+ /*g_signal_connect(bt_unselect, "clicked", G_CALLBACK(mcaststream_on_unselect), NULL);*/
+ g_signal_connect(bt_params, "clicked", G_CALLBACK(mcast_on_params), NULL);
+ g_signal_connect(bt_filter, "clicked", G_CALLBACK(mcaststream_on_filter), NULL);
+ window_set_cancel_button(mcaststream_dlg_w, bt_close, window_cancel_button_cb);
+
+ g_signal_connect(mcaststream_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(mcaststream_dlg_w, "destroy", G_CALLBACK(mcaststream_on_destroy), NULL);
+
+ gtk_widget_show_all(mcaststream_dlg_w);
+ window_present(mcaststream_dlg_w);
+
+ mcaststream_on_unselect(NULL, NULL);
+
+ mcast_stream_dlg = mcaststream_dlg_w;
+
+ g_free(win_name);
+
+}
+
+
+/****************************************************************************/
+/* PUBLIC */
+/****************************************************************************/
+
+/****************************************************************************/
+/* update the contents of the dialog box clist */
+/* list: pointer to list of mcast_stream_info_t* */
+void
+mcaststream_dlg_update(GList *list)
+{
+ if (mcast_stream_dlg != NULL) {
+ gtk_list_store_clear(list_store);
+ streams_nb = 0;
+
+ list = g_list_first(list);
+ while (list)
+ {
+ add_to_list_store((mcast_stream_info_t*)(list->data));
+ list = g_list_next(list);
+ }
+
+ mcaststream_on_unselect(NULL, NULL);
+ }
+
+ last_list = list;
+}
+
+
+/****************************************************************************/
+/* update the contents of the dialog box clist */
+/* list: pointer to list of mcast_stream_info_t* */
+void
+mcaststream_dlg_show(GList *list)
+{
+ if (mcast_stream_dlg != NULL) {
+ /* There's already a dialog box; reactivate it. */
+ reactivate_window(mcast_stream_dlg);
+ /* Another list since last call? */
+ if (list != last_list) {
+ mcaststream_dlg_update(list);
+ }
+ }
+ else {
+ /* Create and show the dialog box */
+ mcaststream_dlg_create();
+ mcaststream_dlg_update(list);
+ }
+}
+
+
+/****************************************************************************/
+/* entry point when called via the GTK menu */
+void
+mcaststream_launch(GtkAction *action _U_, gpointer user_data _U_)
+{
+ /* Register the tap listener */
+ register_tap_listener_mcast_stream();
+
+ /* Scan for Mcast streams (redissect all packets) */
+ mcaststream_scan();
+
+ /* Show the dialog box with the list of streams */
+ mcaststream_dlg_show(mcaststream_get_info()->strinfo_list);
+
+ /* Tap listener will be removed and cleaned up in mcaststream_on_destroy */
+}
+
+/****************************************************************************/
+void
+register_tap_listener_mcast_stream_dlg(void)
+{
+}
+
diff --git a/ui/gtk/mcast_stream_dlg.h b/ui/gtk/mcast_stream_dlg.h
new file mode 100644
index 0000000000..fca233f108
--- /dev/null
+++ b/ui/gtk/mcast_stream_dlg.h
@@ -0,0 +1,54 @@
+/* mcast_stream_dlg.h
+ *
+ * Copyright 2006, Iskratel , Slovenia
+ * By Jakob Bratkovic <j.bratkovic@iskratel.si> and
+ * Miha Jemec <m.jemec@iskratel.si>
+ *
+ * $Id$
+ *
+ * based on rtp_stream_dlg.h
+ * Copyright 2003, Alcatel Business Systems
+ * By Lars Ruoff <lars.ruoff@gmx.net>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MCAST_STREAM_DLG_H__
+#define __MCAST_STREAM_DLG_H__
+
+/** @file
+ * @ingroup dialog_group
+ * "Mcast Stream Analysis" dialog box.
+ */
+
+/**
+ * Create or reactivate the mcast streams dialog box.
+ *
+ * @param list pointer to list of mcast_stream_info_t*
+ */
+void mcaststream_dlg_show(GList *list);
+
+/**
+ * Update the contents of the dialog box clist with that of list.
+ *
+ * @param list pointer to list of mcast_stream_info_t*
+ */
+void mcaststream_dlg_update(GList *list);
+
+#endif /* __MCAST_STREAM_DLG_H__ */
diff --git a/ui/gtk/megaco_stat.c b/ui/gtk/megaco_stat.c
new file mode 100644
index 0000000000..b05159b7d1
--- /dev/null
+++ b/ui/gtk/megaco_stat.c
@@ -0,0 +1,243 @@
+/* megaco_stat.c
+ * megaco-statistics for Wireshark
+ * Copyright 2003 Lars Roland
+ * Copyright 2008, Ericsson AB
+ * By Balint Reczey <balint.reczey@ericsson.com>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/value_string.h>
+#include <epan/tap.h>
+#include "epan/gcp.h"
+#include <epan/prefs-int.h>
+
+#include "../timestats.h"
+#include "../simple_dialog.h"
+#include "../file.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_util.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/main.h"
+
+#include "tap-megaco-common.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+static void
+megacostat_reset(void *pms)
+{
+ megacostat_t *ms=(megacostat_t *)pms;
+ int i;
+
+ for(i=0;i<NUM_TIMESTATS;i++) {
+ ms->rtd[i].num=0;
+ ms->rtd[i].min_num=0;
+ ms->rtd[i].max_num=0;
+ ms->rtd[i].min.secs=0;
+ ms->rtd[i].min.nsecs=0;
+ ms->rtd[i].max.secs=0;
+ ms->rtd[i].max.nsecs=0;
+ ms->rtd[i].tot.secs=0;
+ ms->rtd[i].tot.nsecs=0;
+ }
+
+ ms->open_req_num=0;
+ ms->disc_rsp_num=0;
+ ms->req_dup_num=0;
+ ms->rsp_dup_num=0;
+}
+
+static void
+megacostat_draw(void *pms)
+{
+ megacostat_t *ms=(megacostat_t *)pms;
+ int i;
+ char str[3][256];
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ /* clear list before printing */
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(ms->table));
+ gtk_list_store_clear(store);
+
+ for(i=0;i<NUM_TIMESTATS;i++) {
+ /* nothing seen, nothing to do */
+ if(ms->rtd[i].num==0){
+ continue;
+ }
+
+ g_snprintf(str[0], sizeof(char[256]), "%8.2f msec", nstime_to_msec(&(ms->rtd[i].min)));
+ g_snprintf(str[1], sizeof(char[256]), "%8.2f msec", nstime_to_msec(&(ms->rtd[i].max)));
+ g_snprintf(str[2], sizeof(char[256]), "%8.2f msec", get_average(&(ms->rtd[i].tot), ms->rtd[i].num));
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, val_to_str(i,megaco_message_type,"Other"),
+ 1, ms->rtd[i].num,
+ 2, str[0],
+ 3, str[1],
+ 4, str[2],
+ 5, ms->rtd[i].min_num,
+ 6, ms->rtd[i].max_num,
+ -1);
+ }
+}
+
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ megacostat_t *ms=(megacostat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(ms);
+ unprotect_thread_critical_region();
+
+ if(ms->filter){
+ g_free(ms->filter);
+ ms->filter=NULL;
+ }
+ g_free(ms);
+}
+
+static const stat_column titles[]={
+ {G_TYPE_STRING, LEFT, "Type" },
+ {G_TYPE_UINT, RIGHT, "Messages" },
+ {G_TYPE_STRING, RIGHT, "Min SRT" },
+ {G_TYPE_STRING, RIGHT, "Max SRT" },
+ {G_TYPE_STRING, RIGHT, "Avg SRT" },
+ {G_TYPE_UINT, RIGHT, "Min in Frame" },
+ {G_TYPE_UINT, RIGHT, "Max in Frame" }
+};
+
+static void
+gtk_megacostat_init(const char *optarg, void *userdata _U_)
+{
+ megacostat_t *ms;
+ GString *error_string;
+ GtkWidget *bt_close;
+ GtkWidget *bbox;
+ pref_t *megaco_ctx_track,*h248_ctx_track;
+
+ megaco_ctx_track = prefs_find_preference(prefs_find_module("megaco"),"ctx_info");
+ h248_ctx_track = prefs_find_preference(prefs_find_module("h248"),"ctx_info");
+
+ if (!megaco_ctx_track || !h248_ctx_track) {
+ /* No such preferences */
+ return;
+ }
+
+ if (!*megaco_ctx_track->varp.boolp || !*h248_ctx_track->varp.boolp) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", "Track Context option at Protocols -> MEGACO and Protocols -> H248 preferences has to be set to true to enable measurement of service reponse times.");
+ return;
+ }
+
+ ms=g_malloc(sizeof(megacostat_t));
+
+ if(strncmp(optarg,"megaco,srt,",11) == 0){
+ ms->filter=g_strdup(optarg+11);
+ } else {
+ ms->filter=NULL;
+ }
+
+ megacostat_reset(ms);
+
+ ms->win = dlg_window_new("MEGACO SRT"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(ms->win), TRUE);
+
+ gtk_window_set_default_size(GTK_WINDOW(ms->win), 550, 150);
+
+ ms->vbox=gtk_vbox_new(FALSE, 3);
+
+ init_main_stat_window(ms->win, ms->vbox, "MEGACO Service Response Time (SRT) Statistics", ms->filter);
+
+ /* init a scrolled window*/
+ ms->scrolled_window = scrolled_window_new(NULL, NULL);
+
+ ms->table = create_stat_table(ms->scrolled_window, ms->vbox, 7, titles);
+
+ error_string=register_tap_listener("megaco", ms, ms->filter, 0, megacostat_reset, megacostat_packet, megacostat_draw);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(ms->filter);
+ g_free(ms);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_start(GTK_BOX(ms->vbox), bbox, FALSE, FALSE, 0);
+
+ bt_close = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(ms->win, bt_close, window_cancel_button_cb);
+
+ g_signal_connect(ms->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(ms->win, "destroy", G_CALLBACK(win_destroy_cb), ms);
+
+ gtk_widget_show_all(ms->win);
+ window_present(ms->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(ms->win));
+}
+
+static tap_param megaco_srt_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg megaco_srt_dlg = {
+ "MEGACO Service Response Time (SRT) Statistics",
+ "megaco,srt",
+ gtk_megacostat_init,
+ -1,
+ G_N_ELEMENTS(megaco_srt_params),
+ megaco_srt_params
+};
+
+void
+register_tap_listener_gtkmegacostat(void)
+{
+ register_dfilter_stat(&megaco_srt_dlg, "MEGACO",
+ REGISTER_STAT_GROUP_RESPONSE_TIME);
+}
+
+void megaco_srt_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &megaco_srt_dlg);
+}
+
diff --git a/ui/gtk/menus.h b/ui/gtk/menus.h
new file mode 100644
index 0000000000..845cf69d7b
--- /dev/null
+++ b/ui/gtk/menus.h
@@ -0,0 +1,161 @@
+/* menus.h
+ * Menu definitions
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MENUS_H__
+#define __MENUS_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Add a new recent capture filename to the "Recent Files" submenu
+ (duplicates will be ignored) */
+extern void add_menu_recent_capture_file(gchar *cf_name);
+
+/* Open a file(name)
+ (will not ask to close existing capture file!) */
+extern void menu_open_filename(gchar *cf_name);
+
+/** @file
+ * Menubar and context menus.
+ * @ingroup main_window_group
+ */
+
+/** Write all recent capture filenames to the user's recent file.
+ * @param rf recent file
+ */
+extern void menu_recent_file_write_all(FILE *rf);
+
+/** User pushed a recent file submenu item.
+ *
+ * @param widget parent widget
+ */
+extern void menu_open_recent_file_cmd(gpointer action);
+
+/** The recent file read has finished, update the menu corresponding. */
+extern void menu_recent_read_finished(void);
+
+/** One of the name resolution menu items changed. */
+extern void menu_name_resolution_changed(void);
+
+/** The "Colorize Packet List" option changed. */
+extern void menu_colorize_changed(gboolean packet_list_colorize);
+
+/* Reset preferences menu on profile or preference change. */
+extern void menu_prefs_reset(void);
+
+extern void rebuild_visible_columns_menu (void);
+
+#ifdef HAVE_LIBPCAP
+/** The "Auto Scroll Packet List in Live Capture" option changed. */
+extern void menu_auto_scroll_live_changed(gboolean auto_scroll_in);
+#endif
+
+/** Create a new menu.
+ *
+ * @param accel the created accelerator group
+ * @return the new menu
+ */
+extern GtkWidget *main_menu_new(GtkAccelGroup **accel);
+
+/** Set object data of menu, like g_object_set_data().
+ *
+ * @param path the path of the menu item
+ * @param key the key to set
+ * @param data the data to set
+ */
+extern void set_menu_object_data(const gchar *path, const gchar *key, gpointer data);
+
+/** The popup menu handler.
+ *
+ * @param widget the parent widget
+ * @param event the GdkEvent
+ * @param data the corresponding menu
+ */
+extern gboolean popup_menu_handler(GtkWidget *widget, GdkEvent *event, gpointer data);
+
+/** The packet history has changed, we need to update the menu.
+ *
+ * @param back_history some back history entries available
+ * @param forward_history some forward history entries available
+ */
+extern void set_menus_for_packet_history(gboolean back_history, gboolean forward_history);
+
+/** The current file has changed, we need to update the file set menu items.
+ *
+ * @param file_set the current file is part of a file set
+ * @param previous_file the previous file set (or NULL)
+ * @param next_file the next file set (or NULL)
+ */
+extern void set_menus_for_file_set(gboolean file_set, gboolean previous_file, gboolean next_file);
+
+/** The popup menu. */
+extern GtkWidget *popup_menu_object;
+
+/* Update the packet list heading menu to indicate default
+ column justification. */
+void menus_set_column_align_default (gboolean right_justify);
+
+/* Update the packet list heading menu to indicate if column can be resolved. */
+void menus_set_column_resolved (gboolean resolved, gboolean can_resolve);
+
+/* Fetch the statusbar profiles edit submenu */
+extern GtkWidget *menus_get_profiles_edit_menu (void);
+
+/* Fetch the statusbar profiles delete submenu */
+extern GtkWidget *menus_get_profiles_delete_menu (void);
+
+/* Fetch the statusbar profiles change submenu */
+extern GtkWidget *menus_get_profiles_change_menu (void);
+
+/* Enable or disable menu items based on whether a tree row is selected
+ and and on whether a "Match Selected" can be done. */
+void set_menus_for_selected_tree_row(capture_file *cf);
+
+
+/* Enable or disable menu items based on whether you have a capture file
+ you've finished reading and, if you have one, whether it's been saved
+ and whether it could be saved except by copying the raw packet data. */
+void set_menus_for_capture_file(capture_file *);
+
+
+/* Enable or disable menu items based on whether there's a capture in
+ progress. */
+void set_menus_for_capture_in_progress(gboolean);
+
+/* Enable or disable menu items based on whether you have some captured
+ packets. */
+void set_menus_for_captured_packets(gboolean);
+
+/* Enable or disable menu items based on whether a packet is selected. */
+void set_menus_for_selected_packet(capture_file *cf);
+
+/* Enable or disable menu items based on configuration profile */
+void set_menus_for_profiles(gboolean default_profile);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __MENUS_H__ */
diff --git a/ui/gtk/mgcp_stat.c b/ui/gtk/mgcp_stat.c
new file mode 100644
index 0000000000..aa1834af06
--- /dev/null
+++ b/ui/gtk/mgcp_stat.c
@@ -0,0 +1,329 @@
+/* mgcp_stat.c
+ * mgcp-statistics for Wireshark
+ * Copyright 2003 Lars Roland
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/value_string.h>
+#include <epan/tap.h>
+#include "epan/dissectors/packet-mgcp.h"
+
+#include "../timestats.h"
+#include "../simple_dialog.h"
+#include "../file.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_util.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/main.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+#define NUM_TIMESTATS 10
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _mgcpstat_t {
+ GtkWidget *win;
+ GtkWidget *vbox;
+ char *filter;
+ GtkWidget *scrolled_window;
+ GtkTreeView *table;
+ timestat_t rtd[NUM_TIMESTATS];
+ guint32 open_req_num;
+ guint32 disc_rsp_num;
+ guint32 req_dup_num;
+ guint32 rsp_dup_num;
+} mgcpstat_t;
+
+static const value_string mgcp_mesage_type[] = {
+ { 0, "EPCF"},
+ { 1, "CRCX"},
+ { 2, "MDCX"},
+ { 3, "DLCX"},
+ { 4, "RQNT"},
+ { 5, "NTFY"},
+ { 6, "AUEP"},
+ { 7, "AUCX"},
+ { 8, "RSIP"},
+ { 0, NULL}
+};
+
+static void
+mgcpstat_reset(void *pms)
+{
+ mgcpstat_t *ms=(mgcpstat_t *)pms;
+ int i;
+
+ for(i=0;i<NUM_TIMESTATS;i++) {
+ ms->rtd[i].num=0;
+ ms->rtd[i].min_num=0;
+ ms->rtd[i].max_num=0;
+ ms->rtd[i].min.secs=0;
+ ms->rtd[i].min.nsecs=0;
+ ms->rtd[i].max.secs=0;
+ ms->rtd[i].max.nsecs=0;
+ ms->rtd[i].tot.secs=0;
+ ms->rtd[i].tot.nsecs=0;
+ }
+
+ ms->open_req_num=0;
+ ms->disc_rsp_num=0;
+ ms->req_dup_num=0;
+ ms->rsp_dup_num=0;
+}
+
+
+static int
+mgcpstat_packet(void *pms, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pmi)
+{
+ mgcpstat_t *ms=(mgcpstat_t *)pms;
+ const mgcp_info_t *mi=pmi;
+ nstime_t delta;
+ int ret = 0;
+
+ switch (mi->mgcp_type) {
+
+ case MGCP_REQUEST:
+ if(mi->is_duplicate){
+ /* Duplicate is ignored */
+ ms->req_dup_num++;
+ }
+ else {
+ ms->open_req_num++;
+ }
+ break;
+
+ case MGCP_RESPONSE:
+ if(mi->is_duplicate){
+ /* Duplicate is ignored */
+ ms->rsp_dup_num++;
+ }
+ else if (!mi->request_available) {
+ /* no request was seen */
+ ms->disc_rsp_num++;
+ }
+ else {
+ ms->open_req_num--;
+ /* calculate time delta between request and response */
+ nstime_delta(&delta, &pinfo->fd->abs_ts, &mi->req_time);
+
+ if (g_ascii_strncasecmp(mi->code, "EPCF", 4) == 0 ) {
+ time_stat_update(&(ms->rtd[0]),&delta, pinfo);
+ }
+ else if (g_ascii_strncasecmp(mi->code, "CRCX", 4) == 0 ) {
+ time_stat_update(&(ms->rtd[1]),&delta, pinfo);
+ }
+ else if (g_ascii_strncasecmp(mi->code, "MDCX", 4) == 0 ) {
+ time_stat_update(&(ms->rtd[2]),&delta, pinfo);
+ }
+ else if (g_ascii_strncasecmp(mi->code, "DLCX", 4) == 0 ) {
+ time_stat_update(&(ms->rtd[3]),&delta, pinfo);
+ }
+ else if (g_ascii_strncasecmp(mi->code, "RQNT", 4) == 0 ) {
+ time_stat_update(&(ms->rtd[4]),&delta, pinfo);
+ }
+ else if (g_ascii_strncasecmp(mi->code, "NTFY", 4) == 0 ) {
+ time_stat_update(&(ms->rtd[5]),&delta, pinfo);
+ }
+ else if (g_ascii_strncasecmp(mi->code, "AUEP", 4) == 0 ) {
+ time_stat_update(&(ms->rtd[6]),&delta, pinfo);
+ }
+ else if (g_ascii_strncasecmp(mi->code, "AUCX", 4) == 0 ) {
+ time_stat_update(&(ms->rtd[7]),&delta, pinfo);
+ }
+ else if (g_ascii_strncasecmp(mi->code, "RSIP", 4) == 0 ) {
+ time_stat_update(&(ms->rtd[8]),&delta, pinfo);
+ }
+ else {
+ time_stat_update(&(ms->rtd[9]),&delta, pinfo);
+ }
+
+ ret = 1;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static void
+mgcpstat_draw(void *pms)
+{
+ mgcpstat_t *ms=(mgcpstat_t *)pms;
+ int i;
+ char str[3][256];
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ /* clear list before printing */
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(ms->table));
+ gtk_list_store_clear(store);
+
+ for(i=0;i<NUM_TIMESTATS;i++) {
+ /* nothing seen, nothing to do */
+ if(ms->rtd[i].num==0){
+ continue;
+ }
+
+ g_snprintf(str[0], sizeof(char[256]), "%8.2f msec", nstime_to_msec(&(ms->rtd[i].min)));
+ g_snprintf(str[1], sizeof(char[256]), "%8.2f msec", nstime_to_msec(&(ms->rtd[i].max)));
+ g_snprintf(str[2], sizeof(char[256]), "%8.2f msec", get_average(&(ms->rtd[i].tot), ms->rtd[i].num));
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, val_to_str(i, mgcp_mesage_type,"Other"),
+ 1, ms->rtd[i].num,
+ 2, str[0],
+ 3, str[1],
+ 4, str[2],
+ 5, ms->rtd[i].min_num,
+ 6, ms->rtd[i].max_num,
+ -1);
+ }
+}
+
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ mgcpstat_t *ms=(mgcpstat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(ms);
+ unprotect_thread_critical_region();
+
+ if(ms->filter){
+ g_free(ms->filter);
+ ms->filter=NULL;
+ }
+ g_free(ms);
+}
+
+static const stat_column titles[]={
+ {G_TYPE_STRING, LEFT, "Type" },
+ {G_TYPE_UINT, RIGHT, "Messages" },
+ {G_TYPE_STRING, RIGHT, "Min SRT" },
+ {G_TYPE_STRING, RIGHT, "Max SRT" },
+ {G_TYPE_STRING, RIGHT, "Avg SRT" },
+ {G_TYPE_UINT, RIGHT, "Min in Frame" },
+ {G_TYPE_UINT, RIGHT, "Max in Frame" }
+};
+
+static void
+gtk_mgcpstat_init(const char *optarg, void *userdata _U_)
+{
+ mgcpstat_t *ms;
+ GString *error_string;
+ GtkWidget *bt_close;
+ GtkWidget *bbox;
+
+ ms=g_malloc(sizeof(mgcpstat_t));
+
+ if(strncmp(optarg,"mgcp,srt,",9) == 0){
+ ms->filter=g_strdup(optarg+9);
+ } else {
+ ms->filter=NULL;
+ }
+
+ mgcpstat_reset(ms);
+
+ ms->win = dlg_window_new("MGCP SRT"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(ms->win), TRUE);
+
+ gtk_window_set_default_size(GTK_WINDOW(ms->win), 550, 150);
+
+ ms->vbox=gtk_vbox_new(FALSE, 3);
+
+ init_main_stat_window(ms->win, ms->vbox, "MGCP Service Response Time (SRT) Statistics", ms->filter);
+
+ /* init a scrolled window*/
+ ms->scrolled_window = scrolled_window_new(NULL, NULL);
+
+ ms->table = create_stat_table(ms->scrolled_window, ms->vbox, 7, titles);
+
+ error_string=register_tap_listener("mgcp", ms, ms->filter, 0, mgcpstat_reset, mgcpstat_packet, mgcpstat_draw);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(ms->filter);
+ g_free(ms);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_start(GTK_BOX(ms->vbox), bbox, FALSE, FALSE, 0);
+
+ bt_close = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(ms->win, bt_close, window_cancel_button_cb);
+
+ g_signal_connect(ms->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(ms->win, "destroy", G_CALLBACK(win_destroy_cb), ms);
+
+ gtk_widget_show_all(ms->win);
+ window_present(ms->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(ms->win));
+}
+
+static tap_param mgcp_srt_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg mgcp_srt_dlg = {
+ "MGCP Service Response Time (SRT) Statistics",
+ "mgcp,srt",
+ gtk_mgcpstat_init,
+ -1,
+ G_N_ELEMENTS(mgcp_srt_params),
+ mgcp_srt_params
+};
+
+void
+register_tap_listener_gtkmgcpstat(void)
+{
+ register_dfilter_stat(&mgcp_srt_dlg, "MGCP",
+ REGISTER_STAT_GROUP_RESPONSE_TIME);
+}
+void mgcp_srt_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &mgcp_srt_dlg);
+}
+
diff --git a/ui/gtk/mtp3_stat.c b/ui/gtk/mtp3_stat.c
new file mode 100644
index 0000000000..ddd1a8aa2e
--- /dev/null
+++ b/ui/gtk/mtp3_stat.c
@@ -0,0 +1,445 @@
+/* mtp3_stat.c
+ *
+ * Copyright 2004, Michael Lum <mlum [AT] telostech.com>
+ * In association with Telos Technology Inc.
+ *
+ * Modified from gsm_map_stat.c
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * This TAP provides statistics for MTP3:
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/packet_info.h"
+#include "epan/epan.h"
+#include "epan/value_string.h"
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/emem.h>
+#include <epan/dissectors/packet-mtp3.h>
+
+#include "../stat_menu.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/mtp3_stat.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+enum
+{
+ OPC_COLUMN,
+ DPC_COLUMN,
+ SI_COLUMN,
+ NUM_MSUS_COLUMN,
+ NUM_BYTES_COLUMN,
+ AVG_BYTES_COLUMN,
+ N_COLUMN /* The number of columns */
+};
+
+
+typedef struct _mtp3_stat_dlg_t {
+ GtkWidget *win;
+ GtkWidget *scrolled_win;
+ GtkWidget *table;
+ char *entries[N_COLUMN];
+} mtp3_stat_dlg_t;
+
+static mtp3_stat_dlg_t dlg;
+
+mtp3_stat_t mtp3_stat[MTP3_MAX_NUM_OPC_DPC];
+guint8 mtp3_num_used;
+
+
+
+/* Create list */
+static
+GtkWidget* create_list(void)
+{
+
+ GtkListStore *list_store;
+ GtkWidget *list;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSortable *sortable;
+ GtkTreeView *list_view;
+ GtkTreeSelection *selection;
+
+ /* Create the store */
+ list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX*/
+ G_TYPE_STRING, /* OPC */
+ G_TYPE_STRING, /* DPC */
+ G_TYPE_STRING, /* SI */
+ G_TYPE_INT, /* Num MSUs */
+ G_TYPE_INT, /* Num Bytes */
+ G_TYPE_FLOAT); /* Avg Bytes */
+
+ /* Create a view */
+ list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
+
+ list_view = GTK_TREE_VIEW(list);
+ sortable = GTK_TREE_SORTABLE(list_store);
+
+ /* Speed up the list display */
+ gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
+
+ /* Setup the sortable columns */
+ gtk_tree_sortable_set_sort_column_id(sortable, OPC_COLUMN, GTK_SORT_ASCENDING);
+ gtk_tree_view_set_headers_clickable(list_view, FALSE);
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref (G_OBJECT (list_store));
+
+ /*
+ * Create the first column packet, associating the "text" attribute of the
+ * cell_renderer to the first column of the model
+ */
+ /* 1:st column */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("OPC", renderer,
+ "text", OPC_COLUMN,
+ NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, OPC_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 2:nd column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("DPC", renderer,
+ "text", DPC_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, DPC_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 3:d column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("SI", renderer,
+ "text", SI_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, SI_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 110);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 4:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Num MSUs", renderer,
+ "text", NUM_MSUS_COLUMN,
+ NULL);
+
+
+ gtk_tree_view_column_set_sort_column_id(column, NUM_MSUS_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 5:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Num Bytes", renderer,
+ "text", NUM_BYTES_COLUMN,
+ NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, NUM_BYTES_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 6:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Avg Bytes", renderer,
+ "text", AVG_BYTES_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
+ GINT_TO_POINTER(AVG_BYTES_COLUMN), NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, AVG_BYTES_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Now enable the sorting of each column */
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
+
+ /* Setup the selection handler */
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+
+ return list;
+
+}
+
+static void
+mtp3_stat_reset(
+ void *tapdata)
+{
+ mtp3_stat_t (*stat_p)[MTP3_MAX_NUM_OPC_DPC] = tapdata;
+
+ mtp3_num_used = 0;
+ memset(stat_p, 0, MTP3_MAX_NUM_OPC_DPC * sizeof(mtp3_stat_t));
+
+ if (dlg.win != NULL)
+ {
+ gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(dlg.table))));
+ }
+}
+
+
+static gboolean
+mtp3_stat_packet(
+ void *tapdata,
+ packet_info *pinfo _U_,
+ epan_dissect_t *edt _U_,
+ const void *data)
+{
+ mtp3_stat_t (*stat_p)[MTP3_MAX_NUM_OPC_DPC] = tapdata;
+ const mtp3_tap_rec_t *data_p = data;
+ int i;
+
+ if (data_p->si_code >= MTP3_NUM_SI_CODE)
+ {
+ /*
+ * we thought this si_code was not used ?
+ * is MTP3_NUM_SI_CODE out of date ?
+ */
+ return(FALSE);
+ }
+
+ /*
+ * look for opc/dpc pair
+ */
+ i = 0;
+ while (i < mtp3_num_used)
+ {
+ if (memcmp(&data_p->addr_opc, &(*stat_p)[i].addr_opc, sizeof(mtp3_addr_pc_t)) == 0)
+ {
+ if (memcmp(&data_p->addr_dpc, &(*stat_p)[i].addr_dpc, sizeof(mtp3_addr_pc_t)) == 0)
+ {
+ break;
+ }
+ }
+
+ i++;
+ }
+
+ if (i == mtp3_num_used)
+ {
+ if (mtp3_num_used == MTP3_MAX_NUM_OPC_DPC)
+ {
+ /*
+ * too many
+ */
+ return(FALSE);
+ }
+
+ mtp3_num_used++;
+ }
+
+ (*stat_p)[i].addr_opc = data_p->addr_opc;
+ (*stat_p)[i].addr_dpc = data_p->addr_dpc;
+ (*stat_p)[i].si_code[data_p->si_code].num_msus++;
+ (*stat_p)[i].si_code[data_p->si_code].size += data_p->size;
+
+ return(TRUE);
+}
+
+
+static void
+mtp3_stat_draw(
+ void *tapdata)
+{
+ mtp3_stat_t (*stat_p)[MTP3_MAX_NUM_OPC_DPC] = tapdata;
+ int i,j;
+ char *str;
+ float avg;
+ GtkListStore *list_store = NULL;
+ GtkTreeIter iter;
+
+ if (!dlg.win || !tapdata)
+ {
+ return;
+ }
+
+ str=ep_alloc(256);
+ i = 0;
+
+ list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (dlg.table))); /* Get store */
+ while (i < mtp3_num_used)
+ {
+ mtp3_addr_to_str_buf(&(*stat_p)[i].addr_opc, str, 256);
+ dlg.entries[0] = g_strdup(str);
+
+ mtp3_addr_to_str_buf(&(*stat_p)[i].addr_dpc, str, 256);
+ dlg.entries[1] = g_strdup(str);
+
+ for (j=0; j < MTP3_NUM_SI_CODE; j++){
+ /* Creates a new row at position. iter will be changed to point to this new row.
+ * If position is larger than the number of rows on the list, then the new row will be appended to the list.
+ * The row will be filled with the values given to this function.
+ * :
+ * should generally be preferred when inserting rows in a sorted list store.
+ */
+ avg = 0.0f;
+ if ((*stat_p)[i].si_code[j].num_msus !=0){
+ avg = (float)(*stat_p)[i].si_code[j].size/(float)(*stat_p)[i].si_code[j].num_msus;
+ }
+
+
+ gtk_list_store_insert_with_values( list_store , &iter, G_MAXINT,
+ OPC_COLUMN, dlg.entries[0],
+ DPC_COLUMN, dlg.entries[1],
+ SI_COLUMN, mtp3_service_indicator_code_short_vals[j].strptr,
+ NUM_MSUS_COLUMN, (*stat_p)[i].si_code[j].num_msus,
+ NUM_BYTES_COLUMN, (*stat_p)[i].si_code[j].size,
+ AVG_BYTES_COLUMN, avg,
+ -1);
+ }
+ i++;
+ }
+}
+
+
+
+
+static void
+mtp3_stat_gtk_win_destroy_cb(
+ GtkWindow *win _U_,
+ gpointer user_data)
+{
+ memset((void *) user_data, 0, sizeof(mtp3_stat_dlg_t));
+}
+
+
+static void
+mtp3_stat_gtk_win_create(
+ mtp3_stat_dlg_t *dlg_p,
+ const char *title)
+{
+ GtkWidget *vbox;
+ GtkWidget *bt_close;
+ GtkWidget *bbox;
+
+
+ dlg_p->win = dlg_window_new(title); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(dlg_p->win), TRUE);
+
+ gtk_window_set_default_size(GTK_WINDOW(dlg_p->win), 640, 390);
+
+ vbox = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(dlg_p->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ dlg_p->scrolled_win = scrolled_window_new(NULL, NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), dlg_p->scrolled_win, TRUE, TRUE, 0);
+
+ dlg_p->table = create_list();
+
+ gtk_widget_show(dlg_p->table);
+
+ gtk_container_add(GTK_CONTAINER(dlg_p->scrolled_win), dlg_p->table);
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ bt_close = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(dlg_p->win, bt_close, window_cancel_button_cb);
+
+ g_signal_connect(dlg_p->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(dlg_p->win, "destroy", G_CALLBACK(mtp3_stat_gtk_win_destroy_cb), dlg_p);
+
+ gtk_widget_show_all(dlg_p->win);
+ window_present(dlg_p->win);
+}
+
+
+void mtp3_stat_gtk_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+
+ /*
+ * if the window is already open, bring it to front
+ */
+ if (dlg.win)
+ {
+ gdk_window_raise(gtk_widget_get_window(dlg.win));
+ return;
+ }
+
+ mtp3_stat_gtk_win_create(&dlg, "MTP3 Statistics");
+
+ mtp3_stat_draw(&mtp3_stat);
+}
+
+
+static void
+mtp3_stat_gtk_init( const char *optarg _U_, void* userdata _U_)
+{
+ mtp3_stat_gtk_cb(NULL, NULL);
+}
+
+
+void
+register_tap_listener_gtkmtp3_stat(void)
+{
+ GString *err_p;
+
+
+ memset((void *) &mtp3_stat, 0, sizeof(mtp3_stat_t));
+
+ err_p =
+ register_tap_listener("mtp3", &mtp3_stat, NULL, 0,
+ mtp3_stat_reset,
+ mtp3_stat_packet,
+ mtp3_stat_draw);
+
+ if (err_p != NULL)
+ {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK, "%s", err_p->str);
+ g_string_free(err_p, TRUE);
+
+ exit(1);
+ }
+ register_stat_cmd_arg("mtp3,msus", mtp3_stat_gtk_init,NULL);
+}
diff --git a/ui/gtk/mtp3_stat.h b/ui/gtk/mtp3_stat.h
new file mode 100644
index 0000000000..4aedd65b54
--- /dev/null
+++ b/ui/gtk/mtp3_stat.h
@@ -0,0 +1,55 @@
+/* mtp3_stat.h
+ *
+ * $Id$
+ *
+ * Copyright 2004, Michael Lum <mlum [AT] telostech.com>,
+ * In association with Telos Technology Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __MTP3_STAT_H__
+#define __MTP3_STAT_H__
+
+/** @file
+ * Statistics for MTP3.
+ * @todo Could someone with more knowledge of this comment it for doxygen?
+ */
+
+typedef struct _mtp3_stat_si_code_t {
+ int num_msus;
+ int size;
+} mtp3_stat_si_code_t;
+
+typedef struct _mtp3_stat_t {
+ mtp3_addr_pc_t addr_opc;
+ mtp3_addr_pc_t addr_dpc;
+ mtp3_stat_si_code_t si_code[MTP3_NUM_SI_CODE];
+} mtp3_stat_t;
+
+/*
+ * I don't like it but I don't have time to create
+ * the code for a dynamic size solution
+ */
+#define MTP3_MAX_NUM_OPC_DPC 50
+
+extern mtp3_stat_t mtp3_stat[];
+extern guint8 mtp3_num_used;
+
+#endif /* __MTP3_STAT_H__ */
diff --git a/ui/gtk/mtp3_summary.c b/ui/gtk/mtp3_summary.c
new file mode 100644
index 0000000000..6afa783296
--- /dev/null
+++ b/ui/gtk/mtp3_summary.c
@@ -0,0 +1,419 @@
+/* mtp3_summary.c
+ * Routines for MTP3 Statictics summary window
+ *
+ * Copyright 2004, Michael Lum <mlum [AT] telostech.com>
+ * In association with Telos Technology Inc.
+ *
+ * Modified from gsm_map_summary.c
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/packet_info.h"
+#include "epan/epan.h"
+#include "epan/value_string.h"
+#include <epan/tap.h>
+#include <epan/dissectors/packet-mtp3.h>
+
+#include "../stat_menu.h"
+#include "../globals.h"
+#include "../file.h"
+#include "../summary.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/mtp3_stat.h"
+
+#define SUM_STR_MAX 1024
+
+
+typedef struct _my_columns_t {
+ guint32 value;
+ const gchar *strptr;
+ GtkJustification just;
+} my_columns_t;
+
+enum
+{
+ SI_COLUMN,
+ NUM_MSUS_COLUMN,
+ NUM_MSUS_SEC_COLUMN,
+ NUM_BYTES_COLUMN,
+ NUM_BYTES_MSU_COLUMN,
+ NUM_BYTES_SEC_COLUMN,
+ N_COLUMN /* The number of columns */
+};
+
+/* Create list */
+static
+GtkWidget* create_list(void)
+{
+
+ GtkListStore *list_store;
+ GtkWidget *list;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSortable *sortable;
+ GtkTreeView *list_view;
+ GtkTreeSelection *selection;
+
+ /* Create the store */
+ list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX*/
+ G_TYPE_STRING, /* SI */
+ G_TYPE_INT, /* Num MSUs */
+ G_TYPE_STRING, /* MSUs/sec */
+ G_TYPE_INT, /* Num Bytes */
+ G_TYPE_STRING, /* Bytes/MSU */
+ G_TYPE_STRING); /* Bytes/sec */
+
+ /* Create a view */
+ list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
+
+ list_view = GTK_TREE_VIEW(list);
+ sortable = GTK_TREE_SORTABLE(list_store);
+
+ /* Speed up the list display */
+ gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
+
+ /* Setup the sortable columns */
+ gtk_tree_sortable_set_sort_column_id(sortable, SI_COLUMN, GTK_SORT_ASCENDING);
+ gtk_tree_view_set_headers_clickable(list_view, FALSE);
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref (G_OBJECT (list_store));
+
+ /*
+ * Create the first column packet, associating the "text" attribute of the
+ * cell_renderer to the first column of the model
+ */
+ /* 1:st column */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("SI", renderer,
+ "text", SI_COLUMN,
+ NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, SI_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 110);
+
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 2:nd column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Num MSUs", renderer,
+ "text", NUM_MSUS_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, NUM_MSUS_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 3:d column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("MSUs/sec", renderer,
+ "text", NUM_MSUS_SEC_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, NUM_MSUS_SEC_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 4:d column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Num Bytes", renderer,
+ "text", NUM_BYTES_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, NUM_BYTES_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 5:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Bytes/MSU", renderer,
+ "text", NUM_BYTES_MSU_COLUMN,
+ NULL);
+
+
+ gtk_tree_view_column_set_sort_column_id(column, NUM_BYTES_MSU_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 6:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Bytes/sec", renderer,
+ "text", NUM_BYTES_SEC_COLUMN,
+ NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, NUM_BYTES_SEC_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Now enable the sorting of each column */
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
+
+ /* Setup the selection handler */
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+
+ return list;
+
+}
+
+static void
+add_string_to_box(gchar *str, GtkWidget *box)
+{
+ GtkWidget *lb;
+ lb = gtk_label_new(str);
+ gtk_misc_set_alignment(GTK_MISC(lb), 0.0f, 0.5f);
+ gtk_box_pack_start(GTK_BOX(box), lb,FALSE,FALSE, 0);
+ gtk_widget_show(lb);
+}
+
+
+
+static void
+mtp3_sum_draw(
+ GtkWidget *table,
+ double seconds,
+ int *tot_num_msus_p,
+ double *tot_num_bytes_p)
+{
+ char *entries[N_COLUMN];
+ int i, j;
+ int num_msus;
+ int num_bytes;
+ GtkListStore *list_store = NULL;
+ GtkTreeIter iter;
+
+ *tot_num_msus_p = 0;
+ *tot_num_bytes_p = 0;
+
+ list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (table))); /* Get store */
+
+ for (i=0; i < MTP3_NUM_SI_CODE; i++)
+ {
+ j = 0;
+ num_msus = 0;
+ num_bytes = 0;
+
+ while (j < mtp3_num_used)
+ {
+ num_msus += mtp3_stat[j].si_code[i].num_msus;
+ num_bytes += mtp3_stat[j].si_code[i].size;
+
+ j++;
+ }
+
+ *tot_num_msus_p += num_msus;
+ *tot_num_bytes_p += num_bytes;
+
+ entries[2] = (seconds) ? g_strdup_printf("%.2f", (double)num_msus/seconds) : g_strdup("N/A");
+ entries[4] = (num_msus) ? g_strdup_printf("%.2f", (double)num_bytes/num_msus) : g_strdup("N/A");
+ entries[5] = (seconds) ? g_strdup_printf("%.2f", (double)num_bytes/seconds) : g_strdup("N/A");
+
+ gtk_list_store_insert_with_values( list_store , &iter, G_MAXINT,
+ SI_COLUMN, mtp3_service_indicator_code_short_vals[i].strptr,
+ NUM_MSUS_COLUMN, num_msus,
+ NUM_MSUS_SEC_COLUMN, entries[2],
+ NUM_BYTES_COLUMN, num_bytes,
+ NUM_BYTES_MSU_COLUMN, entries[4],
+ NUM_BYTES_SEC_COLUMN, entries[5],
+ -1);
+
+ g_free(entries[2]);
+ g_free(entries[4]);
+ g_free(entries[5]);
+ }
+}
+
+
+void mtp3_sum_gtk_sum_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ summary_tally summary;
+ GtkWidget *sum_open_w,
+ *main_vb, *file_fr, *data_fr, *file_box,
+ *data_box, *bbox, *close_bt,
+ *tot_fr, *tot_box,
+ *table, *table_fr;
+
+ gchar string_buff[SUM_STR_MAX];
+ const char * file_type;
+ double seconds;
+ int tot_num_msus;
+ double tot_num_bytes;
+
+ /* initialize the tally */
+ summary_fill_in(&cfile, &summary);
+
+ /* initial compututations */
+ seconds = summary.stop_time - summary.start_time;
+
+ sum_open_w = dlg_window_new("MTP3 Statistics: Summary"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(sum_open_w), TRUE);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(sum_open_w), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* File frame */
+ file_fr = gtk_frame_new("File");
+ gtk_container_add(GTK_CONTAINER(main_vb), file_fr);
+ gtk_widget_show(file_fr);
+
+ file_box = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(file_fr), file_box);
+ gtk_widget_show(file_box);
+
+ /* filename */
+ g_snprintf(string_buff, SUM_STR_MAX, "Name: %s", ((summary.filename) ? summary.filename : "None"));
+ add_string_to_box(string_buff, file_box);
+
+ /* length */
+ g_snprintf(string_buff, SUM_STR_MAX, "Length: %" G_GINT64_MODIFIER "d", summary.file_length);
+ add_string_to_box(string_buff, file_box);
+
+ /* format */
+ file_type = wtap_file_type_string(summary.file_type);
+ g_snprintf(string_buff, SUM_STR_MAX, "Format: %s", (file_type ? file_type : "N/A"));
+ add_string_to_box(string_buff, file_box);
+
+ if (summary.has_snap) {
+ /* snapshot length */
+ g_snprintf(string_buff, SUM_STR_MAX, "Snapshot length: %u", summary.snap);
+ add_string_to_box(string_buff, file_box);
+ }
+
+ /* Data frame */
+ data_fr = gtk_frame_new("Data");
+ gtk_container_add(GTK_CONTAINER(main_vb), data_fr);
+ gtk_widget_show(data_fr);
+
+ data_box = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(data_fr), data_box);
+ gtk_widget_show(data_box);
+
+ /* seconds */
+ g_snprintf(string_buff, SUM_STR_MAX, "Elapsed time: %.3f seconds", summary.elapsed_time);
+ add_string_to_box(string_buff, data_box);
+
+ g_snprintf(string_buff, SUM_STR_MAX, "Between first and last packet: %.3f seconds", seconds);
+ add_string_to_box(string_buff, data_box);
+
+ /* Packet count */
+ g_snprintf(string_buff, SUM_STR_MAX, "Packet count: %i", summary.packet_count);
+ add_string_to_box(string_buff, data_box);
+
+ /* MTP3 SPECIFIC */
+ table_fr = gtk_frame_new("Service Indicator (SI) Totals");
+ gtk_container_add(GTK_CONTAINER(main_vb), table_fr);
+ gtk_widget_show(table_fr);
+
+ table = create_list();
+
+ gtk_container_add(GTK_CONTAINER(table_fr), table);
+ gtk_widget_show(table);
+
+
+ mtp3_sum_draw(table, seconds, &tot_num_msus, &tot_num_bytes);
+
+ /* Totals frame */
+ tot_fr = gtk_frame_new("Totals");
+ gtk_container_add(GTK_CONTAINER(main_vb), tot_fr);
+ gtk_widget_show(tot_fr);
+
+ tot_box = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(tot_fr), tot_box);
+ gtk_widget_show(tot_box);
+
+ g_snprintf(string_buff, SUM_STR_MAX, "Total MSUs: %u", tot_num_msus);
+ add_string_to_box(string_buff, tot_box);
+
+ if (seconds) {
+ g_snprintf(string_buff, SUM_STR_MAX, "MSUs/second: %.2f", tot_num_msus/seconds);
+ }
+ else {
+ g_snprintf(string_buff, SUM_STR_MAX, "MSUs/second: N/A");
+ }
+ add_string_to_box(string_buff, tot_box);
+
+ g_snprintf(string_buff, SUM_STR_MAX, "Total Bytes: %.0f", tot_num_bytes);
+ add_string_to_box(string_buff, tot_box);
+
+ if (tot_num_msus) {
+ g_snprintf(string_buff, SUM_STR_MAX, "Average Bytes/MSU: %.2f", tot_num_bytes/tot_num_msus);
+ }
+ else {
+ g_snprintf(string_buff, SUM_STR_MAX, "Average Bytes/MSU: N/A");
+ }
+ add_string_to_box(string_buff, tot_box);
+
+ if (seconds) {
+ g_snprintf(string_buff, SUM_STR_MAX, "Bytes/second: %.2f", tot_num_bytes/seconds);
+ }
+ else {
+ g_snprintf(string_buff, SUM_STR_MAX, "Bytes/second: N/A");
+ }
+ add_string_to_box(string_buff, tot_box);
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_container_add(GTK_CONTAINER(main_vb), bbox);
+ gtk_widget_show(bbox);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(sum_open_w, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(sum_open_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+
+ gtk_widget_show_all(sum_open_w);
+ window_present(sum_open_w);
+}
+
+
+void
+register_tap_listener_gtkmtp3_summary(void)
+{
+}
diff --git a/ui/gtk/ncp_stat.c b/ui/gtk/ncp_stat.c
new file mode 100644
index 0000000000..c83d3d0d9c
--- /dev/null
+++ b/ui/gtk/ncp_stat.c
@@ -0,0 +1,746 @@
+/* ncp_stat.c
+ * ncp_stat 2005 Greg Morris
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/value_string.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-ncp-int.h>
+
+#include "../timestats.h"
+#include "../simple_dialog.h"
+#include "../file.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/service_response_time_table.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/main.h"
+
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _ncpstat_t {
+ GtkWidget *win;
+ srt_stat_table ncp_srt_table;
+ srt_stat_table nds_srt_table;
+ srt_stat_table func_srt_table;
+ srt_stat_table sss_srt_table;
+ srt_stat_table nmas_srt_table;
+ srt_stat_table sub_17_srt_table;
+ srt_stat_table sub_21_srt_table;
+ srt_stat_table sub_22_srt_table;
+ srt_stat_table sub_23_srt_table;
+ srt_stat_table sub_32_srt_table;
+ srt_stat_table sub_34_srt_table;
+ srt_stat_table sub_35_srt_table;
+ srt_stat_table sub_36_srt_table;
+ srt_stat_table sub_86_srt_table;
+ srt_stat_table sub_87_srt_table;
+ srt_stat_table sub_89_srt_table;
+ srt_stat_table sub_90_srt_table;
+ srt_stat_table sub_92_srt_table;
+ srt_stat_table sub_94_srt_table;
+ srt_stat_table sub_104_srt_table;
+ srt_stat_table sub_111_srt_table;
+ srt_stat_table sub_114_srt_table;
+ srt_stat_table sub_123_srt_table;
+ srt_stat_table sub_131_srt_table;
+} ncpstat_t;
+
+static const value_string ncp_group_vals[] = {
+ { 0, "Synchronization" },
+ { 1, "Print" },
+ { 2, "File System" },
+ { 3, "Connection" },
+ { 4, "File Server Environment" },
+ { 5, "Message" },
+ { 6, "Bindery" },
+ { 7, "Queue Management System (QMS)" },
+ { 8, "Accounting" },
+ { 9, "Transaction Tracking" },
+ { 10, "AFP" },
+ { 11, "NCP Extension" },
+ { 12, "Extended Attribute" },
+ { 13, "Auditing" },
+ { 14, "Enhanced File System" },
+ { 15, "Migration" },
+ { 16, "Novell Modular Authentication Services (NMAS)" },
+ { 17, "Secret Store Services (SSS)" },
+ { 18, "Packet Burst" },
+ { 19, "Novell Directory Services (NDS)" },
+ { 20, "Time Synchronization" },
+ { 21, "Server Statistics" },
+ { 22, "Remote" },
+ { 0, NULL}
+};
+
+static const value_string sss_verb_enum[] = {
+ { 0x00000000, "Query Server" },
+ { 0x00000001, "Read App Secrets" },
+ { 0x00000002, "Write App Secrets" },
+ { 0x00000003, "Add Secret ID" },
+ { 0x00000004, "Remove Secret ID" },
+ { 0x00000005, "Remove SecretStore" },
+ { 0x00000006, "Enumerate Secret IDs" },
+ { 0x00000007, "Unlock Store" },
+ { 0x00000008, "Set Master Password" },
+ { 0x00000009, "Get Service Information" },
+ { 0x000000ff, "Fragment"},
+ { 0x00000000, NULL}
+};
+
+static const value_string nmas_subverb_enum[] = {
+ { 0, "Fragmented Ping" },
+ { 2, "Client Put Data" },
+ { 4, "Client Get Data" },
+ { 6, "Client Get User NDS Credentials" },
+ { 8, "Login Store Management" },
+ { 10, "Writable Object Check" },
+ { 1242, "Message Handler" },
+ { 0, NULL}
+};
+
+static const value_string ncp_nds_verb_vals[] = {
+ { 1, "Resolve Name" },
+ { 2, "Read Entry Information" },
+ { 3, "Read" },
+ { 4, "Compare" },
+ { 5, "List" },
+ { 6, "Search Entries" },
+ { 7, "Add Entry" },
+ { 8, "Remove Entry" },
+ { 9, "Modify Entry" },
+ { 10, "Modify RDN" },
+ { 11, "Create Attribute" },
+ { 12, "Read Attribute Definition" },
+ { 13, "Remove Attribute Definition" },
+ { 14, "Define Class" },
+ { 15, "Read Class Definition" },
+ { 16, "Modify Class Definition" },
+ { 17, "Remove Class Definition" },
+ { 18, "List Containable Classes" },
+ { 19, "Get Effective Rights" },
+ { 20, "Add Partition" },
+ { 21, "Remove Partition" },
+ { 22, "List Partitions" },
+ { 23, "Split Partition" },
+ { 24, "Join Partitions" },
+ { 25, "Add Replica" },
+ { 26, "Remove Replica" },
+ { 27, "Open Stream" },
+ { 28, "Search Filter" },
+ { 29, "Create Subordinate Reference" },
+ { 30, "Link Replica" },
+ { 31, "Change Replica Type" },
+ { 32, "Start Update Schema" },
+ { 33, "End Update Schema" },
+ { 34, "Update Schema" },
+ { 35, "Start Update Replica" },
+ { 36, "End Update Replica" },
+ { 37, "Update Replica" },
+ { 38, "Synchronize Partition" },
+ { 39, "Synchronize Schema" },
+ { 40, "Read Syntaxes" },
+ { 41, "Get Replica Root ID" },
+ { 42, "Begin Move Entry" },
+ { 43, "Finish Move Entry" },
+ { 44, "Release Moved Entry" },
+ { 45, "Backup Entry" },
+ { 46, "Restore Entry" },
+ { 47, "Save DIB (Obsolete)" },
+ { 48, "Control" },
+ { 49, "Remove Backlink" },
+ { 50, "Close Iteration" },
+ { 51, "Mutate Entry" },
+ { 52, "Audit Skulking" },
+ { 53, "Get Server Address" },
+ { 54, "Set Keys" },
+ { 55, "Change Password" },
+ { 56, "Verify Password" },
+ { 57, "Begin Login" },
+ { 58, "Finish Login" },
+ { 59, "Begin Authentication" },
+ { 60, "Finish Authentication" },
+ { 61, "Logout" },
+ { 62, "Repair Ring (Obsolete)" },
+ { 63, "Repair Timestamps" },
+ { 64, "Create Back Link" },
+ { 65, "Delete External Reference" },
+ { 66, "Rename External Reference" },
+ { 67, "Create Queue Entry Directory" },
+ { 68, "Remove Queue Entry Directory" },
+ { 69, "Merge Entries" },
+ { 70, "Change Tree Name" },
+ { 71, "Partition Entry Count" },
+ { 72, "Check Login Restrictions" },
+ { 73, "Start Join" },
+ { 74, "Low Level Split" },
+ { 75, "Low Level Join" },
+ { 76, "Abort Partition Operation" },
+ { 77, "Get All Servers" },
+ { 78, "Partition Function" },
+ { 79, "Read References" },
+ { 80, "Inspect Entry" },
+ { 81, "Get Remote Entry ID" },
+ { 82, "Change Security" },
+ { 83, "Check Console Operator" },
+ { 84, "Start Move Tree" },
+ { 85, "Move Tree" },
+ { 86, "End Move Tree" },
+ { 87, "Low Level Abort Join" },
+ { 88, "Check Security Equivalence" },
+ { 89, "Merge Tree" },
+ { 90, "Sync External Reference" },
+ { 91, "Resend Entry" },
+ { 92, "New Schema Epoch" },
+ { 93, "Statistics" },
+ { 94, "Ping" },
+ { 95, "Get Bindery Contexts" },
+ { 96, "Monitor Connection" },
+ { 97, "Get DS Statistics" },
+ { 98, "Reset DS Counters" },
+ { 99, "Console" },
+ { 100, "Read Stream" },
+ { 101, "Write Stream" },
+ { 102, "Create Orphan Partition" },
+ { 103, "Remove Orphan Partition" },
+ { 104, "Link Orphan Partition" },
+ { 105, "Set Distributed Reference Link (DRL)" },
+ { 106, "Available" },
+ { 107, "Available" },
+ { 108, "Verify Distributed Reference Link (DRL)" },
+ { 109, "Verify Partition" },
+ { 110, "Iterator" },
+ { 111, "Available" },
+ { 112, "Close Stream" },
+ { 113, "Available" },
+ { 114, "Read Status" },
+ { 115, "Partition Sync Status" },
+ { 116, "Read Reference Data" },
+ { 117, "Write Reference Data" },
+ { 118, "Resource Event" },
+ { 119, "DIB Request (obsolete)" },
+ { 120, "Set Replication Filter" },
+ { 121, "Get Replication Filter" },
+ { 122, "Change Attribute Definition" },
+ { 123, "Schema in Use" },
+ { 124, "Remove Keys" },
+ { 125, "Clone" },
+ { 126, "Multiple Operations Transaction" },
+ { 240, "Ping" },
+ { 255, "EDirectory Call" },
+ { 0, NULL }
+};
+
+static void
+ncpstat_set_title(ncpstat_t *ss)
+{
+ char *title;
+
+ title = g_strdup_printf("NCP Service Response Time statistics: %s",
+ cf_get_display_name(&cfile));
+ gtk_window_set_title(GTK_WINDOW(ss->win), title);
+ g_free(title);
+}
+
+static void
+ncpstat_reset(void *pss)
+{
+ ncpstat_t *ss=(ncpstat_t *)pss;
+
+ reset_srt_table_data(&ss->ncp_srt_table);
+ reset_srt_table_data(&ss->func_srt_table);
+ reset_srt_table_data(&ss->nds_srt_table);
+ reset_srt_table_data(&ss->sss_srt_table);
+ reset_srt_table_data(&ss->nmas_srt_table);
+ reset_srt_table_data(&ss->sub_17_srt_table);
+ reset_srt_table_data(&ss->sub_21_srt_table);
+ reset_srt_table_data(&ss->sub_22_srt_table);
+ reset_srt_table_data(&ss->sub_23_srt_table);
+ reset_srt_table_data(&ss->sub_32_srt_table);
+ reset_srt_table_data(&ss->sub_34_srt_table);
+ reset_srt_table_data(&ss->sub_35_srt_table);
+ reset_srt_table_data(&ss->sub_36_srt_table);
+ reset_srt_table_data(&ss->sub_86_srt_table);
+ reset_srt_table_data(&ss->sub_87_srt_table);
+ reset_srt_table_data(&ss->sub_89_srt_table);
+ reset_srt_table_data(&ss->sub_90_srt_table);
+ reset_srt_table_data(&ss->sub_92_srt_table);
+ reset_srt_table_data(&ss->sub_94_srt_table);
+ reset_srt_table_data(&ss->sub_104_srt_table);
+ reset_srt_table_data(&ss->sub_111_srt_table);
+ reset_srt_table_data(&ss->sub_114_srt_table);
+ reset_srt_table_data(&ss->sub_123_srt_table);
+ reset_srt_table_data(&ss->sub_131_srt_table);
+ ncpstat_set_title(ss);
+}
+
+static int
+ncpstat_packet(void *pss, packet_info *pinfo, epan_dissect_t *edt _U_, const void *prv)
+{
+ ncpstat_t *ss=(ncpstat_t *)pss;
+ const ncp_req_hash_value *request_val=prv;
+
+ /* if we havent seen the request, just ignore it */
+ if(!request_val || request_val->ncp_rec==0){
+ return 0;
+ }
+ /* By Group */
+ init_srt_table_row(&ss->ncp_srt_table, request_val->ncp_rec->group, val_to_str(request_val->ncp_rec->group, ncp_group_vals, "Unknown(%u)"));
+ add_srt_table_data(&ss->ncp_srt_table, request_val->ncp_rec->group, &request_val->req_frame_time, pinfo);
+ /* By NCP number without subfunction*/
+ if (request_val->ncp_rec->subfunc==0) {
+ init_srt_table_row(&ss->func_srt_table, request_val->ncp_rec->func, request_val->ncp_rec->name);
+ add_srt_table_data(&ss->func_srt_table, request_val->ncp_rec->func, &request_val->req_frame_time, pinfo);
+ }
+ /* By Subfunction number */
+ if(request_val->ncp_rec->subfunc!=0){
+ if (request_val->ncp_rec->func==17) {
+ init_srt_table_row(&ss->sub_17_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name);
+ add_srt_table_data(&ss->sub_17_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo);
+ }
+ if (request_val->ncp_rec->func==21) {
+ init_srt_table_row(&ss->sub_21_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name);
+ add_srt_table_data(&ss->sub_21_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo);
+ }
+ if (request_val->ncp_rec->func==22) {
+ init_srt_table_row(&ss->sub_22_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name);
+ add_srt_table_data(&ss->sub_22_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo);
+ }
+ if (request_val->ncp_rec->func==23) {
+ init_srt_table_row(&ss->sub_23_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name);
+ add_srt_table_data(&ss->sub_23_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo);
+ }
+ if (request_val->ncp_rec->func==32) {
+ init_srt_table_row(&ss->sub_32_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name);
+ add_srt_table_data(&ss->sub_32_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo);
+ }
+ if (request_val->ncp_rec->func==34) {
+ init_srt_table_row(&ss->sub_34_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name);
+ add_srt_table_data(&ss->sub_34_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo);
+ }
+ if (request_val->ncp_rec->func==35) {
+ init_srt_table_row(&ss->sub_35_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name);
+ add_srt_table_data(&ss->sub_35_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo);
+ }
+ if (request_val->ncp_rec->func==36) {
+ init_srt_table_row(&ss->sub_36_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name);
+ add_srt_table_data(&ss->sub_36_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo);
+ }
+ if (request_val->ncp_rec->func==86) {
+ init_srt_table_row(&ss->sub_86_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name);
+ add_srt_table_data(&ss->sub_86_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo);
+ }
+ if (request_val->ncp_rec->func==87) {
+ init_srt_table_row(&ss->sub_87_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name);
+ add_srt_table_data(&ss->sub_87_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo);
+ }
+ if (request_val->ncp_rec->func==89) {
+ init_srt_table_row(&ss->sub_89_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name);
+ add_srt_table_data(&ss->sub_89_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo);
+ }
+ if (request_val->ncp_rec->func==90) {
+ init_srt_table_row(&ss->sub_90_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name);
+ add_srt_table_data(&ss->sub_90_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo);
+ }
+ if (request_val->ncp_rec->func==92) {
+ init_srt_table_row(&ss->sub_92_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name);
+ add_srt_table_data(&ss->sub_92_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo);
+ }
+ if (request_val->ncp_rec->func==94) {
+ init_srt_table_row(&ss->sub_94_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name);
+ add_srt_table_data(&ss->sub_94_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo);
+ }
+ if (request_val->ncp_rec->func==104) {
+ init_srt_table_row(&ss->sub_104_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name);
+ add_srt_table_data(&ss->sub_104_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo);
+ }
+ if (request_val->ncp_rec->func==111) {
+ init_srt_table_row(&ss->sub_111_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name);
+ add_srt_table_data(&ss->sub_111_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo);
+ }
+ if (request_val->ncp_rec->func==114) {
+ init_srt_table_row(&ss->sub_114_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name);
+ add_srt_table_data(&ss->sub_114_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo);
+ }
+ if (request_val->ncp_rec->func==123) {
+ init_srt_table_row(&ss->sub_123_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name);
+ add_srt_table_data(&ss->sub_123_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo);
+ }
+ if (request_val->ncp_rec->func==131) {
+ init_srt_table_row(&ss->sub_131_srt_table, (request_val->ncp_rec->subfunc), request_val->ncp_rec->name);
+ add_srt_table_data(&ss->sub_131_srt_table, (request_val->ncp_rec->subfunc), &request_val->req_frame_time, pinfo);
+ }
+ }
+ /* By NDS verb */
+ if (request_val->ncp_rec->func==0x68) {
+ init_srt_table_row(&ss->nds_srt_table, (request_val->nds_request_verb), val_to_str(request_val->nds_request_verb, ncp_nds_verb_vals, "Unknown(%u)"));
+ add_srt_table_data(&ss->nds_srt_table, (request_val->nds_request_verb), &request_val->req_frame_time, pinfo);
+ }
+ if (request_val->ncp_rec->func==0x5c) {
+ init_srt_table_row(&ss->sss_srt_table, (request_val->req_nds_flags), val_to_str(request_val->req_nds_flags, sss_verb_enum, "Unknown(%u)"));
+ add_srt_table_data(&ss->sss_srt_table, (request_val->req_nds_flags), &request_val->req_frame_time, pinfo);
+ }
+ if (request_val->ncp_rec->func==0x5e) {
+ init_srt_table_row(&ss->nmas_srt_table, (request_val->req_nds_flags), val_to_str(request_val->req_nds_flags, nmas_subverb_enum, "Unknown(%u)"));
+ add_srt_table_data(&ss->nmas_srt_table, (request_val->req_nds_flags), &request_val->req_frame_time, pinfo);
+ }
+ return 1;
+}
+
+
+
+static void
+ncpstat_draw(void *pss)
+{
+ ncpstat_t *ss=(ncpstat_t *)pss;
+
+ draw_srt_table_data(&ss->ncp_srt_table);
+ draw_srt_table_data(&ss->func_srt_table);
+ draw_srt_table_data(&ss->nds_srt_table);
+ draw_srt_table_data(&ss->sss_srt_table);
+ draw_srt_table_data(&ss->nmas_srt_table);
+ draw_srt_table_data(&ss->sub_17_srt_table);
+ draw_srt_table_data(&ss->sub_21_srt_table);
+ draw_srt_table_data(&ss->sub_22_srt_table);
+ draw_srt_table_data(&ss->sub_23_srt_table);
+ draw_srt_table_data(&ss->sub_32_srt_table);
+ draw_srt_table_data(&ss->sub_34_srt_table);
+ draw_srt_table_data(&ss->sub_35_srt_table);
+ draw_srt_table_data(&ss->sub_36_srt_table);
+ draw_srt_table_data(&ss->sub_86_srt_table);
+ draw_srt_table_data(&ss->sub_87_srt_table);
+ draw_srt_table_data(&ss->sub_89_srt_table);
+ draw_srt_table_data(&ss->sub_90_srt_table);
+ draw_srt_table_data(&ss->sub_92_srt_table);
+ draw_srt_table_data(&ss->sub_94_srt_table);
+ draw_srt_table_data(&ss->sub_104_srt_table);
+ draw_srt_table_data(&ss->sub_111_srt_table);
+ draw_srt_table_data(&ss->sub_114_srt_table);
+ draw_srt_table_data(&ss->sub_123_srt_table);
+ draw_srt_table_data(&ss->sub_131_srt_table);
+}
+
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ ncpstat_t *ss=(ncpstat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(ss);
+ unprotect_thread_critical_region();
+
+
+ free_srt_table_data(&ss->ncp_srt_table);
+ free_srt_table_data(&ss->func_srt_table);
+ free_srt_table_data(&ss->nds_srt_table);
+ free_srt_table_data(&ss->sss_srt_table);
+ free_srt_table_data(&ss->nmas_srt_table);
+ free_srt_table_data(&ss->sub_17_srt_table);
+ free_srt_table_data(&ss->sub_21_srt_table);
+ free_srt_table_data(&ss->sub_22_srt_table);
+ free_srt_table_data(&ss->sub_23_srt_table);
+ free_srt_table_data(&ss->sub_32_srt_table);
+ free_srt_table_data(&ss->sub_34_srt_table);
+ free_srt_table_data(&ss->sub_35_srt_table);
+ free_srt_table_data(&ss->sub_36_srt_table);
+ free_srt_table_data(&ss->sub_86_srt_table);
+ free_srt_table_data(&ss->sub_87_srt_table);
+ free_srt_table_data(&ss->sub_89_srt_table);
+ free_srt_table_data(&ss->sub_90_srt_table);
+ free_srt_table_data(&ss->sub_92_srt_table);
+ free_srt_table_data(&ss->sub_94_srt_table);
+ free_srt_table_data(&ss->sub_104_srt_table);
+ free_srt_table_data(&ss->sub_111_srt_table);
+ free_srt_table_data(&ss->sub_114_srt_table);
+ free_srt_table_data(&ss->sub_123_srt_table);
+ free_srt_table_data(&ss->sub_131_srt_table);
+ g_free(ss);
+}
+
+
+static void
+gtk_ncpstat_init(const char *optarg, void *userdata _U_)
+{
+ ncpstat_t *ss;
+ const char *filter=NULL;
+ GtkWidget *label;
+ char *filter_string;
+ GString *error_string;
+ GtkWidget *temp_page;
+ GtkWidget *main_nb;
+ GtkWidget *vbox;
+ GtkWidget *bbox;
+ GtkWidget *close_bt;
+
+ if(!strncmp(optarg,"ncp,srt,",8)){
+ filter=optarg+8;
+ } else {
+ filter=NULL;
+ }
+
+ ss=g_malloc(sizeof(ncpstat_t));
+
+ ss->win = dlg_window_new("ncp-stat"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(ss->win), TRUE);
+ gtk_window_set_default_size(GTK_WINDOW(ss->win), 300, 400);
+
+ ncpstat_set_title(ss);
+
+ vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(ss->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ label=gtk_label_new("NCP Service Response Time Statistics");
+ gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
+
+ filter_string = g_strdup_printf("Filter: %s",filter ? filter : "");
+ label=gtk_label_new(filter_string);
+ g_free(filter_string);
+ gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ main_nb = gtk_notebook_new();
+ gtk_box_pack_start(GTK_BOX(vbox), main_nb, TRUE, TRUE, 0);
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label = gtk_label_new("Groups");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+
+ /* NCP Groups */
+ /* We must display TOP LEVEL Widget before calling init_srt_table() */
+ gtk_widget_show_all(ss->win);
+ label=gtk_label_new("NCP by Group Type");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->ncp_srt_table, 256, temp_page, "ncp.group");
+
+ /* NCP Functions */
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label = gtk_label_new("Functions");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("NCP Functions without Subfunctions");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->func_srt_table, 256, temp_page, "ncp.func");
+
+ /* NCP Subfunctions */
+
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label = gtk_label_new("17");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("Subfunctions for NCP 17");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->sub_17_srt_table, 256, temp_page, "ncp.func==17 && ncp.subfunc");
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("21");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("Subfunctions for NCP 21");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->sub_21_srt_table, 256, temp_page, "ncp.func==21 && ncp.subfunc");
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("22");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("Subfunctions for NCP 22");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->sub_22_srt_table, 256, temp_page, "ncp.func==22 && ncp.subfunc");
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("23");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("Subfunctions for NCP 23");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->sub_23_srt_table, 256, temp_page, "ncp.func==23 && ncp.subfunc");
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("32");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("Subfunctions for NCP 32");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->sub_32_srt_table, 256, temp_page, "ncp.func==32 && ncp.subfunc");
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("34");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("Subfunctions for NCP 34");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->sub_34_srt_table, 256, temp_page, "ncp.func==34 && ncp.subfunc");
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("35");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("Subfunctions for NCP 35");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->sub_35_srt_table, 256, temp_page, "ncp.func==35 && ncp.subfunc");
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("36");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("Subfunctions for NCP 36");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->sub_36_srt_table, 256, temp_page, "ncp.func==36 && ncp.subfunc");
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("86");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("Subfunctions for NCP 86");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->sub_86_srt_table, 256, temp_page, "ncp.func==86 && ncp.subfunc");
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("87");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("Subfunctions for NCP 87");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->sub_87_srt_table, 256, temp_page, "ncp.func==87 && ncp.subfunc");
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("89");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("Subfunctions for NCP 89 (Extended NCP's with UTF8 Support)");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->sub_89_srt_table, 256, temp_page, "ncp.func==89 && ncp.subfunc");
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("90");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("Subfunctions for NCP 90");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->sub_90_srt_table, 256, temp_page, "ncp.func==90 && ncp.subfunc");
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("92");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("Subfunctions for NCP 92 (Secret Store Services)");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->sub_92_srt_table, 256, temp_page, "ncp.func==92 && ncp.subfunc");
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("94");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("Subfunctions for NCP 94 (Novell Modular Authentication Services)");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->sub_94_srt_table, 256, temp_page, "ncp.func==94 && ncp.subfunc");
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("104");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("Subfunctions for NCP 104");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->sub_104_srt_table, 256, temp_page, "ncp.func==104 && ncp.subfunc");
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("111");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("Subfunctions for NCP 111");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->sub_111_srt_table, 256, temp_page, "ncp.func==111 && ncp.subfunc");
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("114");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("Subfunctions for NCP 114");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->sub_114_srt_table, 256, temp_page, "ncp.func==114 && ncp.subfunc");
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("123");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("Subfunctions for NCP 123");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->sub_123_srt_table, 256, temp_page, "ncp.func==123 && ncp.subfunc");
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("131");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("Subfunctions for NCP 131");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->sub_131_srt_table, 256, temp_page, "ncp.func==131 && ncp.subfunc");
+
+ /* NDS Verbs */
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("NDS");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("NDS Verbs");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->nds_srt_table, 256, temp_page, "ncp.ndsverb");
+ /* Secret Store Verbs */
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("SSS");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("Secret Store Verbs");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->sss_srt_table, 256, temp_page, "sss.subverb");
+ /* NMAS Verbs */
+ temp_page = gtk_vbox_new(FALSE, 6);
+ label=gtk_label_new("NMAS");
+ gtk_notebook_append_page(GTK_NOTEBOOK(main_nb), temp_page, label);
+ label=gtk_label_new("NMAS Verbs");
+ gtk_box_pack_start(GTK_BOX(temp_page), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->nmas_srt_table, 256, temp_page, "nmas.subverb");
+
+ /* Register the tap listener */
+ error_string=register_tap_listener("ncp_srt", ss, filter, 0, ncpstat_reset, ncpstat_packet, ncpstat_draw);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(ss);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(ss->win, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(ss->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(ss->win, "destroy", G_CALLBACK(win_destroy_cb), ss);
+
+ gtk_widget_show_all(ss->win);
+ window_present(ss->win);
+
+ cf_redissect_packets(&cfile);
+}
+
+static tap_param ncp_stat_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg ncp_stat_dlg = {
+ "NCP SRT Statistics",
+ "ncp,srt",
+ gtk_ncpstat_init,
+ -1,
+ G_N_ELEMENTS(ncp_stat_params),
+ ncp_stat_params
+};
+
+void
+register_tap_listener_gtkncpstat(void)
+{
+ register_dfilter_stat(&ncp_stat_dlg, "NCP",
+ REGISTER_STAT_GROUP_RESPONSE_TIME);
+}
+void ncp_srt_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &ncp_stat_dlg);
+}
+
diff --git a/ui/gtk/network_icons.h b/ui/gtk/network_icons.h
new file mode 100644
index 0000000000..1f430e683d
--- /dev/null
+++ b/ui/gtk/network_icons.h
@@ -0,0 +1,287 @@
+/* This file was automatically generated. DO NOT EDIT. */
+
+/* GdkPixbuf RGBA C-Source image dump */
+
+#ifdef __SUNPRO_C
+#pragma align 4 (network_bluetooth_pb_data)
+#endif
+#ifdef __GNUC__
+static const guint8 network_bluetooth_pb_data[] __attribute__ ((__aligned__ (4))) =
+#else
+static const guint8 network_bluetooth_pb_data[] =
+#endif
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (1024) */
+ "\0\0\4\30"
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (64) */
+ "\0\0\0@"
+ /* width (16) */
+ "\0\0\0\20"
+ /* height (16) */
+ "\0\0\0\20"
+ /* pixel_data: */
+ "\0\0\0\0\0\0\0\0\0\0\0\0\11\13\16\13\40!\"v\"$'\265\36\40\"\344\35\36"
+ "\37\372\35\36\37\344\36\40\"\251\40\"$b\16\21\24\16\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\33\35\37\241035\350ehl\363\202"
+ "\213\223\363t}\207\362r|\207\361Zcj\362&+/\350\27\33\37\214\10\14\20"
+ "\17\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\23\24\26RCDF\363\220\227"
+ "\236\367j}\221\373y\214\237\377\375\376\376\377r\206\232\377B[t\376H"
+ "Xh\367#'+\356\23\27\34q\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\25\27\30,\34"
+ "\36!\314\215\221\226\373m\201\225\3774Pn\377f|\222\377\375\376\376\377"
+ "\375\376\376\377q\205\231\377>Yt\377;IW\372\26\32\35\333\40!\":\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\36\40!|KOQ\346\227\243\257\374Ib}\377@[w\377g}\222"
+ "\377\375\376\376\377\357\362\364\377\375\376\376\377m\203\227\377;Ri"
+ "\377\32#-\360\40!\"\215\0\0\0\0\0\0\0\0\0\0\0\0\37!\"\254\202\207\213"
+ "\352\\r\211\375\375\376\376\377\307\317\327\377|\217\242\377\375\376"
+ "\376\377g|\222\377\343\350\353\377\375\376\376\377C^x\377\"6L\362\36"
+ "\37\40\266\0\0\0\0\0\0\0\0\0\0\0\0\37\40\"\302\221\227\232\357G`{\376"
+ "`v\216\377\375\376\376\377\341\346\352\377\375\376\376\377\217\236\256"
+ "\377\375\376\376\377\254\270\304\3779Uq\377!5J\362\35\36\37\347\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\36\37!\353\212\215\221\3655Qn\3762Om\377Sk\204\377"
+ "\375\376\376\377\375\376\376\377\375\376\376\377\263\276\311\3773Pn\377"
+ "1Nl\377\"6K\366\31\33\35\347\0\0\0\0\0\0\0\0\0\0\0\0\36\37\40\360\210"
+ "\214\217\3643Ol\3761Nl\3779Tq\377\251\265\301\377\375\376\376\377\375"
+ "\376\376\377aw\215\3771Nl\3771Nl\377#6J\367\27\30\32\3361Nl\1\0\0\0\0"
+ "\0\0\0\0\36\40!\301y|\201\3624Pl\376=Yu\377\214\234\255\377\375\376\376"
+ "\377\375\376\376\377\370\372\373\377\375\376\376\377Xo\207\3772Om\377"
+ "%8M\363\34\37\40\307\0\0\0\0\0\0\0\0\0\0\0\0\37!\"\244\202\205\212\361"
+ "A[v\375\216\236\256\377\375\376\376\377\312\322\331\377\375\376\376\377"
+ "g|\222\377\363\365\367\377\375\376\376\377@Zv\377#3E\363\37\40#\261\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\35\37\"m]ac\361dw\213\376\230\247\266\377\253"
+ "\267\303\377q\205\232\377\375\376\376\377\254\267\303\377\375\376\376"
+ "\377\261\273\306\3774Ni\377\35$+\370\"'(\233\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\22\31\40\25""048\336ku\177\373Jc}\377;Wt\377f|\221\377\375\376\376\377"
+ "\375\376\376\377\240\256\274\3776Ro\377!1B\376\35\37(\304!%(6\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\1\30\35#V<=B\364jw\205\3728Tp\377g}\223\377\375"
+ "\376\376\377\235\253\270\3778Tq\377(>U\374\35%*\365\30\"+|\20\32$\3\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\20\32$\3\26\35$\225>BG\360Yaj\371n}\214"
+ "\372\202\216\233\3740Ha\372$0=\372\35'-\364\24\40-\234\23\37+\31\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\14\23\32\32\31\37%e\36\40"
+ "&\235\35\37$\310\33\35\37\372\35\36\"\337\36#(\264\32\36#\220\16\26\36"
+ "&\20\32$\3\0\0\0\0\0\0\0\0\0\0\0\0"};
+
+
+/* GdkPixbuf RGBA C-Source image dump */
+
+#ifdef __SUNPRO_C
+#pragma align 4 (network_usb_pb_data)
+#endif
+#ifdef __GNUC__
+static const guint8 network_usb_pb_data[] __attribute__ ((__aligned__ (4))) =
+#else
+static const guint8 network_usb_pb_data[] =
+#endif
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (1024) */
+ "\0\0\4\30"
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (64) */
+ "\0\0\0@"
+ /* width (16) */
+ "\0\0\0\20"
+ /* height (16) */
+ "\0\0\0\20"
+ /* pixel_data: */
+ "\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223"
+ "\0]_[\344mok\340qso\340uwr\340xzv\340cea\344\223\223\223\0\223\223\223"
+ "\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223"
+ "\0\223\223\223\0\223\223\223\0\223\223\223\0suq\340\243\245\241\377\254"
+ "\255\251\377\265\266\262\377\275\276\273\377\205\207\203\340\223\223"
+ "\223\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223"
+ "\223\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223\0|~z\340"
+ "UWS\377\302\302\277\377\312\313\310\377UWS\377\217\220\214\340\223\223"
+ "\223\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223"
+ "\223\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223\0\205\207"
+ "\203\340\317\317\314\377\246\250\244\377\254\255\251\377\351\351\347"
+ "\377\226\227\224\340\223\223\223\0\223\223\223\0\223\223\223\0\223\223"
+ "\223\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223"
+ "\223\0\223\223\223\0\217\220\214\340\344\344\342\377\355\355\353\377"
+ "\355\355\353\377\355\355\353\377\226\227\224\340\223\223\223\0\223\223"
+ "\223\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223"
+ "\223\0\223\223\223\0\223\223\223\0\"M\2125>[\201\364Lh\217\363Lh\217"
+ "\363Lh\217\363Lh\217\363>[\201\364\"M\2125\223\223\223\0\223\223\223"
+ "\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223"
+ "\0\223\223\223\0'S\221\3254e\244\3774e\244\3774e\244\3774e\244\3774e"
+ "\244\3774e\244\377'S\221\325\223\223\223\0\223\223\223\0\223\223\223"
+ "\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223"
+ "\0)V\223\3374e\244\3774e\244\3774e\244\3774e\244\3774e\244\3774e\244"
+ "\377)V\223\337\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223"
+ "\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223\0)V\223\337"
+ "4e\244\3774e\244\3774e\244\3774e\244\3774e\244\3774e\244\377)V\223\337"
+ "\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223"
+ "\0\223\223\223\0\223\223\223\0\223\223\223\0)V\223\3374e\244\3774e\244"
+ "\3774e\244\3774e\244\3774e\244\3774e\244\377)V\223\337\223\223\223\0"
+ "\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223"
+ "\0\223\223\223\0\223\223\223\0)V\223\3374e\244\3774e\244\3774e\244\377"
+ "4e\244\3774e\244\3774e\244\377)V\223\337\223\223\223\0\223\223\223\0"
+ "\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223"
+ "\0\223\223\223\0)V\223\3374e\244\3774e\244\3774e\244\3774e\244\3774e"
+ "\244\3774e\244\377)V\223\337\223\223\223\0\223\223\223\0\223\223\223"
+ "\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223"
+ "\0)V\223\3374e\244\3774e\244\3774e\244\3774e\244\3774e\244\3774e\244"
+ "\377)V\223\337\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223"
+ "\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223\0)V\223\337"
+ "4e\244\3774e\244\3774e\244\3774e\244\3774e\244\3774e\244\377)V\223\337"
+ "\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223"
+ "\0\223\223\223\0\223\223\223\0\223\223\223\0(U\223\3354e\244\3774e\244"
+ "\3774e\244\3774e\244\3774e\244\3774e\244\377(U\223\335\223\223\223\0"
+ "\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223\0\223\223\223"
+ "\0\223\223\223\0\223\223\223\0%Q\216\243(U\223\335)V\224\337)V\224\337"
+ ")V\224\337)V\224\337(U\223\335%Q\216\243\223\223\223\0\223\223\223\0"
+ "\223\223\223\0\223\223\223\0"};
+
+
+/* GdkPixbuf RGBA C-Source image dump */
+
+#ifdef __SUNPRO_C
+#pragma align 4 (network_wired_pb_data)
+#endif
+#ifdef __GNUC__
+static const guint8 network_wired_pb_data[] __attribute__ ((__aligned__ (4))) =
+#else
+static const guint8 network_wired_pb_data[] =
+#endif
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (1024) */
+ "\0\0\4\30"
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (64) */
+ "\0\0\0@"
+ /* width (16) */
+ "\0\0\0\20"
+ /* height (16) */
+ "\0\0\0\20"
+ /* pixel_data: */
+ "\0\0\0\0\216\221\213a\213\214\210\366\211\213\206\376\211\213\206\376"
+ "\211\213\206\376\211\213\206\376\211\213\206\376\211\213\206\376\211"
+ "\213\206\376\211\213\206\376\211\213\206\376\211\213\206\376\211\213"
+ "\206\376\213\214\210\366\216\221\213a\0\0\0\0\214\216\211\363\361\361"
+ "\360\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\377\377\377\377\377\377\377\377\377\377\360\360\357\377\213"
+ "\215\211\364\0\0\0\0\211\213\206\376\376\376\376\377\357\357\355\377"
+ "\356\356\354\377\356\356\354\377\355\355\353\377\355\355\353\377\354"
+ "\355\352\377\354\354\352\377\354\354\351\377\353\354\351\377\353\353"
+ "\351\377\354\354\351\377\376\376\376\377\211\213\206\376\0\0\0\0\211"
+ "\213\206\376\377\377\377\377\355\355\353\377\354\355\352\377\354\354"
+ "\352\377\360\360\356\377\363\364\362\377\364\364\362\377\364\364\362"
+ "\377\357\357\355\377\352\352\350\377\351\352\347\377\351\352\347\377"
+ "\377\377\377\377\211\213\206\376\0\0\0\0\211\213\206\376\377\377\377"
+ "\377\353\354\351\377\357\357\356\377\364\364\362\377\350\351\346\377"
+ "\215\217\212\377\210\212\205\377\213\215\210\377\351\352\347\377\364"
+ "\364\363\377\356\357\354\377\350\351\345\377\377\377\377\377\211\213"
+ "\206\376\0\0\0\0\211\213\206\376\377\377\377\377\352\353\350\377\364"
+ "\364\362\377\214\216\211\377\210\212\205\377JON\377.46\377JOO\377\233"
+ "\236\230\377\254\257\250\377\364\365\363\377\346\347\344\377\377\377"
+ "\377\377\211\213\206\376\0\0\0\0\211\213\206\376\377\377\377\377\351"
+ "\351\346\377\364\364\363\377\210\212\205\377.46\377.46\377.46\377.46"
+ "\377.46\377\272\275\266\377\365\365\364\377\345\346\342\377\377\377\377"
+ "\377\211\213\206\376]^[\326VXS\377VXT\377VXT\377VXT\377VXS\377UWS\377"
+ "Y[X\377CGE\377CGE\377CGE\377\272\275\266\377\365\366\364\377\343\345"
+ "\341\377\377\377\377\377\211\213\206\376\\_Z\350\327\327\326\377\377"
+ "\377\377\377\314\316\311\377\323\325\321\377\366\366\365\377\366\367"
+ "\366\377VXS\377UWS\377\260\247Q\377UWS\377\272\275\266\377\366\367\365"
+ "\377\342\344\337\377\377\377\377\377\211\213\206\376UWS\373\260\261\257"
+ "\377\210\212\205\377\242\243\237\377\352\352\350\377\366\367\366\377"
+ "\324\326\322\377VXT\377\272\275\266\377\272\275\266\377\272\275\266\377"
+ "\275\300\271\377\367\370\366\377\341\342\336\377\377\377\377\377\211"
+ "\213\206\376UWS\376\355\355\354\377\262\302\324\377\254\275\323\377\355"
+ "\355\354\377\311\314\307\377\337\340\335\377VXT\377\367\370\366\377\367"
+ "\370\367\377\367\370\367\377\370\370\367\377\354\355\352\377\337\341"
+ "\334\377\377\377\377\377\211\213\206\376UWT\376y\230\277\377O{\261\377"
+ "<l\250\377\254\275\323\377\323\325\321\377\261\262\260\377qtp\377\340"
+ "\342\335\377\337\341\334\377\337\341\334\377\337\341\333\377\336\340"
+ "\333\377\337\341\335\377\376\376\376\377\211\213\206\376Cc\211\377Q}"
+ "\263\377\226\266\332\377Oz\261\377\262\301\325\377\263\265\261\377oq"
+ "n\377\355\355\355\377\377\377\377\377\377\377\377\377\377\377\377\377"
+ "\377\377\377\377\377\377\377\377\376\376\376\377\351\353\350\377\212"
+ "\215\210\364Kw\255\375\226\266\332\377Kv\254\377Cc\212\377VXU\377efb"
+ "\377\204\205\201\376\211\213\206\376\211\213\206\376\211\213\206\376"
+ "\211\213\206\376\211\213\206\376\211\213\206\376\211\213\206\376\213"
+ "\214\210\366\216\221\213a\226\266\332\377Jx\261\3649i\247\245\0\0\377"
+ "\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0Gu\256\3559i\247\245\0\0\377\1\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0"};
+
+
+/* GdkPixbuf RGBA C-Source image dump */
+
+#ifdef __SUNPRO_C
+#pragma align 4 (network_wireless_pb_data)
+#endif
+#ifdef __GNUC__
+static const guint8 network_wireless_pb_data[] __attribute__ ((__aligned__ (4))) =
+#else
+static const guint8 network_wireless_pb_data[] =
+#endif
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (1024) */
+ "\0\0\4\30"
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (64) */
+ "\0\0\0@"
+ /* width (16) */
+ "\0\0\0\20"
+ /* height (16) */
+ "\0\0\0\20"
+ /* pixel_data: */
+ "\0\0\0\0""9c\252\22""5j\252\30\0\0\0\0\0\0\0\0q\252\306\11q\234\325\22"
+ "t\242\321\26t\242\321\26q\234\325\22q\252\306\11\0\0\0\0\0\0\0\0""5j"
+ "\252\30""9c\252\22\0\0\0\0+U\252\6""2d\241.\0\200\200\2\200\237\277\10"
+ "f\231\304\36b\215\311/q\242\3164o\242\3217o\242\3217q\242\3164b\215\311"
+ "/f\231\304\36\200\237\277\10\0\200\200\2""2d\241.+U\252\6""2c\252$0h"
+ "\247\40t\242\321\13d\225\307)V\206\274Ps\232\315Gs\237\317Pq\240\320"
+ "Vq\240\320Vs\237\317Ps\232\315GV\206\274Pd\225\307)t\242\321\13""0h\247"
+ "\40""2c\252$5g\246HKx\264\21s\234\316\37Q\201\267_e\222\304[q\236\320"
+ "af\202\231\245\206\207\206\375^b_\373f\203\235\242q\236\320ae\222\304"
+ "[Q\201\267_s\234\316\37Kx\264\21""5g\246H5e\244eh\242\321\26r\241\320"
+ "1Jx\264\216q\240\320fq\236\316\214fhh\375\373\373\372\377\320\320\316"
+ "\377`dc\374q\236\316\214q\240\320fJx\264\216r\241\3201h\242\321\26""5"
+ "e\244e3e\245wm\240\314#q\236\316\77Fv\260\253q\235\315zq\236\314\256"
+ "^a`\377\322\323\320\377\317\317\314\377`cb\377q\236\314\256q\235\315"
+ "zFv\260\253q\236\316\77m\240\314#3e\245w6f\246va\215\275:s\241\316II"
+ "y\263\253a\217\304\233y\230\266\324{\216\233\377\\`^\377]`^\377}\217"
+ "\237\377y\230\266\324a\217\304\233Iy\263\253s\241\316Ia\215\275:6f\246"
+ "v4e\246SGv\263hr\240\316N_\215\300\206Bs\254\330\201\213\222\371\270"
+ "\310\332\377\221\252\301\377\221\252\301\377\270\310\332\377\177\215"
+ "\226\365Bs\254\330_\215\300\206r\240\316NGv\263h4e\246S3f\246\24""8h"
+ "\246\247m\230\312Rq\236\320lW{\250\323Lr\243\377\255\302\333\377\252"
+ "\270\307\377\204\223\241\377\247\274\324\377Lr\243\377W{\250\323q\236"
+ "\320lm\230\312R8h\246\2473f\246\24\0\0\0\0""4d\244k>n\255\244r\237\315"
+ "e\206\214\215\345\254\271\312\377c\204\255\377Is\252\377Oy\260\377n\217"
+ "\270\377\256\273\315\377\210\212\207\371r\237\315e>n\255\2444d\244k\0"
+ "\0\0\0\0\0\0\0""3f\231\5""3e\245\244Uy\235\313\234\235\231\377\364\364"
+ "\364\377\215\216\212\376\215\220\214\377\271\273\270\377\360\360\357"
+ "\377\377\377\377\377\234\235\231\377Uy\235\3133e\245\2443f\231\5\0\0"
+ "\0\0\0\0\0\0\0\0\0\0+U\252\6\205\211\206\367\326\326\325\377\327\330"
+ "\326\377\333\333\332\377\370\371\370\377\333\333\332\377\263\264\261"
+ "\377\303\305\303\377\324\324\323\377z\204\212\340+U\252\6\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\211\211\2046\226\230\224\377\377\377\377\377\373"
+ "\373\372\377\353\353\353\377\323\324\322\377\300\301\276\377\260\261"
+ "\256\377\246\250\244\377\366\366\366\377\226\230\224\377\211\211\206"
+ "N\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\210\212\205\257\315\316\314\377\310"
+ "\310\306\377\230\232\226\377\250\252\246\377\270\271\266\377\310\311"
+ "\307\377\330\330\327\377\350\350\347\377\374\374\373\377\336\337\335"
+ "\377\210\212\205\360\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\210\212\205\355"
+ "\352\352\351\377\223\225\220\375\210\214\205I\210\210\205G\210\212\206"
+ "i\210\212\204\213\210\213\205\255\207\213\205\327\223\225\220\377\347"
+ "\350\347\377\210\212\205\377\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\211\213"
+ "\205{\210\212\205\356\211\213\205\177\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\207\212\205q\210\212\205\356\211\213\205{\0\0\0\0\0"
+ "\0\0\0"};
+
+
diff --git a/ui/gtk/new_packet_list.c b/ui/gtk/new_packet_list.c
new file mode 100644
index 0000000000..d8df054b10
--- /dev/null
+++ b/ui/gtk/new_packet_list.c
@@ -0,0 +1,1747 @@
+/* new_packet_list.c
+ * Routines to implement a new GTK2 packet list using our custom model
+ * Copyright 2008-2009, Stephen Fisher (see AUTHORS file)
+ * Co-authors Anders Broman, Kovarththanan Rajaratnam and Stig Bjorlykke.
+ *
+ * $Id$
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "string.h"
+
+#include <stdio.h>
+#include <gtk/gtk.h>
+#include <glib.h>
+
+#include "gui_utils.h"
+#include "packet_list_store.h"
+#include "ui/gtk/new_packet_list.h"
+#include "epan/column_info.h"
+#include "epan/prefs.h"
+#include <epan/packet.h>
+#include <epan/epan_dissect.h>
+#include "../ui_util.h"
+#include "../progress_dlg.h"
+#include "../simple_dialog.h"
+#include "../main_statusbar.h"
+#include "epan/emem.h"
+#include "globals.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/font_utils.h"
+#include "ui/gtk/packet_history.h"
+#include "epan/column.h"
+#include "epan/strutil.h"
+#include "ui/gtk/recent.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/menus.h"
+#include "color.h"
+#include "color_filters.h"
+#include "ui/gtk/color_utils.h"
+#include "ui/gtk/capture_file_dlg.h"
+#include "ui/gtk/packet_win.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/prefs_column.h"
+#include "ui/gtk/prefs_dlg.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/filter_autocomplete.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+#define COLUMN_WIDTH_MIN 40
+
+#define COL_EDIT_COLUMN "column"
+#define COL_EDIT_FORMAT_CMB "format_cmb"
+#define COL_EDIT_TITLE_TE "title_te"
+#define COL_EDIT_FIELD_LB "field_lb"
+#define COL_EDIT_FIELD_TE "field_te"
+#define COL_EDIT_OCCURRENCE_LB "occurrence_lb"
+#define COL_EDIT_OCCURRENCE_TE "occurrente_te"
+
+static PacketList *packetlist;
+static gboolean last_at_end = FALSE;
+static gboolean enable_color;
+static gulong column_changed_handler_id;
+
+static GtkWidget *create_view_and_model(void);
+static void scroll_to_and_select_iter(GtkTreeModel *model, GtkTreeSelection *selection, GtkTreeIter *iter);
+static void new_packet_list_select_cb(GtkTreeView *tree_view, gpointer data _U_);
+static void new_packet_list_double_click_cb(GtkTreeView *treeview,
+ GtkTreePath *path _U_,
+ GtkTreeViewColumn *col _U_,
+ gpointer userdata _U_);
+static void show_cell_data_func(GtkTreeViewColumn *col,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data);
+static gint row_number_from_iter(GtkTreeIter *iter);
+static void scroll_to_current(void);
+
+void new_packet_list_set_sel_browse(gboolean val, gboolean force_set);
+
+GtkWidget *
+new_packet_list_create(void)
+{
+ GtkWidget *view, *scrollwin;
+
+ scrollwin = scrolled_window_new(NULL, NULL);
+
+ view = create_view_and_model();
+
+ new_packet_list_set_sel_browse(prefs.gui_plist_sel_browse, FALSE);
+
+ gtk_container_add(GTK_CONTAINER(scrollwin), view);
+
+ g_object_set_data(G_OBJECT(popup_menu_object), E_MPACKET_LIST_KEY, view);
+
+ return scrollwin;
+}
+
+/** @todo XXX - implement a smarter solution for recreating the packet list */
+void
+new_packet_list_recreate(void)
+{
+ g_signal_handler_block(packetlist->view, column_changed_handler_id);
+ gtk_widget_destroy(pkt_scrollw);
+
+ prefs.num_cols = g_list_length(prefs.col_list);
+
+ build_column_format_array(&cfile.cinfo, prefs.num_cols, FALSE);
+
+ pkt_scrollw = new_packet_list_create();
+ gtk_widget_show_all(pkt_scrollw);
+
+ main_widgets_rearrange();
+
+ if(cfile.state != FILE_CLOSED)
+ redissect_packets();
+}
+
+guint
+new_packet_list_append(column_info *cinfo _U_, frame_data *fdata, packet_info *pinfo _U_)
+{
+ /* fdata should be filled with the stuff we need
+ * strings are built at display time.
+ */
+ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(packetlist->view));
+ guint visible_pos = packet_list_append_record(packetlist, fdata);
+ if(model)
+ /* If the model is connected there is no packetsbar_update from "thaw */
+ packets_bar_update();
+ /* Return the _visible_ position */
+
+ return visible_pos;
+}
+
+static gboolean
+right_justify_column (gint col)
+{
+ header_field_info *hfi;
+ gboolean right_justify = FALSE;
+
+ switch (cfile.cinfo.col_fmt[col]) {
+
+ case COL_NUMBER:
+ case COL_PACKET_LENGTH:
+ case COL_CUMULATIVE_BYTES:
+ case COL_DCE_CALL:
+ case COL_DSCP_VALUE:
+ right_justify = TRUE;
+ break;
+
+ case COL_CUSTOM:
+ hfi = proto_registrar_get_byname(cfile.cinfo.col_custom_field[col]);
+ /* Check if this is a valid field and we have no strings lookup table */
+ if ((hfi != NULL) && ((hfi->strings == NULL) || !get_column_resolved(col))) {
+ /* Check for bool, framenum and decimal/octal integer types */
+ if ((hfi->type == FT_BOOLEAN) || (hfi->type == FT_FRAMENUM) ||
+ (((hfi->display == BASE_DEC) || (hfi->display == BASE_OCT)) &&
+ (IS_FT_INT(hfi->type) || IS_FT_UINT(hfi->type)))) {
+ right_justify = TRUE;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return right_justify;
+}
+
+static gboolean
+resolve_column (gint col)
+{
+ header_field_info *hfi;
+ gboolean resolve = FALSE;
+
+ switch (cfile.cinfo.col_fmt[col]) {
+
+ case COL_CUSTOM:
+ hfi = proto_registrar_get_byname(cfile.cinfo.col_custom_field[col]);
+ /* Check if this is a valid field */
+ if (hfi != NULL) {
+ /* Check if we have an OID or a strings table with integer values */
+ if ((hfi->type == FT_OID) ||
+ ((hfi->strings != NULL) &&
+ ((hfi->type == FT_BOOLEAN) || (hfi->type == FT_FRAMENUM) ||
+ IS_FT_INT(hfi->type) || IS_FT_UINT(hfi->type)))) {
+ resolve = TRUE;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return resolve;
+}
+
+static void
+col_title_change_ok (GtkWidget *w, gpointer parent_w)
+{
+ GtkTreeViewColumn *col;
+ const gchar *title, *name, *occurrence_text;
+ gint col_id, cur_fmt, occurrence, col_width;
+ gchar *escaped_title;
+ gboolean recreate = FALSE;
+
+ col = g_object_get_data (G_OBJECT(w), COL_EDIT_COLUMN);
+ col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(col), E_MPACKET_LIST_COL_KEY));
+
+ title = gtk_entry_get_text(GTK_ENTRY(g_object_get_data (G_OBJECT(w), COL_EDIT_TITLE_TE)));
+ name = gtk_entry_get_text(GTK_ENTRY(g_object_get_data (G_OBJECT(w), COL_EDIT_FIELD_TE)));
+ cur_fmt = gtk_combo_box_get_active(GTK_COMBO_BOX(g_object_get_data (G_OBJECT(w), COL_EDIT_FORMAT_CMB)));
+ occurrence_text = gtk_entry_get_text(GTK_ENTRY(g_object_get_data (G_OBJECT(w), COL_EDIT_OCCURRENCE_TE)));
+ occurrence = (gint)strtol(occurrence_text, NULL, 10);
+
+ escaped_title = ws_strdup_escape_char(title, '_');
+ gtk_tree_view_column_set_title(col, escaped_title);
+ g_free(escaped_title);
+
+ if (strcmp (title, get_column_title(col_id)) != 0) {
+ set_column_title (col_id, title);
+ }
+
+ if (cur_fmt != get_column_format(col_id)) {
+ set_column_format (col_id, cur_fmt);
+ recreate = TRUE;
+ }
+
+ if (cur_fmt == COL_CUSTOM) {
+ if (strcmp (name, get_column_custom_field(col_id)) != 0) {
+ set_column_custom_field (col_id, name);
+ recreate = TRUE;
+ }
+
+ if (occurrence != get_column_custom_occurrence(col_id)) {
+ set_column_custom_occurrence (col_id, occurrence);
+ recreate = TRUE;
+ }
+ }
+
+ col_width = get_default_col_size (packetlist->view, title);
+ gtk_tree_view_column_set_min_width(col, col_width);
+
+ if (!prefs.gui_use_pref_save) {
+ prefs_main_write();
+ }
+
+ rebuild_visible_columns_menu ();
+
+ if (recreate) {
+ new_packet_list_recreate();
+ }
+
+ new_packet_list_resize_column (col_id);
+ window_destroy(GTK_WIDGET(parent_w));
+}
+
+static void
+col_title_change_cancel (GtkWidget *w _U_, gpointer parent_w)
+{
+ window_destroy(GTK_WIDGET(parent_w));
+}
+
+static void
+col_details_format_changed_cb(GtkWidget *w, gpointer data _U_)
+{
+ GtkWidget *field_lb, *field_te, *occurrence_lb, *occurrence_te;
+ gint cur_fmt;
+
+ field_lb = g_object_get_data (G_OBJECT(w), COL_EDIT_FIELD_LB);
+ field_te = g_object_get_data (G_OBJECT(w), COL_EDIT_FIELD_TE);
+ occurrence_lb = g_object_get_data (G_OBJECT(w), COL_EDIT_OCCURRENCE_LB);
+ occurrence_te = g_object_get_data (G_OBJECT(w), COL_EDIT_OCCURRENCE_TE);
+
+ cur_fmt = gtk_combo_box_get_active(GTK_COMBO_BOX(w));
+
+ if (cur_fmt == COL_CUSTOM) {
+ /* Changing to custom */
+ gtk_widget_set_sensitive(field_lb, TRUE);
+ gtk_widget_set_sensitive(field_te, TRUE);
+ gtk_widget_set_sensitive(occurrence_lb, TRUE);
+ gtk_widget_set_sensitive(occurrence_te, TRUE);
+ } else {
+ /* Changing to non-custom */
+ gtk_widget_set_sensitive(field_lb, FALSE);
+ gtk_widget_set_sensitive(field_te, FALSE);
+ gtk_widget_set_sensitive(occurrence_lb, FALSE);
+ gtk_widget_set_sensitive(occurrence_te, FALSE);
+ }
+}
+
+static void
+col_details_edit_dlg (gint col_id, GtkTreeViewColumn *col)
+{
+ const gchar *title = gtk_tree_view_column_get_title(col);
+ gchar *unescaped_title = ws_strdup_unescape_char(title, '_');
+
+ GtkWidget *label, *field_lb, *occurrence_lb;
+ GtkWidget *title_te, *format_cmb, *field_te, *occurrence_te;
+ GtkWidget *win, *main_tb, *main_vb, *bbox, *cancel_bt, *ok_bt;
+ char custom_occurrence_str[8];
+ gint cur_fmt, i;
+
+ win = dlg_window_new("Wireshark: Edit Column Details");
+
+ gtk_window_set_resizable(GTK_WINDOW(win),FALSE);
+ gtk_window_resize(GTK_WINDOW(win), 400, 100);
+
+ main_vb = gtk_vbox_new(FALSE, 6);
+ gtk_container_add(GTK_CONTAINER(win), main_vb);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 6);
+
+ main_tb = gtk_table_new(2, 4, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 10);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 5);
+
+ label = gtk_label_new(ep_strdup_printf("Title:"));
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, 0, 1);
+ gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
+ gtk_widget_set_tooltip_text(label, "Packet list column title.");
+
+ title_te = gtk_entry_new();
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), title_te, 1, 2, 0, 1);
+ gtk_entry_set_text(GTK_ENTRY(title_te), unescaped_title);
+ g_free(unescaped_title);
+ gtk_widget_set_tooltip_text(title_te, "Packet list column title.");
+
+ label = gtk_label_new(ep_strdup_printf("Field type:"));
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, 1, 2);
+ gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
+ gtk_widget_set_tooltip_text(label, "Select which packet information to present in the column.");
+
+ format_cmb = gtk_combo_box_text_new();
+ for (i = 0; i < NUM_COL_FMTS; i++) {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(format_cmb), col_format_desc(i));
+ }
+ g_signal_connect(format_cmb, "changed", G_CALLBACK(col_details_format_changed_cb), NULL);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), format_cmb, 1, 2, 1, 2);
+ gtk_widget_set_tooltip_text(format_cmb, "Select which packet information to present in the column.");
+
+ field_lb = gtk_label_new(ep_strdup_printf("Field name:"));
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), field_lb, 0, 1, 2, 3);
+ gtk_misc_set_alignment(GTK_MISC(field_lb), 1.0f, 0.5f);
+ gtk_widget_set_tooltip_text(field_lb,
+ "Field name used when field type is \"Custom\". "
+ "This string has the same syntax as a display filter string.");
+ field_te = gtk_entry_new();
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), field_te, 1, 2, 2, 3);
+ g_object_set_data (G_OBJECT(field_te), E_FILT_FIELD_NAME_ONLY_KEY, "");
+ g_signal_connect(field_te, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
+ g_signal_connect(field_te, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
+ g_signal_connect(win, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
+ gtk_widget_set_tooltip_text(field_te,
+ "Field name used when field type is \"Custom\". "
+ "This string has the same syntax as a display filter string.");
+
+ occurrence_lb = gtk_label_new(ep_strdup_printf("Occurrence:"));
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), occurrence_lb, 0, 1, 3, 4);
+ gtk_misc_set_alignment(GTK_MISC(occurrence_lb), 1.0f, 0.5f);
+ gtk_widget_set_tooltip_text (occurrence_lb,
+ "Field occurence to use. "
+ "0=all (default), 1=first, 2=second, ..., -1=last.");
+
+ occurrence_te = gtk_entry_new();
+ gtk_entry_set_max_length (GTK_ENTRY(occurrence_te), 4);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), occurrence_te, 1, 2, 3, 4);
+ gtk_widget_set_tooltip_text (occurrence_te,
+ "Field occurence to use. "
+ "0=all (default), 1=first, 2=second, ..., -1=last.");
+
+ bbox = dlg_button_row_new(GTK_STOCK_CANCEL,GTK_STOCK_OK, NULL);
+ gtk_box_pack_end(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+
+ ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ g_object_set_data (G_OBJECT(ok_bt), COL_EDIT_COLUMN, col);
+ g_object_set_data (G_OBJECT(ok_bt), COL_EDIT_FORMAT_CMB, format_cmb);
+ g_object_set_data (G_OBJECT(ok_bt), COL_EDIT_TITLE_TE, title_te);
+ g_object_set_data (G_OBJECT(ok_bt), COL_EDIT_FIELD_TE, field_te);
+ g_object_set_data (G_OBJECT(ok_bt), COL_EDIT_OCCURRENCE_TE, occurrence_te);
+ g_object_set_data (G_OBJECT(format_cmb), COL_EDIT_FIELD_LB, field_lb);
+ g_object_set_data (G_OBJECT(format_cmb), COL_EDIT_FIELD_TE, field_te);
+ g_object_set_data (G_OBJECT(format_cmb), COL_EDIT_OCCURRENCE_LB, occurrence_lb);
+ g_object_set_data (G_OBJECT(format_cmb), COL_EDIT_OCCURRENCE_TE, occurrence_te);
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(col_title_change_ok), win);
+
+ cur_fmt = get_column_format (col_id);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(format_cmb), cur_fmt);
+ if (cur_fmt == COL_CUSTOM) {
+ gtk_entry_set_text(GTK_ENTRY(field_te), get_column_custom_field(col_id));
+ g_snprintf(custom_occurrence_str, sizeof(custom_occurrence_str), "%d", get_column_custom_occurrence(col_id));
+ gtk_entry_set_text(GTK_ENTRY(occurrence_te), custom_occurrence_str);
+ }
+
+ dlg_set_activate(title_te, ok_bt);
+ dlg_set_activate(field_te, ok_bt);
+ dlg_set_activate(occurrence_te, ok_bt);
+
+ cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ g_signal_connect(cancel_bt, "clicked", G_CALLBACK(col_title_change_cancel), win);
+ window_set_cancel_button(win, cancel_bt, NULL);
+
+ gtk_widget_grab_default(ok_bt);
+ gtk_widget_show_all(win);
+}
+
+/* Process sort request;
+ * order: GTK_SORT_ASCENDING or GTK_SORT_DESCENDING
+ * sort_indicator: TRUE: set sort_indicator on column; FALSE: don't set ....
+ *
+ * If necessary, columns are first "columnized" for all rows in the packet-list; If this
+ * is not completed (i.e., stopped), then the sort request is aborted.
+ */
+static void
+new_packet_list_sort_column (gint col_id, GtkTreeViewColumn *col, GtkSortType order, gboolean sort_indicator)
+{
+ GtkTreeViewColumn *prev_col;
+
+ if (col == NULL) {
+ col = gtk_tree_view_get_column(GTK_TREE_VIEW(packetlist->view), col_id);
+ }
+ g_assert(col);
+
+ if (!packet_list_do_packet_list_dissect_and_cache_all(packetlist, col_id)) {
+ return; /* "stopped": do not try to sort */
+ }
+
+ prev_col = (GtkTreeViewColumn *)
+ g_object_get_data(G_OBJECT(packetlist->view), E_MPACKET_LIST_PREV_COLUMN_KEY);
+
+ if (prev_col) {
+ gtk_tree_view_column_set_sort_indicator(prev_col, FALSE);
+ }
+ gtk_tree_view_column_set_sort_indicator(col, sort_indicator);
+ gtk_tree_view_column_set_sort_order (col, order);
+ g_object_set_data(G_OBJECT(packetlist->view), E_MPACKET_LIST_PREV_COLUMN_KEY, col);
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(packetlist), col_id, order); /* triggers sort callback */
+
+ scroll_to_current ();
+}
+
+/*
+ * We have our own functionality to toggle sort order on a column to avoid
+ * having empty sorting arrow widgets in the column header.
+ */
+static void
+new_packet_list_column_clicked_cb (GtkTreeViewColumn *col, gpointer user_data _U_)
+{
+ GtkSortType order = gtk_tree_view_column_get_sort_order (col);
+ gint col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(col), E_MPACKET_LIST_COL_KEY));
+
+ if (cfile.state == FILE_READ_IN_PROGRESS)
+ return;
+
+ if (!gtk_tree_view_column_get_sort_indicator(col)) {
+ new_packet_list_sort_column (col_id, col, GTK_SORT_ASCENDING, TRUE);
+ } else if (order == GTK_SORT_ASCENDING) {
+ new_packet_list_sort_column (col_id, col, GTK_SORT_DESCENDING, TRUE);
+ } else {
+ new_packet_list_sort_column (0, NULL, GTK_SORT_ASCENDING, FALSE);
+ }
+}
+
+static gdouble
+get_xalign_value (gchar xalign, gboolean right_justify)
+{
+ double value;
+
+ switch (xalign) {
+ case COLUMN_XALIGN_RIGHT:
+ value = 1.0f;
+ break;
+ case COLUMN_XALIGN_CENTER:
+ value = 0.5f;
+ break;
+ case COLUMN_XALIGN_LEFT:
+ value = 0.0f;
+ break;
+ case COLUMN_XALIGN_DEFAULT:
+ default:
+ if (right_justify) {
+ value = 1.0f;
+ } else {
+ value = 0.0f;
+ }
+ break;
+ }
+
+ return value;
+}
+
+static void
+new_packet_list_xalign_column (gint col_id, GtkTreeViewColumn *col, gchar xalign)
+{
+ GList *renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT(col));
+ gboolean right_justify = right_justify_column(col_id);
+ gdouble value = get_xalign_value (xalign, right_justify);
+ GList *entry;
+ GtkCellRenderer *renderer;
+
+ entry = g_list_first(renderers);
+ while (entry) {
+ renderer = (GtkCellRenderer *)entry->data;
+ g_object_set(G_OBJECT(renderer), "xalign", value, NULL);
+ entry = g_list_next (entry);
+ }
+ g_list_free (renderers);
+
+ if ((xalign == COLUMN_XALIGN_LEFT && !right_justify) ||
+ (xalign == COLUMN_XALIGN_RIGHT && right_justify)) {
+ /* Default value selected, save default in the recent settings */
+ xalign = COLUMN_XALIGN_DEFAULT;
+ }
+
+ recent_set_column_xalign (col_id, xalign);
+ gtk_widget_queue_draw (packetlist->view);
+}
+
+static void
+new_packet_list_set_visible_column (gint col_id, GtkTreeViewColumn *col, gboolean visible)
+{
+ gtk_tree_view_column_set_visible(col, visible);
+ set_column_visible(col_id, visible);
+
+ if (!prefs.gui_use_pref_save) {
+ prefs_main_write();
+ }
+
+ rebuild_visible_columns_menu ();
+ gtk_widget_queue_draw (packetlist->view);
+}
+
+void
+new_packet_list_toggle_visible_column (gint col_id)
+{
+ GtkTreeViewColumn *col =
+ gtk_tree_view_get_column(GTK_TREE_VIEW(packetlist->view), col_id);
+
+ new_packet_list_set_visible_column (col_id, col, get_column_visible(col_id) ? FALSE : TRUE);
+}
+
+void
+new_packet_list_set_all_columns_visible (void)
+{
+ GtkTreeViewColumn *col;
+ int col_id;
+
+ for (col_id = 0; col_id < cfile.cinfo.num_cols; col_id++) {
+ col = gtk_tree_view_get_column(GTK_TREE_VIEW(packetlist->view), col_id);
+ gtk_tree_view_column_set_visible(col, TRUE);
+ set_column_visible(col_id, TRUE);
+ }
+
+ if (!prefs.gui_use_pref_save) {
+ prefs_main_write();
+ }
+
+ rebuild_visible_columns_menu ();
+ gtk_widget_queue_draw (packetlist->view);
+}
+
+static void
+new_packet_list_remove_column (gint col_id, GtkTreeViewColumn *col _U_)
+{
+ column_prefs_remove(col_id);
+
+ if (!prefs.gui_use_pref_save) {
+ prefs_main_write();
+ }
+
+ new_packet_list_recreate();
+}
+
+static void
+new_packet_list_toggle_resolved (GtkWidget *w, gint col_id)
+{
+ /* We have to check for skip-update because we get an emit in menus_set_column_resolved() */
+ if (g_object_get_data(G_OBJECT(w), "skip-update") == NULL) {
+ set_column_resolved (col_id, get_column_resolved (col_id) ? FALSE : TRUE);
+
+ if (!prefs.gui_use_pref_save) {
+ prefs_main_write();
+ }
+
+ new_packet_list_recreate();
+ }
+}
+
+void
+new_packet_list_column_menu_cb (GtkWidget *w, gpointer user_data _U_, COLUMN_SELECTED_E action)
+{
+ GtkTreeViewColumn *col = (GtkTreeViewColumn *)
+ g_object_get_data(G_OBJECT(packetlist->view), E_MPACKET_LIST_COLUMN_KEY);
+ gint col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(col), E_MPACKET_LIST_COL_KEY));
+
+ switch (action) {
+ case COLUMN_SELECTED_SORT_ASCENDING:
+ new_packet_list_sort_column (col_id, col, GTK_SORT_ASCENDING, TRUE);
+ break;
+ case COLUMN_SELECTED_SORT_DESCENDING:
+ new_packet_list_sort_column (col_id, col, GTK_SORT_DESCENDING, TRUE);
+ break;
+ case COLUMN_SELECTED_SORT_NONE:
+ new_packet_list_sort_column (0, NULL, GTK_SORT_ASCENDING, FALSE);
+ break;
+ case COLUMN_SELECTED_TOGGLE_RESOLVED:
+ new_packet_list_toggle_resolved (w, col_id);
+ break;
+ case COLUMN_SELECTED_ALIGN_LEFT:
+ new_packet_list_xalign_column (col_id, col, COLUMN_XALIGN_LEFT);
+ break;
+ case COLUMN_SELECTED_ALIGN_CENTER:
+ new_packet_list_xalign_column (col_id, col, COLUMN_XALIGN_CENTER);
+ break;
+ case COLUMN_SELECTED_ALIGN_RIGHT:
+ new_packet_list_xalign_column (col_id, col, COLUMN_XALIGN_RIGHT);
+ break;
+ case COLUMN_SELECTED_ALIGN_DEFAULT:
+ new_packet_list_xalign_column (col_id, col, COLUMN_XALIGN_DEFAULT);
+ break;
+ case COLUMN_SELECTED_RESIZE:
+ new_packet_list_resize_column (col_id);
+ break;
+ case COLUMN_SELECTED_CHANGE:
+ col_details_edit_dlg (col_id, col);
+ break;
+ case COLUMN_SELECTED_HIDE:
+ new_packet_list_set_visible_column (col_id, col, FALSE);
+ break;
+ case COLUMN_SELECTED_REMOVE:
+ new_packet_list_remove_column (col_id, col);
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+}
+
+static gboolean
+new_packet_list_column_button_pressed_cb (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ GtkWidget *col = (GtkWidget *) data;
+ GtkWidget *menu = g_object_get_data(G_OBJECT(popup_menu_object), PM_PACKET_LIST_COL_KEY);
+ gint col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(col), E_MPACKET_LIST_COL_KEY));
+ gboolean right_justify = right_justify_column (col_id);
+
+ menus_set_column_align_default (right_justify);
+ menus_set_column_resolved (get_column_resolved (col_id), resolve_column (col_id));
+ g_object_set_data(G_OBJECT(packetlist->view), E_MPACKET_LIST_COLUMN_KEY, col);
+ return popup_menu_handler (widget, event, menu);
+}
+
+static void
+column_dnd_changed_cb(GtkTreeView *tree_view, gpointer data _U_)
+{
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GList *columns, *list, *clp, *new_col_list = NULL;
+ gint old_col_id, new_col_id = 0;
+ fmt_data *cfmt;
+
+ selection = gtk_tree_view_get_selection(tree_view);
+ if (!gtk_tree_selection_get_selected(selection, &model, &iter))
+ return;
+
+ list = columns = gtk_tree_view_get_columns(tree_view);
+ while (columns) {
+ column = columns->data;
+ old_col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), E_MPACKET_LIST_COL_KEY));
+
+ clp = g_list_nth (prefs.col_list, old_col_id);
+ cfmt = (fmt_data *) clp->data;
+ new_col_list = g_list_append (new_col_list, cfmt);
+ columns = g_list_next (columns);
+ new_col_id++;
+ }
+ g_list_free (list);
+ g_list_free (prefs.col_list);
+
+ prefs.col_list = new_col_list;
+
+ if (!prefs.gui_use_pref_save) {
+ prefs_main_write();
+ }
+
+ new_packet_list_recreate();
+}
+
+static GtkWidget *
+create_view_and_model(void)
+{
+ GtkTreeViewColumn *col;
+ GtkCellRenderer *renderer;
+ gint i, col_width;
+ gdouble value;
+ gchar *tooltip_text;
+ header_field_info *hfi;
+ gint col_min_width;
+ gchar *escaped_title;
+
+ packetlist = new_packet_list_new();
+
+ packetlist->view = tree_view_new(GTK_TREE_MODEL(packetlist));
+
+ gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(packetlist->view),
+ TRUE);
+ g_signal_connect(packetlist->view, "cursor-changed",
+ G_CALLBACK(new_packet_list_select_cb), NULL);
+ g_signal_connect(packetlist->view, "row-activated",
+ G_CALLBACK(new_packet_list_double_click_cb), NULL);
+ g_signal_connect(packetlist->view, "button_press_event", G_CALLBACK(popup_menu_handler),
+ g_object_get_data(G_OBJECT(popup_menu_object), PM_PACKET_LIST_KEY));
+ column_changed_handler_id = g_signal_connect(packetlist->view, "columns-changed", G_CALLBACK(column_dnd_changed_cb), NULL);
+ g_object_set_data(G_OBJECT(popup_menu_object), E_MPACKET_LIST_KEY, packetlist);
+
+ /* g_object_unref(packetlist); */ /* Destroy automatically with view for now */ /* XXX - Messes up freezing & thawing */
+
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_font(packetlist->view, user_font_get_regular());
+#else
+ gtk_widget_modify_font(packetlist->view, user_font_get_regular());
+#endif
+
+ /* We need one extra column to store the entire PacketListRecord */
+ for(i = 0; i < cfile.cinfo.num_cols; i++) {
+ renderer = gtk_cell_renderer_text_new();
+ col = gtk_tree_view_column_new();
+ gtk_tree_view_column_pack_start(col, renderer, TRUE);
+ value = get_xalign_value(recent_get_column_xalign(i), right_justify_column(i));
+ g_object_set(G_OBJECT(renderer),
+ "xalign", value,
+ NULL);
+ g_object_set(renderer,
+ "ypad", 0,
+ NULL);
+ gtk_tree_view_column_set_cell_data_func(col, renderer,
+ show_cell_data_func,
+ GINT_TO_POINTER(i),
+ NULL);
+ if (cfile.cinfo.col_fmt[i] == COL_CUSTOM) {
+ hfi = proto_registrar_get_byname(cfile.cinfo.col_custom_field[i]);
+ /* Check if this is a valid custom_field */
+ if (hfi != NULL) {
+ if (hfi->parent != -1) {
+ /* Prefix with protocol name */
+ if (cfile.cinfo.col_custom_occurrence[i] != 0) {
+ tooltip_text = g_strdup_printf("%s\n%s (%s#%d)", proto_get_protocol_name(hfi->parent), hfi->name, hfi->abbrev, cfile.cinfo.col_custom_occurrence[i]);
+ } else {
+ tooltip_text = g_strdup_printf("%s\n%s (%s)", proto_get_protocol_name(hfi->parent), hfi->name, hfi->abbrev);
+ }
+ } else {
+ tooltip_text = g_strdup_printf("%s (%s)", hfi->name, hfi->abbrev);
+ }
+ } else {
+ tooltip_text = g_strdup_printf("Unknown Field: %s", get_column_custom_field(i));
+ }
+ } else {
+ tooltip_text = g_strdup(col_format_desc(cfile.cinfo.col_fmt[i]));
+ }
+ escaped_title = ws_strdup_escape_char(cfile.cinfo.col_title[i], '_');
+ gtk_tree_view_column_set_title(col, escaped_title);
+ g_free (escaped_title);
+ gtk_tree_view_column_set_clickable(col, TRUE);
+ gtk_tree_view_column_set_resizable(col, TRUE);
+ gtk_tree_view_column_set_visible(col, get_column_visible(i));
+ gtk_tree_view_column_set_sizing(col,GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_reorderable(col, TRUE); /* XXX - Should this be saved in the prefs? */
+
+ g_object_set_data(G_OBJECT(col), E_MPACKET_LIST_COL_KEY, GINT_TO_POINTER(i));
+ g_signal_connect(col, "clicked", G_CALLBACK(new_packet_list_column_clicked_cb), NULL);
+
+ /*
+ * The column can't be adjusted to a size smaller than this
+ * XXX The minimum size will be the size of the title
+ * should that be limited for long titles?
+ */
+ col_min_width = get_default_col_size (packetlist->view, cfile.cinfo.col_title[i]);
+ if(col_min_width<COLUMN_WIDTH_MIN){
+ gtk_tree_view_column_set_min_width(col, COLUMN_WIDTH_MIN);
+ }else{
+ gtk_tree_view_column_set_min_width(col, col_min_width);
+ }
+
+ /* Set the size the column will be displayed with */
+ col_width = recent_get_column_width(i);
+ if(col_width < 1) {
+ gint fmt;
+ const gchar *long_str;
+
+ fmt = get_column_format(i);
+ long_str = get_column_width_string(fmt, i);
+ if(long_str){
+ col_width = get_default_col_size (packetlist->view, long_str);
+ }else{
+ col_width = COLUMN_WIDTH_MIN;
+ }
+ gtk_tree_view_column_set_fixed_width(col, col_width);
+ }else{
+ gtk_tree_view_column_set_fixed_width(col, col_width);
+ }
+
+ gtk_tree_view_append_column(GTK_TREE_VIEW(packetlist->view), col);
+
+ gtk_widget_set_tooltip_text(gtk_tree_view_column_get_button(col), tooltip_text);
+ g_free(tooltip_text);
+ g_signal_connect(gtk_tree_view_column_get_button(col), "button_press_event",
+ G_CALLBACK(new_packet_list_column_button_pressed_cb), col);
+
+ if (i == 0) { /* Default sort on first column */
+ g_object_set_data(G_OBJECT(packetlist->view), E_MPACKET_LIST_COLUMN_KEY, col);
+ g_object_set_data(G_OBJECT(packetlist->view), E_MPACKET_LIST_PREV_COLUMN_KEY, col);
+ }
+ }
+
+ rebuild_visible_columns_menu ();
+
+ return packetlist->view;
+}
+
+static PacketListRecord *
+new_packet_list_get_record(GtkTreeModel *model, GtkTreeIter *iter)
+{
+ PacketListRecord *record;
+ /* The last column is reserved for the entire PacketListRecord */
+ gint record_column = gtk_tree_model_get_n_columns(model)-1;
+
+ gtk_tree_model_get(model, iter,
+ record_column,
+ &record,
+ -1);
+
+ return record;
+}
+
+void
+new_packet_list_clear(void)
+{
+ packet_history_clear();
+
+ new_packet_list_store_clear(packetlist);
+ gtk_widget_queue_draw(packetlist->view);
+ /* XXX is this correct in all cases?
+ * Reset the sort column, use packetlist as model in case the list is frozen.
+ */
+ new_packet_list_sort_column(0, NULL, GTK_SORT_ASCENDING, FALSE);
+}
+
+void
+new_packet_list_freeze(void)
+{
+ /* So we don't lose the model by the time we want to thaw it */
+ g_object_ref(packetlist);
+
+ /* Detach view from model */
+ gtk_tree_view_set_model(GTK_TREE_VIEW(packetlist->view), NULL);
+}
+
+void
+new_packet_list_thaw(void)
+{
+ /* Apply model */
+ gtk_tree_view_set_model( GTK_TREE_VIEW(packetlist->view), GTK_TREE_MODEL(packetlist));
+
+ /* Remove extra reference added by new_packet_list_freeze() */
+ g_object_unref(packetlist);
+
+ packets_bar_update();
+}
+
+void
+new_packet_list_recreate_visible_rows(void)
+{
+ packet_list_recreate_visible_rows(packetlist);
+}
+
+void new_packet_list_resize_column(gint col)
+{
+ GtkTreeViewColumn *column;
+ gint col_width;
+ const gchar *long_str;
+
+ long_str = packet_list_get_widest_column_string(packetlist, col);
+ if(!long_str || strcmp("",long_str)==0)
+ /* If we get an empty string leave the width unchanged */
+ return;
+ column = gtk_tree_view_get_column (GTK_TREE_VIEW(packetlist->view), col);
+ col_width = get_default_col_size (packetlist->view, long_str);
+ gtk_tree_view_column_set_fixed_width(column, col_width);
+}
+
+static void
+new_packet_list_resize_columns(void)
+{
+ gint progbar_loop_max;
+ gint progbar_loop_var;
+
+ progbar_loop_max = cfile.cinfo.num_cols;
+
+ for (progbar_loop_var = 0; progbar_loop_var < progbar_loop_max; ++progbar_loop_var)
+ new_packet_list_resize_column(progbar_loop_var);
+}
+
+void
+new_packet_list_resize_columns_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ new_packet_list_resize_columns();
+}
+
+static void
+scroll_to_current(void)
+{
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(top_level));
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
+ /* model is filled with the current model as a convenience. */
+ if (!gtk_tree_selection_get_selected(selection, &model, &iter))
+ return;
+
+ scroll_to_and_select_iter(model, selection, &iter);
+
+ /* Set the focus back where it was */
+ if (focus)
+ gtk_window_set_focus(GTK_WINDOW(top_level), focus);
+}
+
+void
+new_packet_list_next(void)
+{
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(top_level));
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
+ /* model is filled with the current model as a convenience. */
+ if (!gtk_tree_selection_get_selected(selection, &model, &iter))
+ return;
+
+ if (!gtk_tree_model_iter_next(model, &iter))
+ return;
+
+ scroll_to_and_select_iter(model, selection, &iter);
+
+ /* Set the focus back where it was */
+ if (focus)
+ gtk_window_set_focus(GTK_WINDOW(top_level), focus);
+}
+
+void
+new_packet_list_prev(void)
+{
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkWidget *focus = gtk_window_get_focus(GTK_WINDOW(top_level));
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
+ /* model is filled with the current model as a convenience. */
+ if (!gtk_tree_selection_get_selected(selection, &model, &iter))
+ return;
+
+ path = gtk_tree_model_get_path(model, &iter);
+
+ if (!gtk_tree_path_prev(path))
+ return;
+
+ if (!gtk_tree_model_get_iter(model, &iter, path))
+ return;
+
+ scroll_to_and_select_iter(model, selection, &iter);
+
+ gtk_tree_path_free(path);
+
+ /* Set the focus back where it was */
+ if (focus)
+ gtk_window_set_focus(GTK_WINDOW(top_level), focus);
+}
+
+static void
+scroll_to_and_select_iter(GtkTreeModel *model, GtkTreeSelection *selection, GtkTreeIter *iter)
+{
+ GtkTreePath *path;
+
+ g_assert(model);
+
+ /* Select the row */
+ if(!selection)
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
+ gtk_tree_selection_select_iter (selection, iter);
+ path = gtk_tree_model_get_path(model, iter);
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(packetlist->view),
+ path,
+ NULL,
+ TRUE, /* use_align */
+ 0.5, /* row_align determines where the row is placed, 0.5 means center */
+ 0); /* The horizontal alignment of the column */
+
+ /* "cursor-changed" signal triggers new_packet_list_select_cb() */
+ /* which will update the middle and bottom panes. */
+ gtk_tree_view_set_cursor(GTK_TREE_VIEW(packetlist->view),
+ path,
+ NULL,
+ FALSE); /* start_editing */
+
+ gtk_tree_path_free(path);
+}
+
+void
+new_packet_list_select_first_row(void)
+{
+ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(packetlist->view));
+ GtkTreeIter iter;
+
+ if(!gtk_tree_model_get_iter_first(model, &iter))
+ return;
+
+ scroll_to_and_select_iter(model, NULL, &iter);
+ gtk_widget_grab_focus(packetlist->view);
+}
+
+void
+new_packet_list_select_last_row(void)
+{
+ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(packetlist->view));
+ GtkTreeIter iter;
+ gint children;
+ guint last_row;
+
+ if((children = gtk_tree_model_iter_n_children(model, NULL)) == 0)
+ return;
+
+ last_row = children-1;
+ if(!gtk_tree_model_iter_nth_child(model, &iter, NULL, last_row))
+ return;
+
+ scroll_to_and_select_iter(model, NULL, &iter);
+}
+
+void
+new_packet_list_moveto_end(void)
+{
+ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(packetlist->view));
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ gint children;
+ guint last_row;
+
+ if((children = gtk_tree_model_iter_n_children(model, NULL)) == 0)
+ return;
+
+ last_row = children-1;
+ if(!gtk_tree_model_iter_nth_child(model, &iter, NULL, last_row))
+ return;
+
+ path = gtk_tree_model_get_path(model, &iter);
+
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(packetlist->view),
+ path,
+ NULL,
+ TRUE, /* use_align */
+ 0.5, /* row_align determines where the row is placed, 0.5 means center */
+ 0); /* The horizontal alignment of the column */
+
+ gtk_tree_path_free(path);
+
+}
+
+gboolean
+new_packet_list_check_end(void)
+{
+ gboolean at_end = FALSE;
+ GtkAdjustment *adj;
+
+#if GTK_CHECK_VERSION(3,0,0)
+ adj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (packetlist->view));
+#else
+ adj = gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(packetlist->view));
+#endif
+ g_return_val_if_fail(adj != NULL, FALSE);
+
+ if (gtk_adjustment_get_value(adj) >= gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj)) {
+ at_end = TRUE;
+ }
+#ifdef HAVE_LIBPCAP
+ if (gtk_adjustment_get_value(adj) > 0 && at_end != last_at_end && at_end != auto_scroll_live) {
+ menu_auto_scroll_live_changed(at_end);
+ }
+#endif
+ last_at_end = at_end;
+ return at_end;
+}
+
+/*
+ * Given a frame_data structure, scroll to and select the row in the
+ * packet list corresponding to that frame. If there is no such
+ * row, return FALSE, otherwise return TRUE.
+ */
+gboolean
+new_packet_list_select_row_from_data(frame_data *fdata_needle)
+{
+ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(packetlist->view));
+ GtkTreeIter iter;
+
+ /* Initializes iter with the first iterator in the tree (the one at the path "0")
+ * and returns TRUE. Returns FALSE if the tree is empty
+ */
+ if(!gtk_tree_model_get_iter_first(model, &iter))
+ return FALSE;
+
+ do {
+ PacketListRecord *record;
+ frame_data *fdata;
+
+ record = new_packet_list_get_record(model, &iter);
+ fdata = record->fdata;
+
+ if(fdata == fdata_needle) {
+ scroll_to_and_select_iter(model, NULL, &iter);
+
+ return TRUE;
+ }
+ } while (gtk_tree_model_iter_next(model, &iter));
+
+ return FALSE;
+}
+
+void
+new_packet_list_set_selected_row(gint row)
+{
+ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(packetlist->view));
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ GtkTreePath *path;
+
+ path = gtk_tree_path_new_from_indices(row-1, -1);
+
+ if (!gtk_tree_model_get_iter(model, &iter, path))
+ return;
+
+ /* Select the row */
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
+ gtk_tree_selection_select_iter (selection, &iter);
+
+ /* "cursor-changed" signal triggers new_packet_list_select_cb() */
+ /* which will update the middle and bottom panes. */
+ gtk_tree_view_set_cursor(GTK_TREE_VIEW(packetlist->view),
+ path,
+ NULL,
+ FALSE); /* start_editing */
+
+ gtk_tree_path_free(path);
+}
+
+static gint
+row_number_from_iter(GtkTreeIter *iter)
+{
+ gint row;
+ gint *indices;
+ GtkTreePath *path;
+ GtkTreeModel *model;
+
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(packetlist->view));
+ path = gtk_tree_model_get_path(model, iter);
+ indices = gtk_tree_path_get_indices(path);
+ g_assert(indices);
+ /* Indices start from 0, but rows start from 1. Hence +1 */
+ row = indices[0] + 1;
+
+ gtk_tree_path_free(path);
+
+ return row;
+}
+
+static void
+new_packet_list_select_cb(GtkTreeView *tree_view, gpointer data _U_)
+{
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ gint row;
+
+ selection = gtk_tree_view_get_selection(tree_view);
+ if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
+ return;
+
+ row = row_number_from_iter(&iter);
+
+ /* Check if already selected
+ */
+ if (cfile.current_frame && cfile.current_row == row)
+ return;
+
+ /* Remove the hex display tab pages */
+ while(gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr_gbl), 0))
+ gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr_gbl), 0);
+
+ cf_select_packet(&cfile, row);
+ /* If searching the tree, set the focus there; otherwise, focus on the packet list */
+ if (cfile.search_in_progress && (cfile.decode_data || cfile.decode_data)) {
+ gtk_widget_grab_focus(tree_view_gbl);
+ } else {
+ gtk_widget_grab_focus(packetlist->view);
+ }
+
+ /* Add newly selected frame to packet history (breadcrumbs) */
+ packet_history_add(row);
+}
+
+static void
+new_packet_list_double_click_cb(GtkTreeView *treeview, GtkTreePath *path _U_,
+ GtkTreeViewColumn *col _U_, gpointer userdata _U_)
+{
+ new_packet_window(GTK_WIDGET(treeview), FALSE);
+}
+
+gboolean
+new_packet_list_get_event_row_column(GdkEventButton *event_button,
+ gint *physical_row, gint *row, gint *column)
+{
+ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(packetlist->view));
+ GtkTreePath *path;
+ GtkTreeViewColumn *view_column;
+
+ if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(packetlist->view),
+ (gint) event_button->x,
+ (gint) event_button->y,
+ &path, &view_column, NULL, NULL)) {
+ GtkTreeIter iter;
+ GList *cols;
+ gint *indices;
+ PacketListRecord *record;
+
+ /* Fetch indices */
+ gtk_tree_model_get_iter(model, &iter, path);
+ indices = gtk_tree_path_get_indices(path);
+ g_assert(indices);
+ /* Indices start from 0. Hence +1 */
+ *row = indices[0] + 1;
+ gtk_tree_path_free(path);
+
+ /* Fetch physical row */
+ record = new_packet_list_get_record(model, &iter);
+ *physical_row = record->fdata->num;
+
+ /* Fetch column */
+ /* XXX -doesn't work if columns are re-arranged? */
+ cols = gtk_tree_view_get_columns(GTK_TREE_VIEW(packetlist->view));
+ *column = g_list_index(cols, (gpointer) view_column);
+ g_list_free(cols);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+frame_data *
+new_packet_list_get_row_data(gint row)
+{
+ GtkTreePath *path = gtk_tree_path_new();
+ GtkTreeIter iter;
+ PacketListRecord *record;
+
+ gtk_tree_path_append_index(path, row-1);
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(packetlist), &iter, path);
+
+ record = new_packet_list_get_record(GTK_TREE_MODEL(packetlist), &iter);
+
+ gtk_tree_path_free(path);
+
+ return record->fdata;
+}
+
+static void
+show_cell_data_func(GtkTreeViewColumn *col _U_, GtkCellRenderer *renderer,
+ GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
+{
+ guint col_num = GPOINTER_TO_INT(data);
+ frame_data *fdata;
+ const gchar *cell_text;
+ PacketListRecord *record;
+
+ record = new_packet_list_get_record(model, iter);
+ fdata = record->fdata;
+
+ if (!record->columnized || !record->colorized) {
+ packet_list_dissect_and_cache_iter(packetlist, iter,
+ !record->columnized,
+ !record->colorized);
+ }
+
+ g_assert(record->col_text);
+
+ if (col_based_on_frame_data(&cfile.cinfo, col_num)) {
+ col_fill_in_frame_data(fdata, &cfile.cinfo, col_num, FALSE);
+ cell_text = cfile.cinfo.col_data[col_num];
+ }else
+ cell_text = record->col_text[col_num];
+
+ g_assert(cell_text);
+
+ if((fdata->color_filter)||(fdata->flags.marked)||(fdata->flags.ignored)){
+ gboolean color_on = enable_color;
+ GdkColor fg_gdk;
+ GdkColor bg_gdk;
+ if(fdata->flags.ignored){
+ color_t_to_gdkcolor(&fg_gdk, &prefs.gui_ignored_fg);
+ color_t_to_gdkcolor(&bg_gdk, &prefs.gui_ignored_bg);
+ color_on = TRUE;
+ }else if(fdata->flags.marked){
+ color_t_to_gdkcolor(&fg_gdk, &prefs.gui_marked_fg);
+ color_t_to_gdkcolor(&bg_gdk, &prefs.gui_marked_bg);
+ color_on = TRUE;
+ }else{
+ color_t fg_color_t;
+ color_t bg_color_t;
+ const color_filter_t *color_filter = fdata->color_filter;
+
+ fg_color_t = color_filter->fg_color;
+ bg_color_t = color_filter->bg_color;
+ color_t_to_gdkcolor(&fg_gdk, &fg_color_t);
+ color_t_to_gdkcolor(&bg_gdk, &bg_color_t);
+ }
+ g_object_set(renderer,
+ "text", cell_text,
+ "foreground-gdk", &fg_gdk,
+ "foreground-set", color_on,
+ "background-gdk", &bg_gdk,
+ "background-set", color_on,
+ NULL);
+ }else{
+ g_object_set(renderer,
+ "text", cell_text,
+ "foreground-set", FALSE,
+ "background-set", FALSE,
+ NULL);
+ }
+}
+
+void
+new_packet_list_enable_color(gboolean enable)
+{
+ enable_color = enable;
+ gtk_widget_queue_draw (packetlist->view);
+}
+
+/* Redraw the packet list *and* currently-selected detail */
+void
+new_packet_list_queue_draw(void)
+{
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ gint row;
+
+ gtk_widget_queue_draw (packetlist->view);
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
+ if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
+ return;
+ row = row_number_from_iter(&iter);
+ cf_select_packet(&cfile, row);
+}
+
+/* Set the selection mode of the packet list window. */
+void
+new_packet_list_set_sel_browse(gboolean val, gboolean force_set)
+{
+ GtkSelectionMode new_mode;
+ /* initialize with a mode we don't use, so that the mode == new_mode
+ * test will fail the first time */
+ static GtkSelectionMode mode = GTK_SELECTION_MULTIPLE;
+
+ /* Yeah, GTK uses "browse" in the case where we do not, but oh well. I
+ * think "browse" in Wireshark makes more sense than "SINGLE" in GTK+ */
+ new_mode = val ? GTK_SELECTION_SINGLE : GTK_SELECTION_BROWSE;
+
+ if ((mode == new_mode) && !force_set) {
+ /*
+ * The mode isn't changing, so don't do anything.
+ * In particular, don't gratuitiously unselect the
+ * current packet.
+ *
+ * XXX - Copied code from "old" packet list
+ * - I don't know if the comment below is still true...
+ * XXX - why do we have to unselect the current packet
+ * ourselves? The documentation for the GtkCList at
+ *
+ * http://developer.gnome.org/doc/API/gtk/gtkclist.html
+ *
+ * says "Note that setting the widget's selection mode to
+ * one of GTK_SELECTION_BROWSE or GTK_SELECTION_SINGLE will
+ * cause all the items in the GtkCList to become deselected."
+ */
+ return;
+ }
+
+ if (cfile.finfo_selected)
+ cf_unselect_field(&cfile);
+
+ mode = new_mode;
+ gtk_tree_selection_set_mode (gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view)), mode);
+}
+
+void
+new_packet_list_set_font(PangoFontDescription *font)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_font(packetlist->view, font);
+#else
+ gtk_widget_modify_font(packetlist->view, font);
+#endif
+}
+
+
+/* call this after last set_frame_mark is done */
+static void
+mark_frames_ready(void)
+{
+ file_save_update_dynamics();
+ packets_bar_update();
+ new_packet_list_queue_draw();
+}
+
+static void
+set_frame_mark(gboolean set, frame_data *fdata)
+{
+ if (set)
+ cf_mark_frame(&cfile, fdata);
+ else
+ cf_unmark_frame(&cfile, fdata);
+}
+
+void
+new_packet_list_mark_frame_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ PacketListRecord *record;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
+ /* model is filled with the current model as a convenience. */
+ if (!gtk_tree_selection_get_selected(selection, &model, &iter))
+ return;
+
+ record = new_packet_list_get_record(model, &iter);
+
+ set_frame_mark(!record->fdata->flags.marked, record->fdata);
+ mark_frames_ready();
+}
+
+static void
+mark_all_displayed_frames(gboolean set)
+{
+ /* XXX: we might need a progressbar here */
+ guint32 framenum;
+ frame_data *fdata;
+ for (framenum = 1; framenum <= cfile.count; framenum++) {
+ fdata = frame_data_sequence_find(cfile.frames, framenum);
+ if( fdata->flags.passed_dfilter )
+ set_frame_mark(set, fdata);
+ }
+}
+
+void
+new_packet_list_mark_all_displayed_frames_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ mark_all_displayed_frames(TRUE);
+ mark_frames_ready();
+}
+
+void
+new_packet_list_unmark_all_displayed_frames_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ mark_all_displayed_frames(FALSE);
+ mark_frames_ready();
+}
+
+static void
+toggle_mark_all_displayed_frames(void)
+{
+ /* XXX: we might need a progressbar here */
+ guint32 framenum;
+ frame_data *fdata;
+ for (framenum = 1; framenum <= cfile.count; framenum++) {
+ fdata = frame_data_sequence_find(cfile.frames, framenum);
+ if( fdata->flags.passed_dfilter )
+ set_frame_mark(!fdata->flags.marked, fdata);
+ }
+}
+
+void
+new_packet_list_toggle_mark_all_displayed_frames_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ toggle_mark_all_displayed_frames();
+ mark_frames_ready();
+}
+
+
+static void
+set_frame_ignore(gboolean set, frame_data *fdata)
+{
+ if (set)
+ cf_ignore_frame(&cfile, fdata);
+ else
+ cf_unignore_frame(&cfile, fdata);
+}
+
+void
+new_packet_list_ignore_frame_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ PacketListRecord *record;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
+ /* model is filled with the current model as a convenience. */
+ if (!gtk_tree_selection_get_selected(selection, &model, &iter))
+ return;
+
+ record = new_packet_list_get_record(model, &iter);
+ set_frame_ignore(!record->fdata->flags.ignored, record->fdata);
+ redissect_packets();
+}
+
+static void
+ignore_all_displayed_frames(gboolean set)
+{
+ guint32 framenum;
+ frame_data *fdata;
+
+ /* XXX: we might need a progressbar here */
+ for (framenum = 1; framenum <= cfile.count; framenum++) {
+ fdata = frame_data_sequence_find(cfile.frames, framenum);
+ if( fdata->flags.passed_dfilter )
+ set_frame_ignore(set, fdata);
+ }
+ redissect_packets();
+}
+
+void
+new_packet_list_ignore_all_displayed_frames_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ if(cfile.displayed_count < cfile.count){
+ frame_data *fdata;
+ /* Due to performance impact with large captures, don't check the filtered list for
+ an ignored frame; just check the first. If a ignored frame exists but isn't first and
+ the user wants to unignore all the displayed frames, they will just re-exec the shortcut. */
+ fdata = frame_data_sequence_find(cfile.frames, cfile.first_displayed);
+ if (fdata->flags.ignored==TRUE) {
+ ignore_all_displayed_frames(FALSE);
+ } else {
+ ignore_all_displayed_frames(TRUE);
+ }
+ }
+}
+
+static void
+unignore_all_frames(void)
+{
+ guint32 framenum;
+ frame_data *fdata;
+
+ /* XXX: we might need a progressbar here */
+ for (framenum = 1; framenum <= cfile.count; framenum++) {
+ fdata = frame_data_sequence_find(cfile.frames, framenum);
+ set_frame_ignore(FALSE, fdata);
+ }
+ redissect_packets();
+}
+
+void
+new_packet_list_unignore_all_frames_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ unignore_all_frames();
+}
+
+
+static void
+untime_reference_all_frames(void)
+{
+ /* XXX: we might need a progressbar here */
+ guint32 framenum;
+ frame_data *fdata;
+ for (framenum = 1; framenum <= cfile.count && cfile.ref_time_count > 0; framenum++) {
+ fdata = frame_data_sequence_find(cfile.frames, framenum);
+ if (fdata->flags.ref_time == 1) {
+ set_frame_reftime(FALSE, fdata, cfile.current_row);
+ }
+ }
+}
+
+void
+new_packet_list_untime_reference_all_frames_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ untime_reference_all_frames();
+}
+
+
+guint
+new_packet_list_get_column_id (gint col_num)
+{
+ GtkTreeViewColumn *column = gtk_tree_view_get_column (GTK_TREE_VIEW(packetlist->view), col_num);
+ gint col_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), E_MPACKET_LIST_COL_KEY));
+
+ return col_id;
+}
+
+static gboolean
+get_col_text_from_record( PacketListRecord *record, gint col_num, gchar** cell_text)
+{
+ gint col_id = new_packet_list_get_column_id (col_num);
+
+ if (col_based_on_frame_data(&cfile.cinfo, col_id)) {
+ col_fill_in_frame_data(record->fdata, &cfile.cinfo, col_id, FALSE);
+ *cell_text = g_strdup(cfile.cinfo.col_data[col_id]);
+ }else
+ *cell_text = g_strdup(record->col_text[col_id]);
+
+ return TRUE;
+}
+
+void
+new_packet_list_copy_summary_cb(gpointer data _U_, copy_summary_type copy_type)
+{
+ gint col;
+ gchar *celltext;
+ GString* text;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ PacketListRecord *record;
+
+ if(CS_CSV == copy_type) {
+ text = g_string_new("\"");
+ } else {
+ text = g_string_new("");
+ }
+
+ if (cfile.current_frame) {
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
+ /* model is filled with the current model as a convenience. */
+ if (!gtk_tree_selection_get_selected(selection, &model, &iter))
+ return;
+
+ record = new_packet_list_get_record(model, &iter);
+ for(col = 0; col < cfile.cinfo.num_cols; ++col) {
+ if(col != 0) {
+ if(CS_CSV == copy_type) {
+ g_string_append(text,"\",\"");
+ } else {
+ g_string_append_c(text, '\t');
+ }
+ }
+ if(get_col_text_from_record( record, col, &celltext)){
+ g_string_append(text,celltext);
+ g_free(celltext);
+ }
+ }
+ if(CS_CSV == copy_type) {
+ g_string_append_c(text,'"');
+ }
+ copy_to_clipboard(text);
+ }
+ g_string_free(text,TRUE);
+}
+
+void
+new_packet_list_recent_write_all(FILE *rf)
+{
+ gint col, width, num_cols, col_fmt;
+ GtkTreeViewColumn *tree_column;
+ gchar xalign;
+
+ fprintf (rf, "%s:", RECENT_KEY_COL_WIDTH);
+ num_cols = g_list_length(prefs.col_list);
+ for (col = 0; col < num_cols; col++) {
+ col_fmt = get_column_format(col);
+ if (col_fmt == COL_CUSTOM) {
+ fprintf (rf, " %%Cus:%s,", get_column_custom_field(col));
+ } else {
+ fprintf (rf, " %s,", col_format_to_string(col_fmt));
+ }
+ tree_column = gtk_tree_view_get_column(GTK_TREE_VIEW(packetlist->view), col);
+ width = gtk_tree_view_column_get_width(tree_column);
+ xalign = recent_get_column_xalign (col);
+ if (width == 0) {
+ /* We have not initialized the packet list yet, use old values */
+ width = recent_get_column_width (col);
+ }
+ fprintf (rf, " %d", width);
+ if (xalign != COLUMN_XALIGN_DEFAULT) {
+ fprintf (rf, ":%c", xalign);
+ }
+ if (col != num_cols-1) {
+ fprintf (rf, ",");
+ }
+ }
+ fprintf (rf, "\n");
+}
+
+GtkWidget *
+new_packet_list_get_widget(void)
+{
+ g_assert(packetlist);
+ g_assert(packetlist->view);
+ return packetlist->view;
+}
+
+void
+new_packet_list_colorize_packets(void)
+{
+ packet_list_reset_colorized(packetlist);
+ gtk_widget_queue_draw (packetlist->view);
+}
diff --git a/ui/gtk/new_packet_list.h b/ui/gtk/new_packet_list.h
new file mode 100644
index 0000000000..b25bdf76f9
--- /dev/null
+++ b/ui/gtk/new_packet_list.h
@@ -0,0 +1,155 @@
+/* new_packet_list.h
+ *
+ * $Id$
+ *
+ * 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 __NEW_PACKET_LIST_H__
+#define __NEW_PACKET_LIST_H__
+
+#include <gtk/gtk.h>
+
+/** @file
+ * @ingroup main_window_group
+ */
+
+#define RECENT_KEY_COL_WIDTH "column.width"
+
+typedef enum {
+ COLUMN_SELECTED_SORT_ASCENDING,
+ COLUMN_SELECTED_SORT_DESCENDING,
+ COLUMN_SELECTED_SORT_NONE,
+ COLUMN_SELECTED_TOGGLE_RESOLVED,
+ COLUMN_SELECTED_ALIGN_LEFT,
+ COLUMN_SELECTED_ALIGN_CENTER,
+ COLUMN_SELECTED_ALIGN_RIGHT,
+ COLUMN_SELECTED_ALIGN_DEFAULT,
+ COLUMN_SELECTED_RESIZE,
+ COLUMN_SELECTED_CHANGE,
+ COLUMN_SELECTED_HIDE,
+ COLUMN_SELECTED_REMOVE
+} COLUMN_SELECTED_E;
+
+/** Create the packet list */
+GtkWidget *new_packet_list_create(void);
+/** Recreate the packetr list */
+void new_packet_list_recreate(void);
+void new_packet_list_toggle_visible_column (gint col_id);
+void new_packet_list_set_all_columns_visible (void);
+void new_packet_list_column_menu_cb (GtkWidget *w, gpointer data, COLUMN_SELECTED_E action);
+void new_packet_list_resize_columns_cb(GtkWidget *widget _U_, gpointer data _U_);
+gboolean new_packet_list_get_event_row_column(GdkEventButton *event_button, gint *physical_row, gint *row, gint *column);
+guint new_packet_list_get_column_id (gint col_num);
+
+/** Set the font of the packet list window.
+ *
+ * @param font new font
+ */
+extern void new_packet_list_set_font(PangoFontDescription *font);
+
+/** Mark the currently selected packet.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void new_packet_list_mark_frame_cb(GtkWidget *widget, gpointer data);
+
+/** Toggle Mark on all displayed packets.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void new_packet_list_toggle_mark_all_displayed_frames_cb(GtkWidget *w _U_, gpointer data _U_);
+
+/** Mark all displayed packets.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void new_packet_list_mark_all_displayed_frames_cb(GtkWidget *w _U_, gpointer data _U_);
+
+/** UnMark all packets in the capture.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void new_packet_list_unmark_all_displayed_frames_cb(GtkWidget *w _U_, gpointer data _U_);
+
+/** Ignore the currently selected packet.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void new_packet_list_ignore_frame_cb(GtkWidget *widget, gpointer data);
+
+/** Ignore/Unignore all displayed packets.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void new_packet_list_ignore_all_displayed_frames_cb(GtkWidget *w _U_, gpointer data _U_);
+
+/** Un-ignore all packets in the list.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void new_packet_list_unignore_all_frames_cb(GtkWidget *w _U_, gpointer data _U_);
+
+/** Un-Time Reference all packets in the capture.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void new_packet_list_untime_reference_all_frames_cb(GtkWidget *w _U_, gpointer data _U_);
+
+/** Different modes of copying summary data */
+typedef enum {
+ CS_TEXT, /**< Packet summary data (tab separated) */
+ CS_CSV /**< Packet summary data (comma separated) */
+} copy_summary_type;
+
+/** Called when user clicks on menu item to copy summary data.
+ *
+ * @param w Not used.
+ * @param data Not used.
+ * @param copy_type Mode in which to copy data (e.g. tab-separated, CSV)
+ */
+void new_packet_list_copy_summary_cb(gpointer data _U_, copy_summary_type copy_type);
+
+/** Write all packet list geometry values to the recent file.
+ *
+ * @param rf recent file handle from caller
+ */
+extern void new_packet_list_recent_write_all(FILE *rf);
+
+GtkWidget * new_packet_list_get_widget(void);
+void new_packet_list_colorize_packets(void);
+
+/** Set the selection mode of the packet list window.
+ *
+ * @param val TRUE for GTK_SELECTION_SINGLE, FALSE for GTK_SELECTION_BROWSE
+ * @param force_set TRUE to force setting of the selection mode even if it
+ * was already set (used within packet_list_recreate).
+ */
+extern void new_packet_list_set_sel_browse(gboolean val, gboolean force_set);
+
+#endif /* __NEW_PACKET_LIST_H__ */
diff --git a/ui/gtk/old-gtk-compat.h b/ui/gtk/old-gtk-compat.h
new file mode 100644
index 0000000000..a3824d5414
--- /dev/null
+++ b/ui/gtk/old-gtk-compat.h
@@ -0,0 +1,98 @@
+/*
+ * Definitions to provide some functions that are not present in older
+ * GTK versions (down to 2.12.0)
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef OLD_GTK_COMPAT_H
+#define OLD_GTK_COMPAT_H
+
+#if !GTK_CHECK_VERSION (2, 14, 0)
+# define gtk_widget_get_window(widget) (widget)->window
+# define gtk_color_selection_dialog_get_color_selection(dialog) (dialog)->colorsel
+# define gtk_selection_data_get_length(seldata) (seldata)->length
+# define gtk_selection_data_get_data(seldata) (seldata)->data
+# define gtk_adjustment_set_upper(adj, val) (adj)->upper = val
+# define gtk_adjustment_get_upper(adj) (adj)->upper
+# define gtk_adjustment_get_lower(adj) (adj)->lower
+# define gtk_adjustment_set_step_increment(adj, val) (adj)->step_increment = val
+# define gtk_adjustment_set_page_increment(adj, val) (adj)->page_increment = val
+# define gtk_adjustment_get_page_increment(adj) (adj)->page_increment
+# define gtk_adjustment_set_page_size(adj, val) (adj)->page_size = val
+# define gtk_adjustment_get_page_size(adj) (adj)->page_size
+# define gtk_dialog_get_content_area(dialog) (dialog)->vbox
+#endif
+
+#if !GTK_CHECK_VERSION (2, 16, 0)
+# define GTK_ORIENTABLE(x) GTK_TOOLBAR(x)
+# define gtk_orientable_set_orientation(x,y) gtk_toolbar_set_orientation(x,y)
+#endif
+
+#if !GTK_CHECK_VERSION (2, 18, 0)
+# define gtk_widget_get_has_window(x) (!GTK_WIDGET_NO_WINDOW(x))
+# define gtk_widget_get_visible(x) GTK_WIDGET_VISIBLE(x)
+# define gtk_widget_get_state(x) GTK_WIDGET_STATE(x)
+# define gtk_widget_get_allocation(x,y) (*(y) = x->allocation)
+# define gtk_widget_get_sensitive(x) GTK_WIDGET_SENSITIVE(x)
+# define gtk_widget_is_drawable(x) GDK_IS_DRAWABLE(x)
+#endif
+
+#if !GTK_CHECK_VERSION (2, 20, 0)
+# define gtk_widget_get_realized(x) GTK_WIDGET_REALIZED(x)
+#endif
+
+#if !GTK_CHECK_VERSION (2, 22, 0)
+#endif
+
+#if !GTK_CHECK_VERSION (2, 24, 0)
+# define GTK_COMBO_BOX_TEXT(x) GTK_COMBO_BOX(x)
+# define gtk_combo_box_text_get_active_text(x) gtk_combo_box_get_active_text(x)
+# define gtk_combo_box_text_new() gtk_combo_box_new_text()
+# define gtk_combo_box_text_insert_text(x,y,z) gtk_combo_box_insert_text(x,y,z)
+# define gtk_combo_box_text_append_text(x,y) gtk_combo_box_append_text(x,y)
+# define gtk_combo_box_text_remove(x,y) gtk_combo_box_remove_text(x,y)
+# define gtk_combo_box_text_new_with_entry() gtk_combo_box_entry_new_text()
+# define gtk_combo_box_text_prepend_text(x,y) gtk_combo_box_prepend_text(x,y)
+#endif
+
+#if !GTK_CHECK_VERSION (3, 0, 0)
+# define gtk_widget_get_preferred_size(x,y,z) gtk_widget_size_request(x,y)
+# define GtkStyleContext GtkStyle
+# define gtk_widget_get_style_context(x) gtk_widget_get_style(x)
+# define gtk_style_context_get_color(x,y,z) gdkcolor_to_color_t(&z, &x->text[y])
+# define gtk_style_context_get_color_background(x,y,z) gdkcolor_to_color_t(&z, &x->base[y])
+# if GTK_CHECK_VERSION (2, 14, 0) && defined(GSEAL_ENABLE)
+ /* This is too late, see https://bugzilla.gnome.org/show_bug.cgi?id=641089
+ * Accoriding to
+ * http://ftp.acc.umu.se/pub/GNOME/sources/gtk+/2.13/gtk+-2.13.4.changes
+ * access to the button element was sealed during 2.13. They also admit that
+ * they missed a use case and thus failed to provide an accessor function:
+ * http://mail.gnome.org/archives/commits-list/2010-December/msg00578.html
+ * An accessor function was finally added in 3.0.
+ */
+# define gtk_tree_view_column_get_button(x) x->_g_sealed__button
+# else
+# define gtk_tree_view_column_get_button(x) x->button
+# endif
+#endif
+
+#endif
diff --git a/ui/gtk/packet_history.c b/ui/gtk/packet_history.c
new file mode 100644
index 0000000000..5951da3b02
--- /dev/null
+++ b/ui/gtk/packet_history.c
@@ -0,0 +1,188 @@
+/* packet_history.c
+ * packet history related functions 2004 Ulf Lamping
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+
+#include "../file.h"
+#include "../globals.h"
+
+#include "ui/gtk/menus.h"
+#include "ui/gtk/packet_history.h"
+
+
+static GList *history_current = NULL;
+static GList *history_list = NULL;
+static gboolean ignore_jump = FALSE;
+
+
+#if 0
+/* print the complete packet history to console */
+static void history_print(void) {
+ GList *current = g_list_first(history_list);
+
+ printf(" List:\n");
+
+ while(current) {
+ if(current == history_current) {
+ printf(" Row: %u *\n", GPOINTER_TO_INT(current->data));
+ } else {
+ printf(" Row: %u\n", GPOINTER_TO_INT(current->data));
+ }
+ current = g_list_next(current);
+ }
+}
+#endif
+
+
+/* adjust menu and toolbar sensitivity depending on the history entries */
+static void adjust_menus(void) {
+
+ if(history_current) {
+ set_menus_for_packet_history(
+ (g_list_previous(history_current) != NULL),
+ (g_list_next(history_current) != NULL));
+ } else {
+ /* we don't have any history */
+ set_menus_for_packet_history(FALSE, FALSE);
+ }
+
+ /* history_print(); */
+}
+
+
+/* clear the history list from the given entry to the end of the list */
+static void clear_list(GList *current) {
+ GList *next_packet;
+
+
+ while(current) {
+ next_packet = g_list_next(current);
+ history_list = g_list_remove(history_list, current->data);
+ current = next_packet;
+ }
+}
+
+
+/* add an entry to the history list */
+void packet_history_add(gint row) {
+
+ if(row < 1) {
+ /* Not a valid row number */
+ return;
+ }
+
+ if(ignore_jump) {
+ /* we jumping back and forward in history, so don't change list */
+ return;
+ }
+
+ if (history_current) {
+ /* clear list behind current position */
+ clear_list(g_list_next(history_current));
+
+ /* ignore duplicates */
+ if(GPOINTER_TO_INT(history_current->data) == row) {
+ adjust_menus();
+ return;
+ }
+ }
+
+ /* add row */
+ history_list = g_list_append(history_list, GINT_TO_POINTER(row));
+ history_current = g_list_last(history_list);
+
+ adjust_menus();
+}
+
+
+void packet_history_clear(void) {
+
+ /* clear "old" list */
+ clear_list(history_list);
+ history_current = NULL;
+
+ /* add the currently selected first row */
+ packet_history_add(0);
+
+ adjust_menus();
+}
+
+
+static void packet_history_back(void) {
+ GList *previous;
+
+ if(history_current) {
+ previous = g_list_previous(history_current);
+
+ /* do we have a previous entry */
+ if(previous) {
+ history_current = previous;
+
+ /* goto that packet but don't change history */
+ ignore_jump = TRUE;
+ cf_goto_frame(&cfile, GPOINTER_TO_INT(previous->data));
+ ignore_jump = FALSE;
+ }
+ }
+
+ adjust_menus();
+}
+
+
+static void packet_history_forward(void) {
+ GList *next;
+
+ if(history_current) {
+ next = g_list_next(history_current);
+
+ /* do we have a forward entry? */
+ if(next) {
+ history_current = next;
+
+ /* goto that packet but don't change history */
+ ignore_jump = TRUE;
+ cf_goto_frame(&cfile, GPOINTER_TO_INT(next->data));
+ ignore_jump = FALSE;
+ }
+ }
+
+ adjust_menus();
+}
+
+
+void history_forward_cb(GtkWidget *widget _U_, gpointer data _U_) {
+ packet_history_forward();
+}
+
+
+void history_back_cb(GtkWidget *widget _U_, gpointer data _U_) {
+ packet_history_back();
+}
+
diff --git a/ui/gtk/packet_history.h b/ui/gtk/packet_history.h
new file mode 100644
index 0000000000..76469e41b9
--- /dev/null
+++ b/ui/gtk/packet_history.h
@@ -0,0 +1,36 @@
+/* packet_history.h
+ * packet history related functions 2004 Ulf Lamping
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PACKET_HISTORY_H__
+#define __PACKET_HISTORY_H__
+
+extern void packet_history_add(gint row);
+
+extern void packet_history_clear(void);
+
+extern void history_forward_cb(GtkWidget *widget, gpointer data);
+
+extern void history_back_cb(GtkWidget *widget, gpointer data);
+
+#endif /* __PACKET_HISTORY_H__ */
diff --git a/ui/gtk/packet_list_store.c b/ui/gtk/packet_list_store.c
new file mode 100644
index 0000000000..989a3ff7e1
--- /dev/null
+++ b/ui/gtk/packet_list_store.c
@@ -0,0 +1,1347 @@
+/* packet_list_store.c
+ * Routines to implement a custom GTK+ list model for Wireshark's packet list
+ * Copyright 2008-2009, Stephen Fisher (see AUTHORS file)
+ * * Co-authors Anders Broman and Kovarththanan Rajaratnam.
+ *
+ * $Id$
+ *
+ * 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.
+ */
+
+/* This code was originally based on the GTK+ Tree View tutorial at
+ * http://scentric.net/tutorial */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <glib.h>
+
+#include "packet_list_store.h"
+#include "ui_util.h"
+
+#include <epan/epan_dissect.h>
+#include <epan/column_info.h>
+#include <epan/column.h>
+#include <epan/nstime.h>
+
+#include "color.h"
+#include "color_filters.h"
+
+#include "globals.h"
+
+#include "../progress_dlg.h"
+
+static void packet_list_init(PacketList *pkg_tree);
+static void packet_list_class_init(PacketListClass *klass);
+static void packet_list_tree_model_init(GtkTreeModelIface *iface);
+static void packet_list_finalize(GObject *object);
+static GtkTreeModelFlags packet_list_get_flags(GtkTreeModel *tree_model);
+static gint packet_list_get_n_columns(GtkTreeModel *tree_model);
+static GType packet_list_get_column_type(GtkTreeModel *tree_model, gint idx);
+static gboolean packet_list_get_iter(GtkTreeModel *tree_model,
+ GtkTreeIter *iter, GtkTreePath *path);
+static GtkTreePath *packet_list_get_path(GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static void packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter,
+ gint column, GValue *value);
+static gboolean packet_list_iter_next(GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gboolean packet_list_iter_children(GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent);
+static gboolean packet_list_iter_has_child(GtkTreeModel *tree_model _U_,
+ GtkTreeIter *iter _U_);
+static gint packet_list_iter_n_children(GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gboolean packet_list_iter_nth_child(GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n);
+static gboolean packet_list_iter_parent(GtkTreeModel *tree_model _U_,
+ GtkTreeIter *iter _U_,
+ GtkTreeIter *child _U_);
+
+static gboolean packet_list_sortable_get_sort_column_id(GtkTreeSortable
+ *sortable,
+ gint *sort_col_id,
+ GtkSortType *order);
+static void packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
+ gint sort_col_id,
+ GtkSortType order);
+static void packet_list_sortable_set_sort_func(GtkTreeSortable *sortable,
+ gint sort_col_id,
+ GtkTreeIterCompareFunc sort_func,
+ gpointer user_data,
+ GDestroyNotify destroy_func);
+static void packet_list_sortable_set_default_sort_func(GtkTreeSortable
+ *sortable,
+ GtkTreeIterCompareFunc
+ sort_func,
+ gpointer user_data,
+ GDestroyNotify
+ destroy_func);
+static gboolean packet_list_sortable_has_default_sort_func(GtkTreeSortable
+ *sortable);
+static void packet_list_sortable_init(GtkTreeSortableIface *iface);
+static gint packet_list_compare_records(gint sort_id _U_, PacketListRecord *a,
+ PacketListRecord *b);
+static void packet_list_resort(PacketList *packet_list);
+static void packet_list_dissect_and_cache_record(PacketList *packet_list, PacketListRecord *record, gboolean dissect_columns, gboolean dissect_color );
+
+static GObjectClass *parent_class = NULL;
+
+
+GType
+packet_list_get_type(void)
+{
+ static GType packet_list_type = 0;
+
+ if(packet_list_type == 0) {
+ static const GTypeInfo packet_list_info = {
+ sizeof(PacketListClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) packet_list_class_init,
+ NULL, /* class finalize */
+ NULL, /* class_data */
+ sizeof(PacketList),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) packet_list_init,
+ NULL /* value_table */
+ };
+
+ static const GInterfaceInfo tree_model_info = {
+ (GInterfaceInitFunc) packet_list_tree_model_init,
+ NULL,
+ NULL
+ };
+
+ static const GInterfaceInfo tree_sortable_info = {
+ (GInterfaceInitFunc) packet_list_sortable_init,
+ NULL,
+ NULL
+ };
+
+ /* Register the new derived type with the GObject type system */
+ packet_list_type = g_type_register_static(G_TYPE_OBJECT,
+ "PacketList",
+ &packet_list_info,
+ (GTypeFlags)0);
+
+ g_type_add_interface_static(packet_list_type,
+ GTK_TYPE_TREE_MODEL,
+ &tree_model_info);
+
+
+ /* Register our GtkTreeModel interface with the type system */
+ g_type_add_interface_static(packet_list_type,
+ GTK_TYPE_TREE_SORTABLE,
+ &tree_sortable_info);
+ }
+
+ return packet_list_type;
+}
+
+static void
+packet_list_sortable_init(GtkTreeSortableIface *iface)
+{
+ iface->get_sort_column_id = packet_list_sortable_get_sort_column_id;
+ iface->set_sort_column_id = packet_list_sortable_set_sort_column_id;
+ /* The following three functions are not implemented */
+ iface->set_sort_func = packet_list_sortable_set_sort_func;
+ iface->set_default_sort_func =
+ packet_list_sortable_set_default_sort_func;
+ iface->has_default_sort_func =
+ packet_list_sortable_has_default_sort_func;
+}
+
+static void
+packet_list_class_init(PacketListClass *klass)
+{
+ GObjectClass *object_class;
+
+ parent_class = (GObjectClass*) g_type_class_peek_parent(klass);
+ object_class = (GObjectClass*) klass;
+
+ object_class->finalize = packet_list_finalize;
+
+#if !GTK_CHECK_VERSION(3,0,0)
+ /* XXX this seems to affect TreeView Application wide
+ * Move to main.c ??? as it's not a bad thing(tm)
+ */
+ gtk_rc_parse_string (
+ "style \"PacketList-style\"\n"
+ "{\n"
+ " GtkTreeView::horizontal-separator = 0\n"
+ " GtkTreeView::vertical-separator = 1\n"
+ "} widget_class \"*TreeView*\""
+ " style \"PacketList-style\"");
+#endif
+
+}
+
+static void
+packet_list_tree_model_init(GtkTreeModelIface *iface)
+{
+ iface->get_flags = packet_list_get_flags;
+ iface->get_n_columns = packet_list_get_n_columns;
+ iface->get_column_type = packet_list_get_column_type;
+ iface->get_iter = packet_list_get_iter;
+ iface->get_path = packet_list_get_path;
+ iface->get_value = packet_list_get_value;
+ iface->iter_next = packet_list_iter_next;
+ iface->iter_children = packet_list_iter_children;
+ iface->iter_has_child = packet_list_iter_has_child;
+ iface->iter_n_children = packet_list_iter_n_children;
+ iface->iter_nth_child = packet_list_iter_nth_child;
+ iface->iter_parent = packet_list_iter_parent;
+}
+
+/* This is called every time a new packet list object instance is created in
+ * packet_list_new. Initialize the list structure's fields here. */
+static void
+packet_list_init(PacketList *packet_list)
+{
+ guint i;
+
+ for(i = 0; i < (guint)cfile.cinfo.num_cols; i++) {
+ /* We get the packetlist record for all columns */
+ packet_list->column_types[i] = G_TYPE_STRING;
+ }
+
+ /* To check whether an iter belongs to our model. */
+ packet_list->stamp = g_random_int();
+
+ /* Note: We need one extra column to store the entire PacketListRecord */
+ packet_list->column_types[i] = G_TYPE_POINTER;
+ packet_list->n_columns = (guint)cfile.cinfo.num_cols+1;
+ packet_list->physical_rows = g_ptr_array_new();
+ packet_list->visible_rows = g_ptr_array_new();
+
+ packet_list->columnized = FALSE;
+ packet_list->sort_id = 0; /* defaults to first column for now */
+ packet_list->sort_order = GTK_SORT_ASCENDING;
+
+#ifdef NEW_PACKET_LIST_STATISTICS
+ packet_list->const_strings = 0;
+#endif
+}
+
+/* This function is called just before a packet list is destroyed. Free
+ * dynamically allocated memory here. */
+static void
+packet_list_finalize(GObject *object)
+{
+ /* PacketList *packet_list = PACKET_LIST(object); */
+
+ /* XXX - Free all records and free all memory used by the list */
+
+ /* must chain up - finalize parent */
+ (* parent_class->finalize) (object);
+}
+
+static GtkTreeModelFlags
+packet_list_get_flags(GtkTreeModel *tree_model)
+{
+ g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model),
+ (GtkTreeModelFlags)0);
+
+ return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
+}
+
+static gint
+packet_list_get_n_columns(GtkTreeModel *tree_model)
+{
+ g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
+
+ return PACKET_LIST(tree_model)->n_columns;
+}
+
+static GType
+packet_list_get_column_type(GtkTreeModel *tree_model, gint idx)
+{
+ PacketList *packet_list;
+ g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), G_TYPE_INVALID);
+ packet_list = PACKET_LIST(tree_model);
+ /* Note: We use one extra column to store the entire PacketListRecord */
+ g_return_val_if_fail(idx < packet_list->n_columns &&
+ idx >= 0, G_TYPE_INVALID);
+
+ return packet_list->column_types[idx];
+}
+
+static gboolean
+packet_list_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter,
+ GtkTreePath *path)
+{
+ PacketList *packet_list;
+ PacketListRecord *record;
+ gint *indices, depth;
+ gint n;
+
+ g_assert(PACKETLIST_IS_LIST(tree_model));
+ g_assert(path != NULL);
+
+ indices = gtk_tree_path_get_indices(path);
+ depth = gtk_tree_path_get_depth(path);
+
+ /* we do not allow children since it's just a list */
+ g_assert(depth == 1);
+
+ n = indices[0]; /* the n-th top level row */
+
+ packet_list = PACKET_LIST(tree_model);
+ if(PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
+ return FALSE;
+
+ if(!PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, n))
+ return FALSE;
+
+ record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, n);
+
+ g_assert(record->visible_pos == n);
+
+ /* We simply store a pointer to our custom record in the iter */
+ iter->stamp = packet_list->stamp;
+ iter->user_data = record;
+ iter->user_data2 = NULL;
+ iter->user_data3 = NULL;
+
+ return TRUE;
+}
+
+static GtkTreePath *
+packet_list_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter)
+{
+ GtkTreePath *path;
+ PacketListRecord *record;
+
+ g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), NULL);
+ g_return_val_if_fail(iter != NULL, NULL);
+ g_return_val_if_fail(iter->user_data != NULL, NULL);
+
+ record = (PacketListRecord*) iter->user_data;
+
+ path = gtk_tree_path_new();
+ gtk_tree_path_append_index(path, record->visible_pos);
+
+ return path;
+}
+
+static void
+packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column,
+ GValue *value)
+{
+ PacketListRecord *record;
+ PacketList *packet_list;
+ GType type;
+
+ g_return_if_fail(PACKETLIST_IS_LIST(tree_model));
+ g_return_if_fail(iter != NULL);
+
+ packet_list = PACKET_LIST(tree_model);
+ /* Note: We use one extra column to store the entire PacketListRecord */
+ g_return_if_fail(column < packet_list->n_columns);
+
+ record = (PacketListRecord*) iter->user_data;
+
+ g_return_if_fail(PACKET_LIST_RECORD_INDEX_VALID(packet_list->physical_rows, record->physical_pos));
+ g_return_if_fail(PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, record->visible_pos));
+
+ type = packet_list->column_types[column];
+ g_value_init(value, type);
+
+ /* XXX Probably the switch should be on column or
+ * should we allways return the pointer and read the data as required??
+ * If we use FOREGROUND_COLOR_COL etc we'll need a couple of "internal" columns
+ */
+ switch(type){
+ case G_TYPE_POINTER:
+ g_value_set_pointer(value, record);
+ break;
+ case G_TYPE_STRING:
+ g_return_if_fail(record->col_text);
+ g_value_set_string(value, record->col_text[column]);
+ break;
+ default:
+ g_warning (G_STRLOC ": Unsupported type (%s) retrieved.", g_type_name (value->g_type));
+ break;
+ }
+}
+
+static PacketListRecord *
+packet_list_iter_next_visible(PacketList *packet_list, PacketListRecord *record)
+{
+ PacketListRecord *nextrecord;
+ gint next_visible_pos;
+
+ g_assert(record->visible_pos >= 0);
+ next_visible_pos = record->visible_pos + 1;
+
+ /* Is this the last record in the list? */
+ if(!PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, next_visible_pos))
+ return NULL;
+
+ nextrecord = PACKET_LIST_RECORD_GET(packet_list->visible_rows, next_visible_pos);
+
+ g_assert(nextrecord->visible_pos == (record->visible_pos + 1));
+ g_assert(nextrecord->physical_pos >= (record->physical_pos + 1));
+
+ return nextrecord;
+}
+
+/* Takes an iter structure and sets it to point to the next row. */
+static gboolean
+packet_list_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
+{
+ PacketListRecord *record, *nextrecord;
+ PacketList *packet_list;
+
+ g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
+
+ if(iter == NULL)
+ return FALSE;
+
+ g_return_val_if_fail(iter->user_data, FALSE);
+
+ packet_list = PACKET_LIST(tree_model);
+
+ record = (PacketListRecord*) iter->user_data;
+ nextrecord = packet_list_iter_next_visible(packet_list, record);
+
+ if (!nextrecord)
+ return FALSE;
+
+ iter->stamp = packet_list->stamp;
+ iter->user_data = nextrecord;
+
+ return TRUE;
+}
+
+static gboolean
+packet_list_iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter,
+ GtkTreeIter *parent)
+{
+ PacketList *packet_list;
+
+ g_return_val_if_fail(parent == NULL || parent->user_data != NULL, FALSE);
+
+ /* This is a list, nodes have no children. */
+ if(parent)
+ return FALSE;
+
+ g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
+
+ packet_list = PACKET_LIST(tree_model);
+
+ /* No rows => no first row */
+ if(PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
+ return FALSE;
+
+ /* Set iter to first item in list */
+ iter->stamp = packet_list->stamp;
+ iter->user_data = PACKET_LIST_RECORD_GET(packet_list->visible_rows, 0);
+
+ return TRUE;
+}
+
+static gboolean
+packet_list_iter_has_child(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_)
+{
+ return FALSE; /* Lists have no children */
+}
+
+static gint
+packet_list_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
+{
+ PacketList *packet_list;
+
+ g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
+ g_return_val_if_fail(iter == NULL || iter->user_data != NULL, 0);
+
+ packet_list = PACKET_LIST(tree_model);
+
+ if(!iter) {
+ /* special case: if iter == NULL, return number of top-level rows */
+ return PACKET_LIST_RECORD_COUNT(packet_list->visible_rows);
+ }
+ else {
+ /* Lists have zero children */
+ return 0;
+ }
+}
+
+static gboolean
+packet_list_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter,
+ GtkTreeIter *parent, gint n)
+{
+ PacketListRecord *record;
+ PacketList *packet_list;
+
+ g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
+
+ packet_list = PACKET_LIST(tree_model);
+
+ /* A list only has top-level rows */
+ if(parent)
+ return FALSE;
+
+ /* Special case: if parent == NULL, set iter to n-th top-level row. */
+ if(!PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, n))
+ return FALSE;
+
+ record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, n);
+
+ g_assert(record->visible_pos == n);
+
+ iter->stamp = packet_list->stamp;
+ iter->user_data = record;
+
+ return TRUE;
+}
+
+static gboolean
+packet_list_iter_parent(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_,
+ GtkTreeIter *child _U_)
+{
+ return FALSE; /* No parents since no children in a list */
+}
+
+PacketList *
+new_packet_list_new(void)
+{
+ PacketList *newpacketlist;
+
+ newpacketlist = (PacketList*) g_object_new(PACKETLIST_TYPE_LIST, NULL);
+
+ g_assert(newpacketlist != NULL);
+
+ return newpacketlist;
+}
+
+#if 0
+static void
+packet_list_row_deleted(PacketList *packet_list, guint pos)
+{
+ GtkTreePath *path;
+
+ /* Inform the tree view and other interested objects (such as tree row
+ * references) that we have deleted a row */
+ path = gtk_tree_path_new();
+ gtk_tree_path_append_index(path, pos);
+
+ gtk_tree_model_row_deleted(GTK_TREE_MODEL(packet_list), path);
+
+ gtk_tree_path_free(path);
+}
+#endif
+
+void
+new_packet_list_store_clear(PacketList *packet_list)
+{
+ g_return_if_fail(packet_list != NULL);
+ g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
+
+ /* Don't issue a row_deleted signal. We rely on our caller to have disconnected
+ * the model from the view.
+ for( ; packet_list->num_rows > 0; --packet_list->num_rows)
+ packet_list_row_deleted(packet_list, packet_list->num_rows-1);
+ */
+
+ /* XXX - hold on to these rows and reuse them instead */
+ if(packet_list->physical_rows)
+ g_ptr_array_free(packet_list->physical_rows, TRUE);
+ if(packet_list->visible_rows)
+ g_ptr_array_free(packet_list->visible_rows, TRUE);
+ packet_list->physical_rows = g_ptr_array_new();
+ packet_list->visible_rows = g_ptr_array_new();
+
+ packet_list->columnized = FALSE;
+
+#ifdef NEW_PACKET_LIST_STATISTICS
+ g_warning("Const strings: %u", packet_list->const_strings);
+ packet_list->const_strings = 0;
+#endif
+}
+
+static void
+packet_list_row_inserted(PacketList *packet_list, guint pos)
+{
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ /* Inform the tree view and other interested objects (such as tree row
+ * references) that we have inserted a new row and where it was
+ * inserted. */
+ path = gtk_tree_path_new();
+ gtk_tree_path_append_index(path, pos);
+
+ packet_list_get_iter(GTK_TREE_MODEL(packet_list), &iter, path);
+
+ gtk_tree_model_row_inserted(GTK_TREE_MODEL(packet_list), path, &iter);
+
+ gtk_tree_path_free(path);
+}
+
+gboolean
+packet_list_visible_record(PacketList *packet_list, GtkTreeIter *iter)
+{
+ PacketListRecord *record;
+
+ g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), FALSE);
+
+ if(iter == NULL || iter->user_data == NULL)
+ return FALSE;
+
+ record = (PacketListRecord*) iter->user_data;
+
+ g_return_val_if_fail(record, FALSE);
+ g_return_val_if_fail(record->fdata, FALSE);
+
+ return (record->fdata->flags.passed_dfilter || record->fdata->flags.ref_time);
+}
+
+gint
+packet_list_append_record(PacketList *packet_list, frame_data *fdata)
+{
+ PacketListRecord *newrecord;
+ GtkTreeModel *model = GTK_TREE_MODEL(packet_list);
+
+ g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), -1);
+
+ newrecord = se_alloc(sizeof(PacketListRecord));
+ newrecord->columnized = FALSE;
+ newrecord->colorized = FALSE;
+ newrecord->col_text_len = se_alloc0(sizeof(*newrecord->col_text_len) * cfile.cinfo.num_cols);
+ newrecord->col_text = se_alloc0(sizeof(*newrecord->col_text) * cfile.cinfo.num_cols);
+ newrecord->fdata = fdata;
+ newrecord->physical_pos = PACKET_LIST_RECORD_COUNT(packet_list->physical_rows);
+
+ if (fdata->flags.passed_dfilter || fdata->flags.ref_time) {
+ newrecord->visible_pos = PACKET_LIST_RECORD_COUNT(packet_list->visible_rows);
+ PACKET_LIST_RECORD_APPEND(packet_list->visible_rows, newrecord);
+ }
+ else
+ newrecord->visible_pos = -1;
+
+ PACKET_LIST_RECORD_APPEND(packet_list->physical_rows, newrecord);
+
+ packet_list->columnized = FALSE; /* XXX, dissect? */
+
+ /*
+ * Issue a row_inserted signal if the model is connected
+ * and the row is visible.
+ */
+ if((model)&&(newrecord->visible_pos!=-1))
+ packet_list_row_inserted(packet_list, newrecord->visible_pos);
+
+ /* XXXX If the model is connected and sort column != frame_num we should
+ * probably resort.
+ * Don't resort the list for every row, the list will be in packet order any way.
+ * packet_list_resort(packet_list);
+ */
+
+ return newrecord->visible_pos;
+}
+
+void
+packet_list_change_record(PacketList *packet_list, guint row, gint col, column_info *cinfo)
+{
+ PacketListRecord *record;
+ gchar *str;
+
+ g_return_if_fail(packet_list);
+ g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
+
+ g_assert(row < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows));
+
+ record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, row);
+
+ g_assert(record->physical_pos == row);
+
+ g_assert((record->col_text != NULL)&&(record->col_text_len != NULL));
+
+ if (record->col_text[col] != NULL)
+ /* TODO: Column already contains a value. Bail out */
+ return;
+
+ switch (cfile.cinfo.col_fmt[col]) {
+ case COL_DEF_SRC:
+ case COL_RES_SRC: /* COL_DEF_SRC is currently just like COL_RES_SRC */
+ case COL_UNRES_SRC:
+ case COL_DEF_DL_SRC:
+ case COL_RES_DL_SRC:
+ case COL_UNRES_DL_SRC:
+ case COL_DEF_NET_SRC:
+ case COL_RES_NET_SRC:
+ case COL_UNRES_NET_SRC:
+ case COL_DEF_DST:
+ case COL_RES_DST: /* COL_DEF_DST is currently just like COL_RES_DST */
+ case COL_UNRES_DST:
+ case COL_DEF_DL_DST:
+ case COL_RES_DL_DST:
+ case COL_UNRES_DL_DST:
+ case COL_DEF_NET_DST:
+ case COL_RES_NET_DST:
+ case COL_UNRES_NET_DST:
+ case COL_PROTOCOL:
+ case COL_INFO:
+ case COL_IF_DIR:
+ case COL_DCE_CALL:
+ case COL_8021Q_VLAN_ID:
+ case COL_EXPERT:
+ case COL_FREQ_CHAN:
+ if (cinfo->col_data[col] && cinfo->col_data[col] != cinfo->col_buf[col]) {
+ /* This is a constant string, so we don't have to copy it */
+ record->col_text[col] = (gchar *) cinfo->col_data[col];
+ record->col_text_len[col] = (guint) strlen(record->col_text[col]);
+#ifdef NEW_PACKET_LIST_STATISTICS
+ ++packet_list->const_strings;
+#endif
+ break;
+ }
+ /* !! FALL-THROUGH!! */
+
+ default:
+ record->col_text_len[col] = (guint) strlen(cinfo->col_data[col]);
+
+ if (!record->col_text_len[col]) {
+ record->col_text[col] = "";
+#ifdef NEW_PACKET_LIST_STATISTICS
+ ++packet_list->const_strings;
+#endif
+ break;
+ }
+
+ if(!packet_list->string_pool)
+ packet_list->string_pool = g_string_chunk_new(32);
+ if (!get_column_resolved (col) && cinfo->col_expr.col_expr_val[col]) {
+ /* Use the unresolved value in col_expr_val */
+ str = g_string_chunk_insert_const (packet_list->string_pool, (const gchar *)cinfo->col_expr.col_expr_val[col]);
+ } else {
+ str = g_string_chunk_insert_const (packet_list->string_pool, (const gchar *)cinfo->col_data[col]);
+ }
+ record->col_text[col] = str;
+ break;
+ }
+}
+
+static gboolean
+packet_list_sortable_get_sort_column_id(GtkTreeSortable *sortable,
+ gint *sort_col_id,
+ GtkSortType *order)
+{
+ PacketList *packet_list;
+
+ g_return_val_if_fail(sortable != NULL, FALSE);
+ g_return_val_if_fail(PACKETLIST_IS_LIST(sortable), FALSE);
+
+ packet_list = PACKET_LIST(sortable);
+
+ if(sort_col_id)
+ *sort_col_id = packet_list->sort_id;
+
+ if(order)
+ *order = packet_list->sort_order;
+
+ return TRUE;
+}
+
+static gboolean
+packet_list_column_contains_values(PacketList *packet_list, gint sort_col_id)
+{
+ if (packet_list->columnized || col_based_on_frame_data(&cfile.cinfo, sort_col_id))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/* packet_list_dissect_and_cache_all()
+ * returns:
+ * TRUE if columnization completed;
+ * packet_list->columnized set to TRUE;
+ * FALSE: columnization did not complete (i.e., was stopped by the user);
+ * packet_list->columnized unchanged (i.e., FALSE).
+ */
+
+static gboolean
+packet_list_dissect_and_cache_all(PacketList *packet_list)
+{
+ PacketListRecord *record;
+
+ int progbar_nextstep;
+ int progbar_quantum;
+ gboolean progbar_stop_flag;
+ GTimeVal progbar_start_time;
+ float progbar_val;
+ progdlg_t *progbar = NULL;
+ gchar progbar_status_str[100];
+ gint progbar_loop_max;
+ gint progbar_loop_var;
+ gint progbar_updates = 100 /* 100% */;
+
+ g_assert(packet_list->columnized == FALSE);
+
+ progbar_loop_max = PACKET_LIST_RECORD_COUNT(packet_list->physical_rows);
+ /* Update the progress bar when it gets to this value. */
+ progbar_nextstep = 0;
+ /* When we reach the value that triggers a progress bar update,
+ bump that value by this amount. */
+ progbar_quantum = progbar_loop_max/progbar_updates;
+ /* Progress so far. */
+ progbar_val = 0.0f;
+
+ progbar_stop_flag = FALSE;
+ g_get_current_time(&progbar_start_time);
+
+ main_window_update();
+
+ for (progbar_loop_var = 0; progbar_loop_var < progbar_loop_max; ++progbar_loop_var) {
+ record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, progbar_loop_var);
+ packet_list_dissect_and_cache_record(packet_list, record, TRUE, FALSE);
+
+ /* Create the progress bar if necessary.
+ We check on every iteration of the loop, so that it takes no
+ longer than the standard time to create it (otherwise, for a
+ large file, we might take considerably longer than that standard
+ time in order to get to the next progress bar step). */
+ if (progbar == NULL)
+ /* Note: The following may call gtk_main_iteration() which will */
+ /* allow certain "interupts" to happen during this code. */
+ /* (Note that the progress_dlg window is set to "modal" */
+ /* so that clicking on other windows is disabled). */
+ progbar = delayed_create_progress_dlg("Construct", "Columns",
+ TRUE, &progbar_stop_flag,
+ &progbar_start_time, progbar_val);
+
+ if (progbar_loop_var >= progbar_nextstep) {
+ /* let's not divide by zero. We should never be started
+ * with count == 0, so let's assert that */
+ g_assert(progbar_loop_max > 0);
+
+ progbar_val = (gfloat) progbar_loop_var / progbar_loop_max;
+
+ if (progbar != NULL) {
+ g_snprintf(progbar_status_str, sizeof(progbar_status_str),
+ "%u of %u frames", progbar_loop_var+1, progbar_loop_max);
+ /* Note: See comment above re use of gtk_main_iteration() */
+ update_progress_dlg(progbar, progbar_val, progbar_status_str);
+ }
+
+ progbar_nextstep += progbar_quantum;
+ }
+
+ if (progbar_stop_flag) {
+ /* Well, the user decided to abort ... */
+ break;
+ }
+ }
+
+ /* We're done; destroy the progress bar if it was created. */
+ if (progbar != NULL)
+ destroy_progress_dlg(progbar);
+
+ if (progbar_stop_flag) {
+ return FALSE; /* user aborted before columnization completed */
+ }
+
+ packet_list->columnized = TRUE;
+ return TRUE;
+}
+
+/* packet_list_do_packet_list_dissect_and_cache_all()
+ * returns:
+ * TRUE: if columnization not needed or columnization completed;
+ * FALSE: columnization did not complete (i.e., stopped by the user)
+ */
+gboolean
+packet_list_do_packet_list_dissect_and_cache_all(PacketList *packet_list, gint sort_col_id)
+{
+ if (!packet_list_column_contains_values(packet_list, sort_col_id)) {
+ return packet_list_dissect_and_cache_all(packet_list);
+ }
+ return TRUE;
+}
+
+static void
+packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
+ gint sort_col_id,
+ GtkSortType order)
+{
+ PacketList *packet_list;
+
+ g_return_if_fail(sortable != NULL);
+ g_return_if_fail(PACKETLIST_IS_LIST(sortable));
+
+ packet_list = PACKET_LIST(sortable);
+
+ if(packet_list->sort_id == sort_col_id &&
+ packet_list->sort_order == order)
+ return;
+
+ packet_list->sort_id = sort_col_id;
+ packet_list->sort_order = order;
+
+ if(PACKET_LIST_RECORD_COUNT(packet_list->physical_rows) == 0)
+ return;
+
+ packet_list_resort(packet_list);
+
+ /* emit "sort-column-changed" signal to tell any tree views
+ * that the sort column has changed (so the little arrow
+ * in the column header of the sort column is drawn
+ * in the right column) */
+
+ gtk_tree_sortable_sort_column_changed(sortable);
+}
+
+static void
+packet_list_sortable_set_sort_func(GtkTreeSortable *sortable _U_,
+ gint sort_col_id _U_,
+ GtkTreeIterCompareFunc sort_func _U_,
+ gpointer user_data _U_,
+ GDestroyNotify destroy_func _U_)
+{
+ g_warning(G_STRLOC ": is not supported by the PacketList model.\n");
+}
+
+static void
+packet_list_sortable_set_default_sort_func(GtkTreeSortable *sortable _U_,
+ GtkTreeIterCompareFunc sort_func _U_,
+ gpointer user_data _U_,
+ GDestroyNotify destroy_func _U_)
+{
+ g_warning(G_STRLOC ": is not supported by the PacketList model.\n");
+}
+
+static gboolean
+packet_list_sortable_has_default_sort_func(GtkTreeSortable *sortable _U_)
+{
+ return FALSE; /* Since packet_list_sortable_set_sort_func and
+ set_default_sort_func are not implemented. */
+}
+
+static gint
+packet_list_compare_custom(gint sort_id, PacketListRecord *a, PacketListRecord *b)
+{
+ header_field_info *hfi;
+
+ hfi = proto_registrar_get_byname(cfile.cinfo.col_custom_field[sort_id]);
+
+ if (hfi == NULL) {
+ return frame_data_compare(a->fdata, b->fdata, COL_NUMBER);
+ } else if ((hfi->strings == NULL) &&
+ (((IS_FT_INT(hfi->type) || IS_FT_UINT(hfi->type)) &&
+ ((hfi->display == BASE_DEC) || (hfi->display == BASE_DEC_HEX) ||
+ (hfi->display == BASE_OCT))) ||
+ (hfi->type == FT_DOUBLE) || (hfi->type == FT_FLOAT) ||
+ (hfi->type == FT_BOOLEAN) || (hfi->type == FT_FRAMENUM) ||
+ (hfi->type == FT_RELATIVE_TIME)))
+ {
+ /* Attempt to convert to numbers */
+ double num_a = atof(a->col_text[sort_id]);
+ double num_b = atof(b->col_text[sort_id]);
+
+ if (num_a < num_b)
+ return -1;
+ else if (num_a > num_b)
+ return 1;
+ else
+ return frame_data_compare(a->fdata, b->fdata, COL_NUMBER);
+ }
+
+ return strcmp(a->col_text[sort_id], b->col_text[sort_id]);
+}
+
+static gint
+_packet_list_compare_records(gint sort_id, PacketListRecord *a,
+ PacketListRecord *b)
+{
+ g_assert(a->col_text);
+ g_assert(b->col_text);
+ g_assert(a->col_text[sort_id]);
+ g_assert(b->col_text[sort_id]);
+
+ if(a->col_text[sort_id] == b->col_text[sort_id])
+ return 0; /* no need to call strcmp() */
+
+ if (cfile.cinfo.col_fmt[sort_id] == COL_CUSTOM) {
+ return packet_list_compare_custom (sort_id, a, b);
+ }
+ return strcmp(a->col_text[sort_id], b->col_text[sort_id]);
+}
+
+static gint
+packet_list_compare_records(gint sort_id, PacketListRecord *a,
+ PacketListRecord *b)
+{
+ gint ret;
+
+ if (col_based_on_frame_data(&cfile.cinfo, sort_id))
+ return frame_data_compare(a->fdata, b->fdata, cfile.cinfo.col_fmt[sort_id]);
+
+ ret = _packet_list_compare_records(sort_id, a, b);
+ if (ret == 0)
+ ret = a->fdata->num - b->fdata->num;
+ return ret;
+}
+
+static gint
+packet_list_qsort_physical_compare_func(PacketListRecord **a, PacketListRecord **b,
+ PacketList *packet_list)
+{
+ gint ret;
+
+ g_assert((a) && (b) && (packet_list));
+
+ ret = packet_list_compare_records(packet_list->sort_id, *a, *b);
+
+ /* Swap -1 and 1 if sort order is reverse */
+ if(ret != 0 && packet_list->sort_order == GTK_SORT_DESCENDING)
+ ret = (ret < 0) ? 1 : -1;
+
+ return ret;
+}
+
+static void
+packet_list_resort(PacketList *packet_list)
+{
+ PacketListRecord *record;
+ GtkTreePath *path;
+ gint *neworder;
+ guint phy_idx;
+ guint vis_idx;
+
+ g_return_if_fail(packet_list != NULL);
+ g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
+
+ if(PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
+ return;
+
+ /* resort physical rows according to sorting column */
+ g_ptr_array_sort_with_data(packet_list->physical_rows,
+ (GCompareDataFunc) packet_list_qsort_physical_compare_func,
+ packet_list);
+
+ /* let other objects know about the new order */
+ neworder = g_new0(gint, PACKET_LIST_RECORD_COUNT(packet_list->visible_rows));
+
+ for(phy_idx = 0, vis_idx = 0; phy_idx < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++phy_idx) {
+ record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, phy_idx);
+ record->physical_pos = phy_idx;
+ g_assert(record->visible_pos >= -1);
+ if (record->visible_pos >= 0) {
+ g_assert(record->fdata->flags.passed_dfilter || record->fdata->flags.ref_time);
+ neworder[vis_idx] = record->visible_pos;
+ PACKET_LIST_RECORD_SET(packet_list->visible_rows, vis_idx, record);
+ record->visible_pos = vis_idx;
+ ++vis_idx;
+ }
+ }
+
+ g_assert(vis_idx == PACKET_LIST_RECORD_COUNT(packet_list->visible_rows));
+
+ path = gtk_tree_path_new();
+
+ gtk_tree_model_rows_reordered(GTK_TREE_MODEL(packet_list), path, NULL,
+ neworder);
+
+ gtk_tree_path_free(path);
+ g_free(neworder);
+}
+
+guint
+packet_list_recreate_visible_rows(PacketList *packet_list)
+{
+ guint phy_idx;
+ guint vis_idx;
+ PacketListRecord *record;
+
+ g_return_val_if_fail(packet_list != NULL, 0);
+ g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), 0);
+
+ if(PACKET_LIST_RECORD_COUNT(packet_list->physical_rows) == 0)
+ return 0;
+
+ if(packet_list->visible_rows)
+ g_ptr_array_free(packet_list->visible_rows, TRUE);
+
+ packet_list->visible_rows = g_ptr_array_new();
+
+ for(phy_idx = 0, vis_idx = 0; phy_idx < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++phy_idx) {
+ record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, phy_idx);
+ if (record->fdata->flags.passed_dfilter || record->fdata->flags.ref_time) {
+ record->visible_pos = vis_idx++;
+ PACKET_LIST_RECORD_APPEND(packet_list->visible_rows, record);
+ }
+ else
+ record->visible_pos = -1;
+ }
+
+ return vis_idx;
+}
+
+void
+packet_list_dissect_and_cache_iter(PacketList *packet_list, GtkTreeIter *iter, gboolean dissect_columns, gboolean dissect_color)
+{
+ PacketListRecord *record;
+
+ g_return_if_fail(packet_list != NULL);
+ g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
+ g_return_if_fail(iter != NULL);
+ g_return_if_fail(iter->user_data != NULL);
+
+ record = iter->user_data;
+
+ packet_list_dissect_and_cache_record(packet_list, record, dissect_columns, dissect_color);
+}
+
+static void
+packet_list_dissect_and_cache_record(PacketList *packet_list, PacketListRecord *record, gboolean dissect_columns, gboolean dissect_color)
+{
+ epan_dissect_t edt;
+ frame_data *fdata;
+ column_info *cinfo;
+ gint col;
+ gboolean create_proto_tree;
+ union wtap_pseudo_header pseudo_header; /* Packet pseudo_header */
+ guint8 pd[WTAP_MAX_PACKET_SIZE]; /* Packet data */
+
+ /* XXX: Does it work to check if the record is already columnized/colorized ?
+ * i.e.: test record->columnized and record->colorized and just return
+ * if they're both TRUE.
+ * Note: Part of the patch submitted with Bug #4273 had code to do this but it
+ * was commented out in the patch and was not included in SVN #33011.
+ */
+ fdata = record->fdata;
+
+ if (dissect_columns)
+ cinfo = &cfile.cinfo;
+ else
+ cinfo = NULL;
+
+ if (!cf_read_frame_r(&cfile, fdata, &pseudo_header, pd)) {
+ /*
+ * Error reading the frame.
+ *
+ * Don't set the color filter for now (we might want
+ * to colorize it in some fashion to warn that the
+ * row couldn't be filled in or colorized), and
+ * set the columns to placeholder values, except
+ * for the Info column, where we'll put in an
+ * error message.
+ */
+ if (dissect_columns) {
+ col_fill_in_error(cinfo, fdata, FALSE, FALSE /* fill_fd_columns */);
+
+ for(col = 0; col < cinfo->num_cols; ++col) {
+ /* Skip columns based on frame_data because we already store those. */
+ if (!col_based_on_frame_data(cinfo, col))
+ packet_list_change_record(packet_list, record->physical_pos, col, cinfo);
+ }
+ record->columnized = TRUE;
+ }
+ if (dissect_color) {
+ fdata->color_filter = NULL;
+ record->colorized = TRUE;
+ }
+ return; /* error reading the frame */
+ }
+
+ create_proto_tree = (color_filters_used() && dissect_color) ||
+ (have_custom_cols(cinfo) && dissect_columns);
+
+ epan_dissect_init(&edt,
+ create_proto_tree,
+ FALSE /* proto_tree_visible */);
+
+ if (dissect_color)
+ color_filters_prime_edt(&edt);
+ if (dissect_columns)
+ col_custom_prime_edt(&edt, cinfo);
+
+ /*
+ * XXX - need to catch an OutOfMemoryError exception and
+ * attempt to recover from it.
+ */
+ epan_dissect_run(&edt, &pseudo_header, pd, fdata, cinfo);
+
+ if (dissect_color)
+ fdata->color_filter = color_filters_colorize_packet(&edt);
+
+ if (dissect_columns) {
+ /* "Stringify" non frame_data vals */
+ epan_dissect_fill_in_columns(&edt, FALSE, FALSE /* fill_fd_columns */);
+
+ for(col = 0; col < cinfo->num_cols; ++col) {
+ /* Skip columns based on frame_data because we already store those. */
+ if (!col_based_on_frame_data(cinfo, col))
+ packet_list_change_record(packet_list, record->physical_pos, col, cinfo);
+ }
+ }
+
+ if (dissect_columns)
+ record->columnized = TRUE;
+ if (dissect_color)
+ record->colorized = TRUE;
+
+ epan_dissect_cleanup(&edt);
+}
+
+void
+packet_list_reset_colorized(PacketList *packet_list)
+{
+ PacketListRecord *record;
+ guint i;
+
+ for(i = 0; i < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++i) {
+ record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, i);
+ record->colorized = FALSE;
+ }
+}
+
+const char*
+packet_list_get_widest_column_string(PacketList *packet_list, gint col)
+{
+ g_return_val_if_fail(packet_list != NULL, NULL);
+ g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), NULL);
+ g_return_val_if_fail(col < packet_list->n_columns && col >= 0, NULL);
+
+ if (PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
+ return "";
+
+ if (col_based_on_frame_data(&cfile.cinfo, col)) {
+ PacketListRecord *record;
+ guint vis_idx;
+
+ frame_data fdata;
+ memset (&fdata, 0, sizeof fdata);
+
+ nstime_set_zero(&fdata.abs_ts);
+ nstime_set_zero(&fdata.rel_ts);
+ nstime_set_zero(&fdata.del_cap_ts);
+ nstime_set_zero(&fdata.del_dis_ts);
+
+ for(vis_idx = 0; vis_idx < PACKET_LIST_RECORD_COUNT(packet_list->visible_rows); ++vis_idx) {
+ record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, vis_idx);
+ switch (cfile.cinfo.col_fmt[col]) {
+
+ case COL_NUMBER:
+ if (record->fdata->num > fdata.num)
+ fdata.num = record->fdata->num;
+ break;
+ case COL_PACKET_LENGTH:
+ if (record->fdata->pkt_len > fdata.pkt_len)
+ fdata.pkt_len = record->fdata->pkt_len;
+ break;
+ case COL_CUMULATIVE_BYTES:
+ if (record->fdata->cum_bytes > fdata.cum_bytes)
+ fdata.cum_bytes = record->fdata->cum_bytes;
+ break;
+ case COL_ABS_TIME:
+ case COL_ABS_DATE_TIME:
+ case COL_UTC_TIME:
+ case COL_UTC_DATE_TIME:
+ if (nstime_cmp(&record->fdata->abs_ts, &fdata.abs_ts) > 0)
+ fdata.abs_ts = record->fdata->abs_ts;
+ break;
+ case COL_REL_TIME:
+ if (nstime_cmp(&record->fdata->rel_ts, &fdata.rel_ts) > 0)
+ fdata.rel_ts = record->fdata->rel_ts;
+ break;
+ case COL_DELTA_TIME:
+ if (nstime_cmp(&record->fdata->del_cap_ts, &fdata.del_cap_ts) > 0)
+ fdata.del_cap_ts = record->fdata->del_cap_ts;
+ break;
+ case COL_DELTA_TIME_DIS:
+ if (nstime_cmp(&record->fdata->del_dis_ts, &fdata.del_dis_ts) > 0)
+ fdata.del_dis_ts = record->fdata->del_dis_ts;
+ break;
+ case COL_CLS_TIME:
+ switch (timestamp_get_type()) {
+ case TS_ABSOLUTE:
+ case TS_ABSOLUTE_WITH_DATE:
+ case TS_UTC:
+ case TS_UTC_WITH_DATE:
+ if (nstime_cmp(&record->fdata->abs_ts, &fdata.abs_ts) > 0)
+ fdata.abs_ts = record->fdata->abs_ts;
+ break;
+
+ case TS_RELATIVE:
+ if (nstime_cmp(&record->fdata->rel_ts, &fdata.rel_ts) > 0)
+ fdata.rel_ts = record->fdata->rel_ts;
+ break;
+
+ case TS_DELTA:
+ if (nstime_cmp(&record->fdata->del_cap_ts, &fdata.del_cap_ts) > 0)
+ fdata.del_cap_ts = record->fdata->del_cap_ts;
+ break;
+
+ case TS_DELTA_DIS:
+ if (nstime_cmp(&record->fdata->del_dis_ts, &fdata.del_dis_ts) > 0)
+ fdata.del_dis_ts = record->fdata->del_dis_ts;
+ break;
+
+ case TS_EPOCH:
+ if (nstime_cmp(&record->fdata->abs_ts, &fdata.abs_ts) > 0)
+ fdata.abs_ts = record->fdata->abs_ts;
+ break;
+
+ case TS_NOT_SET:
+ /* code is missing for this case, but I don't know which [jmayer20051219] */
+ g_assert_not_reached();
+ break;
+ }
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+ }
+
+ col_fill_in_frame_data(&fdata, &cfile.cinfo, col, FALSE);
+
+ return cfile.cinfo.col_buf[col];
+ }
+ else {
+ PacketListRecord *record;
+ guint vis_idx;
+
+ gchar *widest_column_str = NULL;
+ guint widest_column_len = 0;
+
+ if (!packet_list->columnized)
+ packet_list_dissect_and_cache_all(packet_list); /* XXX: need to handle case of "incomplete" ? */
+
+ for(vis_idx = 0; vis_idx < PACKET_LIST_RECORD_COUNT(packet_list->visible_rows); ++vis_idx) {
+ record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, vis_idx);
+ if (record->col_text_len[col] > widest_column_len) {
+ widest_column_str = record->col_text[col];
+ widest_column_len = record->col_text_len[col];
+ }
+ }
+
+ return widest_column_str;
+ }
+}
diff --git a/ui/gtk/packet_list_store.h b/ui/gtk/packet_list_store.h
new file mode 100644
index 0000000000..34ce0dfa94
--- /dev/null
+++ b/ui/gtk/packet_list_store.h
@@ -0,0 +1,130 @@
+/* packet_list_store.h
+ *
+ * $Id$
+ *
+ * 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.
+ */
+
+/* Uncomment to track some statistics (const strings, etc.) */
+/* #define NEW_PACKET_LIST_STATISTICS */
+
+#ifndef __PACKET_LIST_STORE_H__
+#define __PACKET_LIST_STORE_H__
+
+#include <glib.h>
+
+#include "epan/column_info.h"
+#include "epan/frame_data.h"
+
+/** @file
+ * The packet list store
+ * @ingroup main_window_group
+ */
+extern GType packet_list_get_type(void);
+#define PACKETLIST_TYPE_LIST (packet_list_get_type())
+#define PACKET_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PACKETLIST_TYPE_LIST, PacketList))
+#define PACKETLIST_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CART((klass), PACKETLIST_TYPE_LIST))
+#define PACKETLIST_IS_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PACKETLIST_TYPE_LIST))
+#define PACKETLIST_IS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE(klass), PACKETLIST_TYPE_LIST)
+#define PACKETLIST_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PACKETLIST_TYPE_LIST, PacketListClass))
+
+typedef struct _PacketListRecord PacketListRecord;
+typedef struct _PacketList PacketList;
+typedef struct _PacketListClass PacketListClass;
+
+#define PACKET_LIST_RECORD_GET(rows, pos) ((PacketListRecord*) g_ptr_array_index((rows), (pos)))
+#define PACKET_LIST_RECORD_SET(rows, pos, item) g_ptr_array_index((rows), (pos)) = (item)
+#define PACKET_LIST_RECORD_APPEND(rows, item) g_ptr_array_add((rows), (item))
+#define PACKET_LIST_RECORD_COUNT(rows) ((rows) ? (rows)->len : 0)
+#define PACKET_LIST_RECORD_INDEX_VALID(rows, idx) ((rows) ? (((guint) (idx)) < (rows)->len) : FALSE)
+
+/** PacketListRecord: represents a row */
+struct _PacketListRecord
+{
+ /** The column text for some columns */
+ gchar **col_text;
+ /**< The length of the column text strings in 'col_text' */
+ guint *col_text_len;
+
+ frame_data *fdata;
+
+ /** Has this record been columnized? */
+ gboolean columnized;
+ /** Has this record been colorized? */
+ gboolean colorized;
+
+ /* admin stuff used by the custom list model */
+ /** position within the physical array */
+ guint physical_pos;
+ /** position within the visible array */
+ gint visible_pos;
+};
+
+/** PacketList: Everything for our model implementation. */
+struct _PacketList
+{
+ GObject parent; /** MUST be first */
+
+ /** Array of pointers to the PacketListRecord structure for each visible row. */
+ GPtrArray *visible_rows;
+ /** Array of pointers to the PacketListRecord structure for each row. */
+ GPtrArray *physical_rows;
+
+ /** Has the entire file been columnized? */
+ gboolean columnized;
+
+ gint n_columns;
+ /**< Note: We need one extra column to store the entire PacketListRecord */
+ GType column_types[NUM_COL_FMTS+1];
+ GtkWidget *view; /**< @todo XXX - Does this really belong here?? */
+
+ gint sort_id;
+ GtkSortType sort_order;
+
+ GStringChunk *string_pool;
+
+ /** Random integer to check whether an iter belongs to our model. */
+ gint stamp;
+
+#ifdef NEW_PACKET_LIST_STATISTICS
+ /** Statistics */
+ guint const_strings;
+#endif
+};
+
+/** PacketListClass: more boilerplate GObject stuff */
+struct _PacketListClass
+{
+ GObjectClass parent_class;
+};
+
+GType packet_list_list_get_type(void);
+PacketList *new_packet_list_new(void);
+void new_packet_list_store_clear(PacketList *packet_list);
+guint packet_list_recreate_visible_rows(PacketList *packet_list);
+gboolean packet_list_visible_record(PacketList *packet_list, GtkTreeIter *iter);
+gint packet_list_append_record(PacketList *packet_list, frame_data *fdata);
+void packet_list_change_record(PacketList *packet_list, guint row, gint col, column_info *cinfo);
+void packet_list_dissect_and_cache_iter(PacketList *packet_list, GtkTreeIter *iter, gboolean dissect_columns, gboolean dissect_color);
+gboolean packet_list_do_packet_list_dissect_and_cache_all(PacketList *packet_list, gint sort_col_id);
+void packet_list_reset_colorized(PacketList *packet_list);
+const char* packet_list_get_widest_column_string(PacketList *packet_list, gint col);
+
+#endif /* __PACKET_LIST_STORE_H__ */
diff --git a/ui/gtk/packet_win.c b/ui/gtk/packet_win.c
new file mode 100644
index 0000000000..44e504e339
--- /dev/null
+++ b/ui/gtk/packet_win.c
@@ -0,0 +1,1113 @@
+/* packet_win.c
+ * Routines for popping a window to display current packet
+ *
+ * Copyright 2000, Jeffrey C. Foster <jfoste@woodward.com>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * To do:
+ * - Add close button to bottom.
+ * - improve the window Title and allow user to config it
+ * - Add print support ? ( could be a mess)
+ * - Add button to have main window jump to this packet ?
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#if GTK_CHECK_VERSION(3,0,0)
+# include <gdk/gdkkeysyms-compat.h>
+#endif
+
+#include <string.h>
+
+#include <epan/epan.h>
+#include <epan/timestamp.h>
+#include <epan/packet.h>
+#include <epan/prefs.h>
+#include <epan/column.h>
+#include <epan/addr_resolv.h>
+#include <epan/plugins.h>
+#include <epan/epan_dissect.h>
+#include <epan/strutil.h>
+#include <epan/tvbuff-int.h>
+
+#include "../file.h"
+#include "../print.h"
+#include "../ui_util.h"
+#include "../summary.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/font_utils.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/packet_win.h"
+#include "ui/gtk/main_proto_draw.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/recent.h"
+
+#define BV_SIZE 75
+#define TV_SIZE 95
+
+/* Data structure holding information about a packet-detail window. */
+struct PacketWinData {
+ frame_data *frame; /* The frame being displayed */
+ union wtap_pseudo_header pseudo_header; /* Pseudo-header for packet */
+ guint8 *pd; /* Data for packet */
+ GtkWidget *main;
+ GtkWidget *tv_scrollw;
+ GtkWidget *tree_view;
+ GtkWidget *bv_nb_ptr;
+ field_info *finfo_selected;
+ epan_dissect_t edt;
+
+ int pd_offset;
+ int pd_bitoffset;
+};
+
+#ifdef WANT_PACKET_EDITOR
+struct FieldinfoWinData {
+ frame_data *frame; /* The frame being displayed */
+ union wtap_pseudo_header pseudo_header; /* Pseudo-header for packet */
+ guint8 *pd; /* Data for packet */
+ int start_offset;
+
+ field_info *finfo;
+/* fvalue */
+ GtkWidget *edit;
+ GtkWidget *repr;
+/* byteviews */
+ GtkWidget *bv;
+ GtkWidget *app_bv;
+
+ int pd_offset;
+ int pd_bitoffset;
+};
+
+struct CommonWinData {
+ frame_data *frame; /* The frame being displayed */
+ guint8 *pd; /* Data for packet */
+
+ int pd_offset;
+ int pd_bitoffset;
+ int val;
+};
+
+static gboolean edit_pkt_common_key_pressed_cb(GdkEventKey *event, struct CommonWinData *DataPtr);
+#endif
+
+/* List of all the packet-detail windows popped up. */
+static GList *detail_windows;
+
+static void new_tree_view_selection_changed_cb(GtkTreeSelection *sel,
+ gpointer user_data);
+
+
+static void destroy_new_window(GObject *object, gpointer user_data);
+
+static gboolean
+button_press_handler(GtkWidget *widget, GdkEvent *event, gpointer data _U_)
+{
+ if (widget == NULL || event == NULL) {
+ return FALSE;
+ }
+
+ tree_view_select(widget, (GdkEventButton *) event);
+
+ /* GDK_2BUTTON_PRESS is a doubleclick -> expand/collapse tree row */
+ if (event->type == GDK_2BUTTON_PRESS) {
+ GtkTreePath *path;
+
+ if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
+ (gint) (((GdkEventButton *)event)->x),
+ (gint) (((GdkEventButton *)event)->y),
+ &path, NULL, NULL, NULL))
+ {
+ if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(widget), path)) {
+ gtk_tree_view_collapse_row(GTK_TREE_VIEW(widget), path);
+ } else {
+ gtk_tree_view_expand_row(GTK_TREE_VIEW(widget), path, FALSE);
+ }
+ gtk_tree_path_free(path);
+ }
+ }
+
+ return FALSE;
+}
+
+#ifdef WANT_PACKET_EDITOR
+static field_info *
+proto_finfo_find(proto_tree *tree, field_info *old_finfo)
+{
+ proto_node *node;
+
+ for (node = tree->first_child; node != NULL; node = node->next) {
+ field_info *cur = PNODE_FINFO(node);
+
+ if (!cur)
+ continue;
+
+ /* check everything, if it doesn't work report to me */
+ if (cur->hfinfo == old_finfo->hfinfo &&
+ cur->start == old_finfo->start && cur->length == old_finfo->length &&
+ cur->appendix_start == old_finfo->appendix_start && cur->appendix_length == old_finfo->appendix_length &&
+ cur->tree_type == old_finfo->tree_type && cur->flags == old_finfo->flags)
+ {
+ return cur;
+ }
+
+ if ((cur = proto_finfo_find((proto_tree *)node, old_finfo)))
+ return cur;
+ }
+ return NULL;
+}
+
+static gboolean
+finfo_window_refresh(struct FieldinfoWinData *DataPtr)
+{
+ field_info *old_finfo = DataPtr->finfo;
+ field_info *finfo;
+ epan_dissect_t edt;
+
+ const guint8 *data;
+ GtkWidget *byte_view;
+ gchar label_str[ITEM_LABEL_LENGTH];
+
+ /* always update byteviews */
+ if (DataPtr->bv && (byte_view = get_notebook_bv_ptr(DataPtr->bv))) {
+ int pos_inside = DataPtr->pd_offset - DataPtr->start_offset - old_finfo->start;
+
+ if (pos_inside < 0 || pos_inside >= old_finfo->length)
+ pos_inside = -1;
+
+ data = DataPtr->pd + DataPtr->start_offset + old_finfo->start;
+ packet_hex_editor_print(byte_view, data, DataPtr->frame, pos_inside, DataPtr->pd_bitoffset, old_finfo->length);
+ }
+
+ if (DataPtr->app_bv && (byte_view = get_notebook_bv_ptr(DataPtr->app_bv))) {
+ int pos_inside = DataPtr->pd_offset - DataPtr->start_offset - old_finfo->appendix_start;
+
+ if (pos_inside < 0 || pos_inside >= old_finfo->appendix_length)
+ pos_inside = -1;
+
+ data = DataPtr->pd + DataPtr->start_offset + old_finfo->appendix_start;
+ packet_hex_editor_print(byte_view, data, DataPtr->frame, pos_inside, DataPtr->pd_bitoffset, old_finfo->appendix_length);
+ }
+
+ /* redisect */
+ epan_dissect_init(&edt, TRUE, TRUE);
+ /* Makes any sense?
+ if (old_finfo->hfinfo)
+ proto_tree_prime_hfid(edt.tree, old_finfo->hfinfo->id);
+ */
+ epan_dissect_run(&edt, &DataPtr->pseudo_header, DataPtr->pd, DataPtr->frame, NULL);
+
+ /* Try to find finfo which looks like old_finfo.
+ * We might not found one, if protocol requires specific magic values, etc... */
+ if (!(finfo = proto_finfo_find(edt.tree, old_finfo))) {
+ epan_dissect_cleanup(&edt);
+ gtk_entry_set_text(GTK_ENTRY(DataPtr->repr), "[finfo not found, try with another value, or restore old. If you think it is bug, fill bugreport]");
+ return FALSE;
+ }
+
+ /* XXX, update fvalue_edit, e.g. when hexedit was changed */
+
+ if (finfo->rep == NULL) {
+ proto_item_fill_label(finfo, label_str);
+ gtk_entry_set_text(GTK_ENTRY(DataPtr->repr), label_str);
+ } else
+ gtk_entry_set_text(GTK_ENTRY(DataPtr->repr), finfo->rep->representation);
+
+ epan_dissect_cleanup(&edt);
+ return TRUE;
+}
+
+static void
+finfo_integer_common(struct FieldinfoWinData *DataPtr, guint64 u_val)
+{
+ const field_info *finfo = DataPtr->finfo;
+ const header_field_info *hfinfo = finfo->hfinfo;
+ /* XXX, appendix? */
+ unsigned int finfo_offset = DataPtr->start_offset + finfo->start;
+ int finfo_length = finfo->length;
+
+ if (finfo_offset <= DataPtr->frame->cap_len && finfo_offset + finfo_length <= DataPtr->frame->cap_len) {
+ guint32 u_mask = hfinfo->bitmask;
+
+ while (finfo_length--) {
+ guint8 *ptr = (FI_GET_FLAG(finfo, FI_LITTLE_ENDIAN)) ?
+ &(DataPtr->pd[finfo_offset++]) :
+ &(DataPtr->pd[finfo_offset + finfo_length]);
+
+ if (u_mask) {
+ guint8 n_val = *ptr;
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ if (u_mask & 1) {
+ if (u_val & 1)
+ n_val |= (1 << i);
+ else
+ n_val &= ~(1 << i);
+ }
+ u_mask >>= 1;
+ u_val >>= 1;
+ }
+ *ptr = n_val;
+
+ if (!u_mask)
+ break;
+ } else {
+ *ptr = u_val & 0xff;
+ u_val >>= 8;
+ }
+ }
+ }
+ finfo_window_refresh(DataPtr);
+}
+
+static void
+finfo_string_changed(GtkEditable *editable, gpointer user_data)
+{
+ struct FieldinfoWinData *DataPtr = (struct FieldinfoWinData *) user_data;
+
+ /* XXX, appendix? */
+ const field_info *finfo = DataPtr->finfo;
+ unsigned int finfo_offset = DataPtr->start_offset + finfo->start;
+ int finfo_length = finfo->length;
+ int finfo_type = (finfo->hfinfo) ? finfo->hfinfo->type : FT_NONE;
+
+ const gchar *val = gtk_entry_get_text(GTK_ENTRY(editable));
+
+ if (finfo_offset <= DataPtr->frame->cap_len && finfo_offset + finfo_length <= DataPtr->frame->cap_len) {
+ /* strncpy */
+ while (finfo_length && *val) {
+ DataPtr->pd[finfo_offset++] = *val;
+ finfo_length--;
+ val++;
+ }
+
+ /* When FT_STRINGZ is there free space for NUL? */
+ if (finfo_type == FT_STRINGZ && finfo_length) {
+ DataPtr->pd[finfo_offset++] = '\0';
+ finfo_length--;
+ }
+
+ /* XXX, string shorter than previous one. Warn user (red background?), for now fill with NULs */
+ while (finfo_length > 0) {
+ DataPtr->pd[finfo_offset++] = '\0';
+ finfo_length--;
+ }
+ }
+ finfo_window_refresh(DataPtr);
+}
+
+static void
+finfo_boolean_changed(GtkToggleButton *togglebutton, gpointer user_data)
+{
+ struct FieldinfoWinData *DataPtr = (struct FieldinfoWinData *) user_data;
+
+ gboolean val = gtk_toggle_button_get_active(togglebutton);
+
+ finfo_integer_common(DataPtr, val ? G_MAXUINT64 : 0);
+}
+
+static void
+finfo_integer_changed(GtkSpinButton *spinbutton, gpointer user_data)
+{
+ struct FieldinfoWinData *DataPtr = (struct FieldinfoWinData *) user_data;
+
+ const field_info *finfo = DataPtr->finfo;
+ const header_field_info *hfinfo = finfo->hfinfo;
+ int finfo_type = (hfinfo) ? hfinfo->type : FT_NONE;
+
+ gdouble val = gtk_spin_button_get_value(spinbutton);
+ guint64 u_val;
+
+ if (finfo_type == FT_INT8 || finfo_type == FT_INT16 || finfo_type == FT_INT24 || finfo_type == FT_INT32 || finfo_type == FT_INT64)
+ u_val = (guint64) ((gint64) val);
+
+ else if (finfo_type == FT_UINT8 || finfo_type == FT_UINT16 || finfo_type == FT_UINT24 || finfo_type == FT_UINT32 || finfo_type == FT_UINT64)
+ u_val = (guint64) val;
+ else {
+ g_assert_not_reached();
+ return;
+ }
+
+ if (hfinfo->bitmask && hfinfo->bitshift > 0)
+ u_val <<= hfinfo->bitshift;
+
+ finfo_integer_common(DataPtr, u_val);
+}
+
+static void
+finfo_ipv4_changed(GtkSpinButton *spinbutton, gpointer user_data)
+{
+ struct FieldinfoWinData *DataPtr = (struct FieldinfoWinData *) user_data;
+
+ gdouble val = gtk_spin_button_get_value(spinbutton);
+
+ finfo_integer_common(DataPtr, (guint32) val);
+}
+
+static gboolean
+finfo_bv_key_pressed_cb(GtkWidget *bv _U_, GdkEventKey *event, gpointer user_data)
+{
+ struct FieldinfoWinData *DataPtr = (struct FieldinfoWinData *)user_data;
+ const field_info *finfo = DataPtr->finfo;
+ struct CommonWinData data;
+ gboolean have_appendix;
+ gboolean ret;
+
+ /* save */
+ data.frame = DataPtr->frame;
+ data.pd = DataPtr->pd;
+ data.pd_offset = DataPtr->pd_offset;
+ data.pd_bitoffset = DataPtr->pd_bitoffset;
+
+ ret = edit_pkt_common_key_pressed_cb(event, &data);
+
+ /* restore */
+ DataPtr->pd_offset = data.pd_offset;
+ DataPtr->pd_bitoffset = data.pd_bitoffset;
+
+ /* XXX, assuming finfo->appendix_start >= finfo->start, and if appendix exists, main exists also.
+ * easy to fix if needed */
+ have_appendix = (finfo->appendix_start >= 0 && finfo->appendix_length > 0);
+
+ if ((DataPtr->pd_offset >= DataPtr->start_offset + finfo->start && DataPtr->pd_offset < DataPtr->start_offset + finfo->start + finfo->length) ||
+ (have_appendix && DataPtr->pd_offset >= DataPtr->start_offset + finfo->appendix_start && DataPtr->pd_offset < DataPtr->start_offset + finfo->appendix_start + finfo->appendix_length))
+ { /* pd_offset ok */ }
+ else
+ if (have_appendix && DataPtr->pd_offset >= DataPtr->start_offset + finfo->appendix_start + finfo->appendix_length) {
+ DataPtr->pd_offset = DataPtr->start_offset + finfo->start;
+ DataPtr->pd_bitoffset = 0; /* first bit */
+
+ } else if (DataPtr->pd_offset >= DataPtr->start_offset + finfo->start + finfo->length) {
+ if (have_appendix)
+ DataPtr->pd_offset = DataPtr->start_offset + finfo->appendix_start;
+ else
+ DataPtr->pd_offset = DataPtr->start_offset + finfo->start;
+ DataPtr->pd_bitoffset = 0; /* first bit */
+ }
+ else
+ if (DataPtr->pd_offset < DataPtr->start_offset + finfo->start) {
+ if (have_appendix)
+ DataPtr->pd_offset = DataPtr->start_offset + finfo->appendix_start + finfo->appendix_length-1;
+ else
+ DataPtr->pd_offset = DataPtr->start_offset + finfo->start + finfo->length-1;
+ /* XXX, last bit/octect? */
+
+ } else if (have_appendix && DataPtr->pd_offset < DataPtr->start_offset + finfo->appendix_start) {
+ DataPtr->pd_offset = DataPtr->start_offset + finfo->start + finfo->length-1;
+ /* XXX, last bit/octect? */
+ }
+
+ if (ret)
+ finfo_window_refresh(DataPtr);
+ return ret;
+}
+
+static gint
+finfo_ipv4_input(GtkSpinButton *spinbutton, gpointer arg1, gpointer user_data _U_)
+{
+ const gchar *addr_str = gtk_entry_get_text(GTK_ENTRY(spinbutton));
+ gdouble *out_val = (gdouble *) arg1;
+ guint32 addr;
+#if 0
+ /* XXX, get_host_ipaddr() support hostname resolution */
+ if (!get_host_ipaddr(addr_str, &addr))
+ return GTK_INPUT_ERROR;
+ addr = GUINT32_FROM_BE(addr);
+#else
+ unsigned int a0, a1, a2, a3;
+
+ if (sscanf(addr_str, "%u.%u.%u.%u", &a0, &a1, &a2, &a3) != 4)
+ return GTK_INPUT_ERROR;
+
+ if (a0 > 255 || a1 > 255 || a2 > 255 || a3 > 255)
+ return GTK_INPUT_ERROR;
+
+ addr = a0 << 24 | a1 << 16 | a2 << 8 | a3;
+#endif
+ *out_val = (gdouble) addr;
+ return TRUE;
+}
+
+static gboolean
+finfo_ipv4_output(GtkSpinButton *spinbutton, gpointer user_data _U_)
+{
+ GtkAdjustment *adj;
+ guint32 value;
+
+ adj = gtk_spin_button_get_adjustment(spinbutton);
+ value = (guint32) gtk_adjustment_get_value(adj);
+ value = GUINT32_TO_BE(value);
+ /* ip_to_str_buf((guint8*)&value, buf, MAX_IP_STR_LEN); */ /* not exported */
+ gtk_entry_set_text(GTK_ENTRY(spinbutton), ip_to_str((guint8*)&value)); /* XXX, can we ep_alloc() inside gui? */
+ return TRUE;
+}
+
+static gint
+new_finfo_window(GtkWidget *w, struct FieldinfoWinData *DataPtr)
+{
+ field_info *finfo = DataPtr->finfo;
+ const header_field_info *hfinfo = finfo->hfinfo;
+ int finfo_type = (hfinfo) ? hfinfo->type : FT_NONE;
+
+ GtkWidget *dialog = gtk_dialog_new_with_buttons("Editing finfo: ....",
+ GTK_WINDOW(w),
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
+ NULL);
+
+ GtkWidget *dialog_vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+ GtkWidget *fvalue_edit;
+ GtkWidget *native_repr;
+ GtkWidget *bv_nb_ptr;
+ GtkWidget *frame, *frame_vbox;
+
+ gint result;
+
+ if (!FI_GET_FLAG(finfo, FI_LITTLE_ENDIAN) && !FI_GET_FLAG(finfo, FI_BIG_ENDIAN)) {
+ fvalue_edit = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(fvalue_edit), "<not added by proto_tree_add_item()>");
+ gtk_editable_set_editable(GTK_EDITABLE(fvalue_edit), FALSE);
+ gtk_widget_set_sensitive(fvalue_edit, FALSE);
+
+ } /* else if (XXX) {
+ fvalue_edit = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(fvalue_edit), "<ERROR: Value stored in finfo doesn't match value from tvb>");
+ gtk_editable_set_editable(GTK_EDITABLE(fvalue_edit), FALSE);
+ gtk_widget_set_sensitive(fvalue_edit, FALSE);
+
+ } */ else if (finfo_type == FT_INT8 || finfo_type == FT_INT16 || finfo_type == FT_INT24 || finfo_type == FT_INT32 ||
+ finfo_type == FT_UINT8 || finfo_type == FT_UINT16 || finfo_type == FT_UINT24 || finfo_type == FT_UINT32)
+ {
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkAdjustment *adj;
+#else
+ GtkObject *adj;
+#endif
+ int bitcount = 0;
+
+ if (finfo_type == FT_INT8 || finfo_type == FT_UINT8)
+ bitcount = 8;
+ if (finfo_type == FT_INT16 || finfo_type == FT_UINT16)
+ bitcount = 16;
+ if (finfo_type == FT_INT24 || finfo_type == FT_UINT24)
+ bitcount = 24;
+ if (finfo_type == FT_INT32 || finfo_type == FT_UINT32)
+ bitcount = 32;
+ /* if (finfo_type == FT_INT64 || finfo_type == FT_UINT64)
+ bitcount = 64; */
+
+ if (finfo->length * 8 < bitcount)
+ bitcount = finfo->length / 8;
+
+ if (hfinfo->bitmask && hfinfo->bitshift > 0)
+ bitcount -= hfinfo->bitshift;
+
+ /* XXX, hfinfo->bitmask: Can we configure GTK_ADJUSTMENT to do custom step? (value-changed signal?) */
+
+ /* XXX, I'm little worried about these casts from (unsigned) integer to double... */
+
+ if (finfo_type == FT_INT8 || finfo_type == FT_INT16 || finfo_type == FT_INT24 || finfo_type == FT_INT32 /* || finfo_type == FT_INT64 */)
+ adj = gtk_adjustment_new((double) fvalue_get_sinteger(&finfo->value), (double) -(G_GINT64_CONSTANT(1) << (bitcount-1)), (double) ((G_GINT64_CONSTANT(1) << (bitcount-1))-1), 1.0, 10.0, 0);
+ else if (finfo_type == FT_UINT8 || finfo_type == FT_UINT16 || finfo_type == FT_UINT24 || finfo_type == FT_UINT32 /* || finfo_type == FT_UINT64 */ )
+ adj = gtk_adjustment_new((double) fvalue_get_uinteger(&finfo->value), 0.0, (double) ((G_GINT64_CONSTANT(1U) << bitcount)-1), 1.0, 10.0, 0);
+ else {
+ g_assert_not_reached();
+ goto not_supported;
+ }
+
+ fvalue_edit = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 1.0, 0);
+ gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(fvalue_edit), TRUE);
+ g_signal_connect(fvalue_edit, "value-changed", G_CALLBACK(finfo_integer_changed), DataPtr);
+
+ } else if (finfo_type == FT_STRING || finfo_type == FT_STRINGZ) {
+ fvalue_edit = gtk_entry_new();
+ gtk_entry_set_max_length(GTK_ENTRY(fvalue_edit), finfo->length);
+ gtk_entry_set_text(GTK_ENTRY(fvalue_edit), fvalue_get(&finfo->value));
+ g_signal_connect(fvalue_edit, "changed", G_CALLBACK(finfo_string_changed), DataPtr);
+
+ } else if (finfo_type == FT_BOOLEAN) {
+ fvalue_edit = gtk_check_button_new();
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fvalue_edit), (fvalue_get_uinteger(&finfo->value) != 0));
+ g_signal_connect(fvalue_edit, "toggled", G_CALLBACK(finfo_boolean_changed), DataPtr);
+
+ } else if (finfo_type == FT_IPv4) {
+ guint32 net_addr = ipv4_get_net_order_addr(fvalue_get(&finfo->value));
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkAdjustment *adj;
+#else
+ GtkObject *adj;
+#endif
+ adj = gtk_adjustment_new((double) (GUINT32_FROM_BE(net_addr)), 0.0, 4294967295.0 /* (2^32)-1 */, 1.0, 256.0, 0);
+
+ /* XXX, create four gtk_spin_button_new which takes 0..255 */
+ fvalue_edit = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 1.0, 0);
+ gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(fvalue_edit), GTK_UPDATE_IF_VALID);
+ g_signal_connect(fvalue_edit, "value-changed", G_CALLBACK(finfo_ipv4_changed), DataPtr);
+ g_signal_connect(fvalue_edit, "input", G_CALLBACK(finfo_ipv4_input), NULL);
+ g_signal_connect(fvalue_edit, "output", G_CALLBACK(finfo_ipv4_output), NULL);
+
+ } else {
+not_supported:
+ /* List of unsupported FT_*:
+ FT_NONE, FT_PROTOCOL,
+ FT_BYTES, FT_UINT_BYTES,
+ FT_INT64, FT_UINT64, ; should work with FT_INT[8,16,24,32] code
+ FT_FLOAT, FT_DOUBLE,
+ FT_IPXNET, FT_IPv6, FT_ETHER,
+ FT_GUID, FT_OID,
+ FT_UINT_STRING,
+ FT_ABSOLUTE_TIME, FT_RELATIVE_TIME
+ */
+ fvalue_edit = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(fvalue_edit), "<not supported>");
+ gtk_editable_set_editable(GTK_EDITABLE(fvalue_edit), FALSE);
+ gtk_widget_set_sensitive(fvalue_edit, FALSE);
+ }
+ gtk_box_pack_start(GTK_BOX(dialog_vbox), fvalue_edit, FALSE, FALSE, 0);
+ gtk_widget_show(fvalue_edit);
+
+ DataPtr->edit = fvalue_edit;
+
+ native_repr = gtk_entry_new();
+ gtk_editable_set_editable(GTK_EDITABLE(native_repr), FALSE);
+ gtk_widget_set_sensitive(native_repr, FALSE);
+ gtk_box_pack_start(GTK_BOX(dialog_vbox), native_repr, FALSE, FALSE, 0);
+ gtk_widget_show(native_repr);
+
+ DataPtr->repr = native_repr;
+
+ frame = gtk_frame_new("Hex edit");
+ frame_vbox = gtk_vbox_new(TRUE, 1);
+
+ /* raw hex edit */
+ if (finfo->start >= 0 && finfo->length > 0) {
+ GtkWidget *byte_view;
+ /* Byte view */
+ bv_nb_ptr = byte_view_new();
+ gtk_container_add(GTK_CONTAINER(frame_vbox), bv_nb_ptr);
+ gtk_widget_set_size_request(bv_nb_ptr, -1, BV_SIZE);
+ gtk_widget_show(bv_nb_ptr);
+
+ if ((byte_view = get_notebook_bv_ptr(bv_nb_ptr)))
+ g_signal_connect(byte_view, "key-press-event", G_CALLBACK(finfo_bv_key_pressed_cb), DataPtr);
+ DataPtr->bv = bv_nb_ptr;
+ }
+
+ if (finfo->appendix_start >= 0 && finfo->appendix_length > 0) {
+ GtkWidget *byte_view;
+ /* Appendix byte view */
+ bv_nb_ptr = byte_view_new();
+ gtk_container_add(GTK_CONTAINER(frame_vbox), bv_nb_ptr);
+ gtk_widget_set_size_request(bv_nb_ptr, -1, BV_SIZE);
+ gtk_widget_show(bv_nb_ptr);
+
+ if ((byte_view = get_notebook_bv_ptr(bv_nb_ptr)))
+ g_signal_connect(byte_view, "key-press-event", G_CALLBACK(finfo_bv_key_pressed_cb), DataPtr);
+ DataPtr->app_bv = bv_nb_ptr;
+ }
+ gtk_container_add(GTK_CONTAINER(frame), frame_vbox);
+ gtk_widget_show(frame_vbox); gtk_widget_show(frame);
+ gtk_container_add(GTK_CONTAINER(dialog_vbox), frame);
+
+ gtk_window_set_default_size(GTK_WINDOW(dialog), DEF_WIDTH, -1);
+ finfo_window_refresh(DataPtr);
+ result = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ return result;
+}
+
+static void
+edit_pkt_tree_row_activated_cb(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column _U_, gpointer user_data)
+{
+ struct PacketWinData *DataPtr = (struct PacketWinData*)user_data;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ field_info *finfo;
+
+ model = gtk_tree_view_get_model(tree_view);
+ if (!gtk_tree_model_get_iter(model, &iter, path))
+ return;
+
+ gtk_tree_model_get(model, &iter, 1, &finfo, -1);
+ if (!finfo)
+ return;
+
+ if (!FI_GET_FLAG(finfo, FI_GENERATED) &&
+ finfo->ds_tvb && finfo->ds_tvb->real_data >= DataPtr->pd && finfo->ds_tvb->real_data <= DataPtr->pd + DataPtr->frame->cap_len)
+ {
+ struct FieldinfoWinData data;
+
+ data.frame = DataPtr->frame;
+ data.pseudo_header = DataPtr->pseudo_header;
+ data.pd = g_memdup(DataPtr->pd, DataPtr->frame->cap_len);
+ data.start_offset = (int) (finfo->ds_tvb->real_data - DataPtr->pd);
+
+ data.finfo = finfo;
+ data.app_bv = data.bv = NULL;
+ data.repr = data.edit = NULL;
+
+ data.pd_offset = data.start_offset + finfo->start;
+ data.pd_bitoffset = 0;
+
+ if (new_finfo_window(DataPtr->main, &data) == GTK_RESPONSE_ACCEPT) {
+ /* DataPtr->pseudo_header = data.pseudo_header; */
+ memcpy(DataPtr->pd, data.pd, DataPtr->frame->cap_len);
+
+ epan_dissect_cleanup(&(DataPtr->edt));
+ epan_dissect_init(&(DataPtr->edt), TRUE, TRUE);
+ epan_dissect_run(&(DataPtr->edt), &DataPtr->pseudo_header, DataPtr->pd, DataPtr->frame, NULL);
+ add_byte_views(&(DataPtr->edt), DataPtr->tree_view, DataPtr->bv_nb_ptr);
+ proto_tree_draw(DataPtr->edt.tree, DataPtr->tree_view);
+ }
+ g_free(data.pd);
+
+ } else {
+ /* XXX, simple_dialog() is shown on top of main_window, instead of edit_window. */
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK, "Item can't be edited. FI_GENERATED or tvb not subset of packet data (uncompressed?)");
+ }
+}
+
+static gboolean
+edit_pkt_common_key_pressed_cb(GdkEventKey *event, struct CommonWinData *DataPtr)
+{
+ int val = -1;
+
+ switch (recent.gui_bytes_view) {
+ case BYTES_HEX:
+ if (event->keyval >= 'a' && event->keyval <= 'f')
+ val = (event->keyval - 'a') + 10;
+ else if (event->keyval >= 'A' && event->keyval <= 'F')
+ val = (event->keyval - 'A') + 10;
+ else if (event->keyval >= '0' && event->keyval <= '9')
+ val = (event->keyval - '0');
+ else if (event->keyval == GDK_Left)
+ DataPtr->pd_bitoffset -= 4;
+ else if (event->keyval == GDK_Right)
+ DataPtr->pd_bitoffset += 4;
+ else
+ return FALSE;
+
+ if (val != -1) {
+ /* Lazy...
+ * XXX Allow (DataPtr->pd_bitoffset % 4) != 0 ? */
+ if (DataPtr->pd_bitoffset < 4) {
+ DataPtr->pd[DataPtr->pd_offset] = (DataPtr->pd[DataPtr->pd_offset] & 0x0f) | (val << 4);
+ DataPtr->pd_bitoffset = 4;
+ } else {
+ DataPtr->pd[DataPtr->pd_offset] = (DataPtr->pd[DataPtr->pd_offset] & 0xf0) | val;
+ DataPtr->pd_bitoffset = 8;
+ }
+ /* DataPtr->pd_bitoffset += 4; */
+ }
+ break;
+
+ case BYTES_BITS:
+ if (event->keyval == '0' || event->keyval == '1')
+ val = (event->keyval != '0');
+ else if (event->keyval == GDK_Left)
+ DataPtr->pd_bitoffset -= 1;
+ else if (event->keyval == GDK_Right)
+ DataPtr->pd_bitoffset += 1;
+ else
+ return FALSE;
+
+ if (val != -1) {
+ if (val)
+ DataPtr->pd[DataPtr->pd_offset] |= (1 << (7-DataPtr->pd_bitoffset));
+ else
+ DataPtr->pd[DataPtr->pd_offset] &= ~(1 << (7-DataPtr->pd_bitoffset));
+ DataPtr->pd_bitoffset += 1;
+ }
+ break;
+
+ default:
+ g_assert_not_reached();
+ return FALSE;
+ }
+
+ while (DataPtr->pd_bitoffset >= 8) {
+ DataPtr->pd_offset += 1;
+ DataPtr->pd_bitoffset -= 8;
+ }
+ while (DataPtr->pd_bitoffset < 0) {
+ DataPtr->pd_offset -= 1;
+ DataPtr->pd_bitoffset += 8;
+ }
+
+ DataPtr->val = val;
+ return TRUE;
+}
+
+static gboolean
+edit_pkt_win_key_pressed_cb(GtkWidget *win _U_, GdkEventKey *event, gpointer user_data)
+{
+ struct PacketWinData *DataPtr = (struct PacketWinData *)user_data;
+ struct CommonWinData data;
+ GSList *src_le;
+ gboolean ret;
+ tvbuff_t *ds_tvb = NULL;
+
+ /* save */
+ data.frame = DataPtr->frame;
+ data.pd = DataPtr->pd;
+ data.pd_offset = DataPtr->pd_offset;
+ data.pd_bitoffset = DataPtr->pd_bitoffset;
+
+ ret = edit_pkt_common_key_pressed_cb(event, &data);
+
+ /* restore */
+ DataPtr->pd_offset = data.pd_offset;
+ DataPtr->pd_bitoffset = data.pd_bitoffset;
+
+ if (DataPtr->pd_offset < 0) {
+ DataPtr->pd_offset = DataPtr->frame->cap_len-1;
+ /* XXX, last bit/octect? */
+ }
+
+ if ((guint)DataPtr->pd_offset >= DataPtr->frame->cap_len) {
+ DataPtr->pd_offset = 0;
+ DataPtr->pd_bitoffset = 0; /* first bit */
+ }
+
+ if (!ret)
+ return FALSE;
+
+ /* redissect if changed */
+ if (data.val != -1) {
+ /* XXX, can be optimized? */
+ epan_dissect_cleanup(&(DataPtr->edt));
+ epan_dissect_init(&(DataPtr->edt), TRUE, TRUE);
+ epan_dissect_run(&(DataPtr->edt), &DataPtr->pseudo_header, DataPtr->pd, DataPtr->frame, NULL);
+ add_byte_views(&(DataPtr->edt), DataPtr->tree_view, DataPtr->bv_nb_ptr);
+ proto_tree_draw(DataPtr->edt.tree, DataPtr->tree_view);
+ }
+
+ for (src_le = DataPtr->edt.pi.data_src; src_le != NULL; src_le = src_le->next) {
+ const data_source *src = src_le->data;
+ tvbuff_t *tvb = src->tvb;
+
+ if (tvb && tvb->real_data == DataPtr->pd) {
+ ds_tvb = tvb;
+ break;
+ }
+ }
+
+ if (ds_tvb != NULL) {
+ GtkWidget *byte_view;
+
+ set_notebook_page(DataPtr->bv_nb_ptr, ds_tvb);
+ byte_view = get_notebook_bv_ptr(DataPtr->bv_nb_ptr);
+ if (byte_view)
+ packet_hex_editor_print(byte_view, DataPtr->pd, DataPtr->frame, DataPtr->pd_offset, DataPtr->pd_bitoffset, DataPtr->frame->cap_len);
+ }
+ return TRUE;
+}
+
+static void
+edit_pkt_destroy_new_window(GObject *object _U_, gpointer user_data)
+{
+ /* like destroy_new_window, but without freeding DataPtr->pd */
+ struct PacketWinData *DataPtr = user_data;
+
+ detail_windows = g_list_remove(detail_windows, DataPtr);
+ epan_dissect_cleanup(&(DataPtr->edt));
+ g_free(DataPtr);
+
+ /* XXX, notify main packet list that packet should be redisplayed */
+}
+
+static gint g_direct_compare_func(gconstpointer a, gconstpointer b, gpointer user_data _U_) {
+ if (a > b)
+ return 1;
+ else if (a < b)
+ return -1;
+ else
+ return 0;
+}
+
+static void modifed_frame_data_free(gpointer data) {
+ modified_frame_data *mfd = (modified_frame_data *) data;
+
+ g_free(mfd->pd);
+ g_free(mfd);
+}
+
+#endif /* WANT_PACKET_EDITOR */
+
+void new_packet_window(GtkWidget *w _U_, gboolean editable _U_)
+{
+#define NewWinTitleLen 1000
+ char Title[NewWinTitleLen] = "";
+ const char *TextPtr;
+ GtkWidget *main_w, *main_vbox, *pane,
+ *tree_view, *tv_scrollw,
+ *bv_nb_ptr;
+ struct PacketWinData *DataPtr;
+ int i;
+
+ if (!cfile.current_frame) {
+ /* nothing has been captured so far */
+ return;
+ }
+
+ /* With the new packetlists "lazy columns" it's neccesary to reread the frame */
+ if (!cf_read_frame(&cfile, cfile.current_frame)) {
+ /* error reading the frame */
+ return;
+ }
+
+ /* Allocate data structure to represent this window. */
+ DataPtr = (struct PacketWinData *) g_malloc(sizeof(struct PacketWinData));
+
+ DataPtr->frame = cfile.current_frame;
+ memcpy(&DataPtr->pseudo_header, &cfile.pseudo_header, sizeof DataPtr->pseudo_header);
+ DataPtr->pd = g_malloc(DataPtr->frame->cap_len);
+ memcpy(DataPtr->pd, cfile.pd, DataPtr->frame->cap_len);
+
+ epan_dissect_init(&(DataPtr->edt), TRUE, TRUE);
+ epan_dissect_run(&(DataPtr->edt), &DataPtr->pseudo_header, DataPtr->pd,
+ DataPtr->frame, &cfile.cinfo);
+ epan_dissect_fill_in_columns(&(DataPtr->edt), FALSE, TRUE);
+
+ /*
+ * Build title of window by getting column data constructed when the
+ * frame was dissected.
+ */
+ for (i = 0; i < cfile.cinfo.num_cols; ++i) {
+ TextPtr = cfile.cinfo.col_data[i];
+ if ((strlen(Title) + strlen(TextPtr)) < NewWinTitleLen - 1) {
+ g_strlcat(Title, TextPtr, NewWinTitleLen);
+ g_strlcat(Title, " ", NewWinTitleLen);
+ }
+ }
+
+ main_w = window_new(GTK_WINDOW_TOPLEVEL, Title);
+ gtk_window_set_default_size(GTK_WINDOW(main_w), DEF_WIDTH, -1);
+
+ /* Container for paned windows */
+ main_vbox = gtk_vbox_new(FALSE, 1);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vbox), 1);
+ gtk_container_add(GTK_CONTAINER(main_w), main_vbox);
+ gtk_widget_show(main_vbox);
+
+ /* Panes for the tree and byte view */
+ pane = gtk_vpaned_new();
+ gtk_container_add(GTK_CONTAINER(main_vbox), pane);
+ gtk_widget_show(pane);
+
+ /* Tree view */
+ tv_scrollw = main_tree_view_new(&prefs, &tree_view);
+ gtk_paned_pack1(GTK_PANED(pane), tv_scrollw, TRUE, TRUE);
+ gtk_widget_set_size_request(tv_scrollw, -1, TV_SIZE);
+ gtk_widget_show(tv_scrollw);
+ gtk_widget_show(tree_view);
+
+ /* Byte view */
+ bv_nb_ptr = byte_view_new();
+ gtk_paned_pack2(GTK_PANED(pane), bv_nb_ptr, FALSE, FALSE);
+ gtk_widget_set_size_request(bv_nb_ptr, -1, BV_SIZE);
+ gtk_widget_show(bv_nb_ptr);
+
+ DataPtr->main = main_w;
+ DataPtr->tv_scrollw = tv_scrollw;
+ DataPtr->tree_view = tree_view;
+ DataPtr->bv_nb_ptr = bv_nb_ptr;
+ detail_windows = g_list_append(detail_windows, DataPtr);
+
+ /* load callback handlers */
+ g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)),
+ "changed", G_CALLBACK(new_tree_view_selection_changed_cb), DataPtr);
+ g_signal_connect(tree_view, "button_press_event", G_CALLBACK(button_press_handler), NULL);
+#ifdef WANT_PACKET_EDITOR
+ if (editable && DataPtr->frame->cap_len != 0) {
+ g_signal_connect(main_w, "key-press-event", G_CALLBACK(edit_pkt_win_key_pressed_cb), DataPtr);
+ /* XXX, popup-menu instead of row-activated? */
+ g_signal_connect(tree_view, "row-activated", G_CALLBACK(edit_pkt_tree_row_activated_cb), DataPtr);
+ g_signal_connect(main_w, "destroy", G_CALLBACK(edit_pkt_destroy_new_window), DataPtr);
+ } else
+#endif
+ g_signal_connect(main_w, "destroy", G_CALLBACK(destroy_new_window), DataPtr);
+
+ /* draw the protocol tree & print hex data */
+ add_byte_views(&(DataPtr->edt), tree_view, DataPtr->bv_nb_ptr);
+ proto_tree_draw(DataPtr->edt.tree, tree_view);
+
+ DataPtr->finfo_selected = NULL;
+ DataPtr->pd_offset = 0;
+ DataPtr->pd_bitoffset = 0;
+ gtk_widget_show(main_w);
+
+#ifdef WANT_PACKET_EDITOR
+ if (editable && DataPtr->frame->cap_len != 0) {
+ /* XXX, there's no Save button here, so lets assume packet is always edited */
+ modified_frame_data *mfd = g_malloc(sizeof(modified_frame_data));
+
+ mfd->pd = DataPtr->pd;
+ mfd->ph = DataPtr->pseudo_header;
+
+ if (cfile.edited_frames == NULL)
+ cfile.edited_frames = g_tree_new_full(g_direct_compare_func, NULL, NULL, modifed_frame_data_free);
+ g_tree_insert(cfile.edited_frames, GINT_TO_POINTER(DataPtr->frame->num), mfd);
+ DataPtr->frame->file_off = -1;
+ }
+#endif
+}
+
+static void
+destroy_new_window(GObject *object _U_, gpointer user_data)
+{
+ struct PacketWinData *DataPtr = user_data;
+
+ detail_windows = g_list_remove(detail_windows, DataPtr);
+ epan_dissect_cleanup(&(DataPtr->edt));
+ g_free(DataPtr->pd);
+ g_free(DataPtr);
+}
+
+/* called when a tree row is (un)selected in the popup packet window */
+static void
+new_tree_view_selection_changed_cb(GtkTreeSelection *sel, gpointer user_data)
+{
+ field_info *finfo;
+ GtkWidget *byte_view;
+ const guint8 *data;
+ guint len;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ struct PacketWinData *DataPtr = (struct PacketWinData*)user_data;
+
+ /* if something is selected */
+ if (gtk_tree_selection_get_selected(sel, &model, &iter))
+ {
+ gtk_tree_model_get(model, &iter, 1, &finfo, -1);
+ if (!finfo) return;
+
+ set_notebook_page(DataPtr->bv_nb_ptr, finfo->ds_tvb);
+ byte_view = get_notebook_bv_ptr(DataPtr->bv_nb_ptr);
+ if (!byte_view) /* exit if no hex window to write in */
+ return;
+
+ data = get_byte_view_data_and_length(byte_view, &len);
+ if (data == NULL) {
+ data = DataPtr->pd;
+ len = DataPtr->frame->cap_len;
+ }
+
+ DataPtr->finfo_selected = finfo;
+
+#ifdef WANT_PACKET_EDITOR
+ DataPtr->pd_offset = 0;
+ DataPtr->pd_bitoffset = 0;
+
+ if (!FI_GET_FLAG(finfo, FI_GENERATED) &&
+ finfo->ds_tvb && finfo->ds_tvb->real_data >= DataPtr->pd && finfo->ds_tvb->real_data <= DataPtr->pd + DataPtr->frame->cap_len)
+ {
+ /* I haven't really test if TVB subsets works, but why not? :> */
+ int pd_offset = (int) (finfo->ds_tvb->real_data - DataPtr->pd);
+
+ /* some code from packet_hex_print */
+ int finfo_offset = finfo->start;
+ int finfo_len = finfo->length;
+
+ if (!(finfo_offset >= 0 && finfo_len > 0)) {
+ finfo_offset = finfo->appendix_start;
+ finfo_len = finfo->appendix_length;
+ }
+
+ /* Don't care about things like bitmask or LE/BE, just point DataPtr->tvb_[bit]offset to proper offsets. */
+ if (finfo_offset >= 0 && finfo_len > 0) {
+ DataPtr->pd_offset = pd_offset + finfo_offset;
+ DataPtr->pd_bitoffset = 0; /* XXX */
+ }
+
+ if (DataPtr->pd_offset < 0)
+ DataPtr->pd_offset = 0;
+ if ((guint)DataPtr->pd_offset >= DataPtr->frame->cap_len)
+ DataPtr->pd_offset = 0;
+ }
+#endif
+
+ packet_hex_print(byte_view, data, DataPtr->frame, finfo, len);
+ }
+ else
+ {
+ DataPtr->finfo_selected = NULL;
+
+ byte_view = get_notebook_bv_ptr(DataPtr->bv_nb_ptr);
+ if (!byte_view) /* exit if no hex window to write in */
+ return;
+
+ data = get_byte_view_data_and_length(byte_view, &len);
+ g_assert(data != NULL);
+ packet_hex_reprint(byte_view);
+ }
+}
+
+/* Functions called from elsewhere to act on all popup packet windows. */
+
+/* Destroy all popup packet windows. */
+void
+destroy_packet_wins(void)
+{
+ struct PacketWinData *DataPtr;
+
+ /* Destroying a packet window causes it to be removed from
+ the list of packet windows, so we can't do a "g_list_foreach()"
+ to go through the list of all packet windows and destroy them
+ as we find them; instead, as long as the list is non-empty,
+ we destroy the first window on the list. */
+ while (detail_windows != NULL) {
+ DataPtr = (struct PacketWinData *)(detail_windows->data);
+ window_destroy(DataPtr->main);
+ }
+}
+
+static void
+redraw_packet_bytes_cb(gpointer data, gpointer user_data _U_)
+{
+ struct PacketWinData *DataPtr = (struct PacketWinData *)data;
+
+ redraw_packet_bytes(DataPtr->bv_nb_ptr, DataPtr->frame, DataPtr->finfo_selected);
+}
+
+/* Redraw the packet bytes part of all the popup packet windows. */
+void
+redraw_packet_bytes_packet_wins(void)
+{
+ g_list_foreach(detail_windows, redraw_packet_bytes_cb, NULL);
+}
diff --git a/ui/gtk/packet_win.h b/ui/gtk/packet_win.h
new file mode 100644
index 0000000000..1f4f0f5a88
--- /dev/null
+++ b/ui/gtk/packet_win.h
@@ -0,0 +1,47 @@
+/* packet_win.h
+ * Declarations for popping a window to display current packet
+ *
+ * Copyright 2000, Jeffrey C. Foster <jfoste@woodward.com>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PACKET_WIN_H__
+#define __PACKET_WIN_H__
+
+/** @file
+ * Pop up a window to display the current packet only.
+ */
+
+/** Create a new packet window.
+ *
+ * @param widget parent widget (unused)
+ */
+extern void new_packet_window(GtkWidget *widget, gboolean editable);
+
+/** Destroy all popup packet windows.
+ */
+void destroy_packet_wins(void);
+
+/** Redraw the packet bytes panes of all packet windows. */
+void redraw_packet_bytes_packet_wins(void);
+
+#endif
diff --git a/ui/gtk/pixmap_save.c b/ui/gtk/pixmap_save.c
new file mode 100644
index 0000000000..49d17df033
--- /dev/null
+++ b/ui/gtk/pixmap_save.c
@@ -0,0 +1,214 @@
+/* pixmap_save.c
+ * Routines for saving pixmaps using the Gdk-Pixmap library
+ * Copyright 2007, Stephen Fisher (see AUTHORS file)
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <errno.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/filesystem.h>
+
+#include "../simple_dialog.h"
+
+#include "ui/gtk/pixmap_save.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/file_dlg.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+#if GTK_CHECK_VERSION(2,22,0)
+#if !GTK_CHECK_VERSION(3,0,0)
+#include "ui/gtk/gui_utils.h"
+#endif
+#endif
+static GtkWidget *save_as_w;
+
+static void
+pixbuf_save_destroy_cb(GtkWidget *window _U_, gpointer data _U_)
+{
+ /* We no longer have a save as dialog */
+ save_as_w = NULL;
+}
+
+static gboolean
+pixbuf_save_button_cb(GtkWidget *save_as_w_lcl, GdkPixbuf *pixbuf)
+{
+ gchar *filename, *file_type;
+ GtkWidget *type_cm, *simple_w;
+ GError *error = NULL;
+ gboolean ret;
+
+ filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(save_as_w_lcl));
+ type_cm = g_object_get_data(G_OBJECT(save_as_w_lcl), "type_cm");
+ file_type = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(type_cm));
+
+ /* Perhaps the user specified a directory instead of a file.
+ Check whether they did. */
+ if(test_for_directory(filename) == EISDIR) {
+ /* It's a directory - set the file selection box to display that
+ directory, and leave the selection box displayed. */
+ set_last_open_dir(filename);
+ g_free(filename);
+ g_free(file_type);
+ file_selection_set_current_folder(save_as_w_lcl,
+ get_last_open_dir());
+ gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_as_w_lcl), "");
+ return FALSE;
+ }
+
+ ret = gdk_pixbuf_save(pixbuf, filename, file_type, &error, NULL);
+ g_free(filename);
+ g_free(file_type);
+
+ if(!ret) {
+ simple_w = simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s%s%s",
+ simple_dialog_primary_start(),
+ error->message,
+ simple_dialog_primary_end());
+ gtk_window_set_transient_for(GTK_WINDOW(simple_w),
+ GTK_WINDOW(save_as_w_lcl));
+ }
+ return TRUE;
+}
+
+void
+pixmap_save_cb(GtkWidget *w, gpointer pixmap_ptr _U_)
+{
+#if GTK_CHECK_VERSION(2,22,0)
+ surface_info_t *surface_info = g_object_get_data(G_OBJECT(w), "surface-info");
+#else
+ GdkPixmap *pixmap = g_object_get_data(G_OBJECT(w), "pixmap");
+#endif
+ GdkPixbuf *pixbuf;
+ GdkPixbufFormat *pixbuf_format;
+ GtkWidget *main_vb, *save_as_type_hb, *type_lb, *type_cm;
+ GSList *file_formats,*ffp;
+ GdkWindow *parent;
+
+ gchar *format_name;
+ guint format_index = 0;
+ guint default_index = 0;
+
+#if GTK_CHECK_VERSION(2,22,0)
+ pixbuf = gdk_pixbuf_get_from_surface (surface_info->surface,
+ 0, 0, surface_info->width, surface_info->height);
+#else
+ pixbuf = gdk_pixbuf_get_from_drawable(NULL, pixmap, NULL,
+ 0, 0, 0, 0, -1, -1);
+#endif
+ if(!pixbuf) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%sCould not get image from graph%s",
+ simple_dialog_primary_start(),
+ simple_dialog_primary_end());
+ return;
+ }
+
+#if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
+ if(save_as_w != NULL) {
+ /* If this save as window is already open, re-open it */
+ reactivate_window(save_as_w);
+ return;
+ }
+#endif
+
+ save_as_w = file_selection_new("Wireshark: Save Graph As ...",
+ FILE_SELECTION_SAVE);
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(save_as_w), TRUE);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 0);
+ file_selection_set_extra_widget(save_as_w, main_vb);
+ gtk_widget_show(main_vb);
+
+ save_as_type_hb = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(main_vb), save_as_type_hb, FALSE, FALSE, 0);
+ gtk_widget_show(save_as_type_hb);
+
+ type_lb = gtk_label_new("File type: ");
+ gtk_box_pack_start(GTK_BOX(save_as_type_hb), type_lb, FALSE, FALSE, 0);
+ gtk_widget_show(type_lb);
+
+ type_cm = gtk_combo_box_text_new();
+ gtk_box_pack_start(GTK_BOX(save_as_type_hb), type_cm, FALSE, FALSE, 0);
+
+ /* List all of the file formats the gdk-pixbuf library supports */
+ file_formats = gdk_pixbuf_get_formats();
+ ffp = file_formats;
+ while(ffp) {
+ pixbuf_format = ffp->data;
+ if (gdk_pixbuf_format_is_writable(pixbuf_format)) {
+ format_name = gdk_pixbuf_format_get_name(pixbuf_format);
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(type_cm),
+ format_name);
+ if (!(g_ascii_strcasecmp(format_name, "png")))
+ default_index = format_index;
+ format_index++;
+ }
+ ffp = g_slist_next(ffp);
+ }
+ g_slist_free(file_formats);
+
+ gtk_combo_box_set_active(GTK_COMBO_BOX(type_cm), default_index);
+ g_object_set_data(G_OBJECT(save_as_w), "type_cm", type_cm);
+ gtk_widget_show(type_cm);
+
+ g_signal_connect(save_as_w, "destroy", G_CALLBACK(pixbuf_save_destroy_cb), NULL);
+
+ gtk_widget_show(save_as_w);
+ window_present(save_as_w);
+ parent = gtk_widget_get_parent_window(w);
+ gdk_window_set_transient_for(gtk_widget_get_window(save_as_w), parent);
+
+#if 0
+ if(gtk_dialog_run(GTK_DIALOG(save_as_w)) == GTK_RESPONSE_ACCEPT)
+ pixbuf_save_button_cb(save_as_w, pixbuf);
+
+ window_destroy(save_as_w);
+#else
+ /* "Run" the GtkFileChooserDialog. */
+ /* Upon exit: If "Accept" run the OK callback. */
+ /* If the OK callback returns with a FALSE status, re-run the dialog.*/
+ /* If not accept (ie: cancel) destroy the window. */
+ /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
+ /* return with a TRUE status so that the dialog window will be destroyed. */
+ /* Trying to re-run the dialog after popping up an alert box will not work */
+ /* since the user will not be able to dismiss the alert box. */
+ /* The (somewhat unfriendly) effect: the user must re-invoke the */
+ /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
+ /* */
+ /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
+ /* GtkFileChooserDialog. */
+ while (gtk_dialog_run(GTK_DIALOG(save_as_w)) == GTK_RESPONSE_ACCEPT) {
+ if (pixbuf_save_button_cb(save_as_w, pixbuf)) {
+ break; /* we're done */
+ }
+ }
+ window_destroy(save_as_w);
+#endif
+}
diff --git a/ui/gtk/pixmap_save.h b/ui/gtk/pixmap_save.h
new file mode 100644
index 0000000000..ec92f4f984
--- /dev/null
+++ b/ui/gtk/pixmap_save.h
@@ -0,0 +1,38 @@
+/* pixmap_save.h
+ * Routines for saving pixmaps using the Gdk-Pixmap library
+ * Copyright 2007, Stephen Fisher (see AUTHORS file)
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PIXMAP_SAVE_H__
+#define __PIXMAP_SAVE_H__
+
+/* Callback to be tied to a save button. This function will pop-up a dialog
+ * asking for options to save the graph with (such as file type). */
+void pixmap_save_cb(GtkWidget *w, gpointer pixmap_ptr);
+
+typedef struct _surface_info_t {
+ cairo_surface_t *surface;
+ gint width;
+ gint height;
+} surface_info_t;
+#endif /* __PIXMAP_SAVE_H__ */
diff --git a/ui/gtk/plugins_dlg.c b/ui/gtk/plugins_dlg.c
new file mode 100644
index 0000000000..543cc5f5b9
--- /dev/null
+++ b/ui/gtk/plugins_dlg.c
@@ -0,0 +1,122 @@
+/* plugins_dlg.c
+ * Dialog boxes for plugins
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include "epan/plugins.h"
+
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/plugins_dlg.h"
+
+
+#if defined(HAVE_PLUGINS) || defined(HAVE_LUA_5_1)
+
+/*
+ * Fill the list widget with a list of the plugin modules.
+ * XXX - We might want to combine this with plugins_dump_all().
+ */
+static void
+plugins_scan(GtkWidget *list)
+{
+#ifdef HAVE_PLUGINS
+ plugin *pt_plug;
+ const char *sep;
+#endif
+#ifdef HAVE_LUA_5_1
+ wslua_plugin *lua_plug;
+#endif
+ GString *type;
+
+#ifdef HAVE_PLUGINS
+ for (pt_plug = plugin_list; pt_plug != NULL; pt_plug = pt_plug->next)
+ {
+ type = g_string_new("");
+ sep = "";
+ if (pt_plug->register_protoinfo)
+ {
+ type = g_string_append(type, "dissector");
+ sep = ", ";
+ }
+ if (pt_plug->register_tap_listener)
+ {
+ type = g_string_append(type, sep);
+ type = g_string_append(type, "tap");
+ sep = ", ";
+ }
+ if (pt_plug->register_wtap_module)
+ {
+ type = g_string_append(type, sep);
+ type = g_string_append(type, "file format");
+ sep = ", ";
+ }
+ if (pt_plug->register_codec_module)
+ {
+ type = g_string_append(type, sep);
+ type = g_string_append(type, "codec");
+ }
+ simple_list_append(list, 0, pt_plug->name, 1, pt_plug->version,
+ 2, type->str, 3, g_module_name(pt_plug->handle), -1);
+ g_string_free(type, TRUE);
+ }
+#endif
+
+#ifdef HAVE_LUA_5_1
+ for (lua_plug = wslua_plugin_list; lua_plug != NULL; lua_plug = lua_plug->next)
+ {
+ type = g_string_new("");
+ type = g_string_append(type, "lua script");
+
+ simple_list_append(list, 0, lua_plug->name, 1, lua_plug->version, 2, type->str, 3, lua_plug->filename, -1);
+ g_string_free(type, TRUE);
+ }
+#endif
+}
+
+
+GtkWidget *
+about_plugins_page_new(void)
+{
+ GtkWidget *scrolledwindow;
+ GtkWidget *plugins_list;
+ const gchar *titles[] = {"Name", "Version", "Type", "Path"};
+
+
+ scrolledwindow = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwindow),
+ GTK_SHADOW_IN);
+
+ plugins_list = simple_list_new(4, titles);
+ plugins_scan(plugins_list);
+
+ gtk_container_add(GTK_CONTAINER(scrolledwindow), plugins_list);
+
+ return scrolledwindow;
+}
+
+#endif /* HAVE_PLUGINS || HAVE_LUA_5_1 */
diff --git a/ui/gtk/plugins_dlg.h b/ui/gtk/plugins_dlg.h
new file mode 100644
index 0000000000..652393e0ff
--- /dev/null
+++ b/ui/gtk/plugins_dlg.h
@@ -0,0 +1,38 @@
+/* plugins_dlg.h
+ * Plugins definitions
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PLUGINS_DLG_H__
+#define __PLUGINS_DLG_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+GtkWidget * about_plugins_page_new(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __PLUGINS_DLG_H__ */
diff --git a/ui/gtk/prefs_capture.c b/ui/gtk/prefs_capture.c
new file mode 100644
index 0000000000..a5a5bbd44d
--- /dev/null
+++ b/ui/gtk/prefs_capture.c
@@ -0,0 +1,1430 @@
+/* capture_prefs.c
+ * Dialog box for capture preferences
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_LIBPCAP
+
+#include <string.h>
+#include <gtk/gtk.h>
+
+#include <epan/prefs.h>
+
+#include "../simple_dialog.h"
+#include "../capture_ifinfo.h"
+#include "../capture_ui_utils.h"
+
+#include "ui/gtk/prefs_capture.h"
+#include "ui/gtk/prefs_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/main_welcome.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/old-gtk-compat.h"
+#include <epan/strutil.h>
+
+
+#define DEVICE_KEY "device"
+#define PROM_MODE_KEY "prom_mode"
+#define PCAP_NG_KEY "pcap_ng"
+#define CAPTURE_REAL_TIME_KEY "capture_real_time"
+#define AUTO_SCROLL_KEY "auto_scroll"
+#define SHOW_INFO_KEY "show_info"
+
+#define CAPTURE_TABLE_ROWS 6
+
+#define IFOPTS_CALLER_PTR_KEY "ifopts_caller_ptr"
+#define IFOPTS_DIALOG_PTR_KEY "ifopts_dialog_ptr"
+#define IFOPTS_TABLE_ROWS 2
+#define IFOPTS_LIST_TEXT_COLS 4
+#define IFOPTS_MAX_DESCR_LEN 128
+#define IFOPTS_IF_NOSEL -1
+
+/* interface options dialog */
+static GtkWidget *cur_list, *if_dev_lb, *if_name_lb, *if_linktype_lb, *if_linktype_cb, *if_descr_te, *if_hide_cb;
+#ifdef HAVE_PCAP_CREATE
+static GtkWidget *if_monitor_lb, *if_monitor_cb;
+#endif
+static GtkTreeSelection *if_selection; /* current interface row selected */
+static int num_linktypes;
+static gboolean interfaces_info_nochange; /* TRUE to ignore Interface Options Properties */
+ /* widgets "changed" callbacks. */
+
+static void ifopts_edit_cb(GtkWidget *w, gpointer data);
+static void ifopts_edit_ok_cb(GtkWidget *w, gpointer parent_w);
+static void ifopts_edit_destroy_cb(GtkWidget *win, gpointer data);
+static void ifopts_edit_ifsel_cb(GtkTreeSelection *selection, gpointer data);
+#ifdef HAVE_PCAP_CREATE
+static void ifopts_edit_monitor_changed_cb(GtkToggleButton *tbt, gpointer udata);
+#endif
+static void ifopts_edit_linktype_changed_cb(GtkComboBox *ed, gpointer udata);
+static void ifopts_edit_descr_changed_cb(GtkEditable *ed, gpointer udata);
+static void ifopts_edit_hide_changed_cb(GtkToggleButton *tbt, gpointer udata);
+static void ifopts_options_add(GtkListStore *list_store, if_info_t *if_info);
+static void ifopts_options_free(gchar *text[]);
+static void ifopts_if_liststore_add(void);
+#ifdef HAVE_PCAP_CREATE
+static void ifopts_write_new_monitor_mode(void);
+#endif
+static void ifopts_write_new_linklayer(void);
+static void ifopts_write_new_descr(void);
+static void ifopts_write_new_hide(void);
+
+GtkWidget*
+capture_prefs_show(void)
+{
+ GtkWidget *main_tb, *main_vb;
+ GtkWidget *if_cbxe, *if_lb, *promisc_cb, *pcap_ng_cb, *sync_cb, *auto_scroll_cb, *show_info_cb;
+ GtkWidget *ifopts_lb, *ifopts_bt;
+ GList *if_list, *combo_list;
+ int err;
+ int row = 0;
+ gchar *tooltips_text;
+
+ /* Main vertical box */
+ main_vb = gtk_vbox_new(FALSE, 7);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+
+ /* Main table */
+ main_tb = gtk_table_new(CAPTURE_TABLE_ROWS, 2, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
+ gtk_widget_show(main_tb);
+
+ /* Default device */
+ if_lb = gtk_label_new("Default interface:");
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), if_lb, 0, 1, row, row+1);
+ gtk_misc_set_alignment(GTK_MISC(if_lb), 1.0f, 0.5f);
+ gtk_widget_show(if_lb);
+
+ if_cbxe = gtk_combo_box_text_new_with_entry();
+ /*
+ * XXX - what if we can't get the list?
+ */
+ if_list = capture_interface_list(&err, NULL);
+ combo_list = build_capture_combo_list(if_list, FALSE);
+ free_interface_list(if_list);
+ if (combo_list != NULL) {
+ GList *combo_entry;
+ for (combo_entry = combo_list; combo_entry != NULL; combo_entry = g_list_next(combo_entry)) {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(if_cbxe), combo_entry->data);
+ }
+ }
+ if (prefs.capture_device) {
+ gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(if_cbxe))),
+ prefs.capture_device);
+ }
+ else if (combo_list != NULL) {
+ gtk_combo_box_set_active(GTK_COMBO_BOX(if_cbxe), 0);
+ }
+ free_capture_combo_list(combo_list);
+
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), if_cbxe, 1, 2, row, row+1);
+ tooltips_text = "The default interface to be captured from.";
+ gtk_widget_set_tooltip_text(if_lb, tooltips_text);
+ gtk_widget_set_tooltip_text(gtk_bin_get_child(GTK_BIN(if_cbxe)), tooltips_text);
+ gtk_widget_show(if_cbxe);
+ g_object_set_data(G_OBJECT(main_vb), DEVICE_KEY, if_cbxe);
+ row++;
+
+ /* Interface properties */
+ ifopts_lb = gtk_label_new("Interfaces:");
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), ifopts_lb, 0, 1, row, row+1);
+ gtk_misc_set_alignment(GTK_MISC(ifopts_lb), 1.0f, 0.5f);
+ gtk_widget_show(ifopts_lb);
+
+ ifopts_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_EDIT);
+ tooltips_text = "Open a dialog box to set various interface options.";
+ gtk_widget_set_tooltip_text(ifopts_lb, tooltips_text);
+ gtk_widget_set_tooltip_text(ifopts_bt, tooltips_text);
+ g_signal_connect(ifopts_bt, "clicked", G_CALLBACK(ifopts_edit_cb), NULL);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), ifopts_bt, 1, 2, row, row+1);
+ row++;
+
+ /* Promiscuous mode */
+ promisc_cb = create_preference_check_button(main_tb, row++,
+ "Capture packets in promiscuous mode:",
+ "Usually a network card will only capture the traffic sent to its own network address. "
+ "If you want to capture all traffic that the network card can \"see\", mark this option. "
+ "See the FAQ for some more details of capturing packets from a switched network. ",
+ prefs.capture_prom_mode);
+ g_object_set_data(G_OBJECT(main_vb), PROM_MODE_KEY, promisc_cb);
+
+ /* Pcap-NG format */
+ pcap_ng_cb = create_preference_check_button(main_tb, row++,
+ "Capture packets in pcap-ng format:",
+ "Capture packets in the next-generation capture file format.",
+ prefs.capture_pcap_ng);
+ g_object_set_data(G_OBJECT(main_vb), PCAP_NG_KEY, pcap_ng_cb);
+
+ /* Real-time capture */
+ sync_cb = create_preference_check_button(main_tb, row++,
+ "Update list of packets in real time:",
+ "Update the list of packets while capture is in progress. "
+ "Don't use this option if you notice packet drops.",
+ prefs.capture_real_time);
+ g_object_set_data(G_OBJECT(main_vb), CAPTURE_REAL_TIME_KEY, sync_cb);
+
+ /* Auto-scroll real-time capture */
+ auto_scroll_cb = create_preference_check_button(main_tb, row++,
+ "Automatic scrolling in live capture:",
+ "Automatic scrolling of the packet list while live capture is in progress. ",
+ prefs.capture_auto_scroll);
+ g_object_set_data(G_OBJECT(main_vb), AUTO_SCROLL_KEY, auto_scroll_cb);
+
+ /* Show capture info dialog */
+ show_info_cb = create_preference_check_button(main_tb, row++,
+ "Hide capture info dialog:",
+ "Hide the capture info dialog while capturing. ",
+ !prefs.capture_show_info);
+ g_object_set_data(G_OBJECT(main_vb), SHOW_INFO_KEY, show_info_cb);
+
+ /* Show 'em what we got */
+ gtk_widget_show_all(main_vb);
+
+ return(main_vb);
+}
+
+void
+capture_prefs_fetch(GtkWidget *w)
+{
+ GtkWidget *if_cbxe, *promisc_cb, *pcap_ng_cb, *sync_cb, *auto_scroll_cb, *show_info_cb;
+ gchar *if_text;
+
+ if_cbxe = (GtkWidget *)g_object_get_data(G_OBJECT(w), DEVICE_KEY);
+ promisc_cb = (GtkWidget *)g_object_get_data(G_OBJECT(w), PROM_MODE_KEY);
+ pcap_ng_cb = (GtkWidget *)g_object_get_data(G_OBJECT(w), PCAP_NG_KEY);
+ sync_cb = (GtkWidget *)g_object_get_data(G_OBJECT(w), CAPTURE_REAL_TIME_KEY);
+ auto_scroll_cb = (GtkWidget *)g_object_get_data(G_OBJECT(w), AUTO_SCROLL_KEY);
+ show_info_cb = (GtkWidget *)g_object_get_data(G_OBJECT(w), SHOW_INFO_KEY);
+
+ if (prefs.capture_device != NULL) {
+ g_free(prefs.capture_device);
+ prefs.capture_device = NULL;
+ }
+ if_text = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(if_cbxe)))));
+ /* Strip out white space */
+ g_strstrip(if_text);
+ /* If there was nothing but white space, treat that as an
+ indication that the user doesn't want to wire in a default
+ device, and just wants the first device in the list chosen. */
+ if (*if_text == '\0') {
+ g_free(if_text);
+ if_text = NULL;
+ }
+ prefs.capture_device = if_text;
+
+ prefs.capture_prom_mode = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(promisc_cb));
+
+ prefs.capture_pcap_ng = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pcap_ng_cb));
+
+ prefs.capture_real_time = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sync_cb));
+
+ prefs.capture_auto_scroll = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(auto_scroll_cb));
+
+ prefs.capture_show_info = !(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(show_info_cb)));
+}
+
+void
+capture_prefs_apply(GtkWidget *w _U_)
+{
+}
+
+void
+capture_prefs_destroy(GtkWidget *w)
+{
+ GtkWidget *caller = gtk_widget_get_toplevel(w);
+ GtkWidget *dlg;
+
+ /* Is there an interface descriptions dialog associated with this
+ Preferences dialog? */
+ dlg = g_object_get_data(G_OBJECT(caller), IFOPTS_DIALOG_PTR_KEY);
+
+ if (dlg != NULL) {
+ /* Yes. Destroy it. */
+ window_destroy(dlg);
+ }
+}
+
+/*
+ * Create an edit interface options dialog.
+ */
+enum
+{
+ DEVICE_COLUMN,
+ DESC_COLUMN,
+#ifdef HAVE_PCAP_CREATE
+ DEF_MONITOR_MODE_COLUMN,
+#endif
+ DEF_LINK_LAYER_COLUMN,
+ COMMENT_COLUMN,
+ HIDE_COLUMN,
+ DLT_COLUMN,
+ N_COLUMN /* The number of columns */
+};
+
+static void
+ifopts_edit_cb(GtkWidget *w, gpointer data _U_)
+{
+ GtkWidget *ifopts_edit_dlg, *cur_scr_win, *main_hb, *main_tb,
+ *cur_opts_fr, *ed_opts_fr, *main_vb,
+ *if_descr_lb,
+ *if_hide_lb,
+ *bbox, *ok_bt, *cancel_bt, *help_bt;
+
+ GtkListStore *list_store;
+ GtkWidget *list;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeView *list_view;
+ GtkTreeSelection *selection;
+
+ int row = 0;
+
+ GtkWidget *caller = gtk_widget_get_toplevel(w);
+
+ /* Has an edit dialog box already been opened for that top-level
+ widget? */
+ ifopts_edit_dlg = g_object_get_data(G_OBJECT(caller), IFOPTS_DIALOG_PTR_KEY);
+ if (ifopts_edit_dlg != NULL) {
+ /* Yes. Just re-activate that dialog box. */
+ reactivate_window(ifopts_edit_dlg);
+ return;
+ }
+
+ /* create a new dialog */
+ ifopts_edit_dlg = dlg_conf_window_new("Wireshark: Preferences: Interface Options");
+ gtk_window_set_default_size(GTK_WINDOW(ifopts_edit_dlg), 1000, 440);
+
+ main_vb = gtk_vbox_new(FALSE, 1);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(ifopts_edit_dlg), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* create current options frame */
+ cur_opts_fr = gtk_frame_new("Interfaces");
+ gtk_container_add(GTK_CONTAINER(main_vb), cur_opts_fr);
+ gtk_widget_show(cur_opts_fr);
+
+ /* create a scrolled window to pack the current options TreeView widget into */
+ cur_scr_win = scrolled_window_new(NULL, NULL);
+ gtk_container_set_border_width(GTK_CONTAINER(cur_scr_win), 3);
+ gtk_container_add(GTK_CONTAINER(cur_opts_fr), cur_scr_win);
+ gtk_widget_show(cur_scr_win);
+
+ /*
+ * Create current options TreeView.
+ */
+ list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX */
+ G_TYPE_STRING, /* Device */
+ G_TYPE_STRING, /* Description */
+#ifdef HAVE_PCAP_CREATE
+ G_TYPE_BOOLEAN, /* Monitor mode */
+#endif
+ G_TYPE_STRING, /* Default link-layer */
+ G_TYPE_STRING, /* Comment */
+ G_TYPE_BOOLEAN, /* Hide? */
+ G_TYPE_INT); /* Dlt */
+
+ list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
+
+ list_view = GTK_TREE_VIEW(list);
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref (G_OBJECT (list_store));
+
+ /*
+ * Create the first column packet, associating the "text" attribute of the
+ * cell_renderer to the first column of the model
+ */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Device", renderer,
+ "text", DEVICE_COLUMN,
+ NULL);
+
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+#ifdef _WIN32
+ gtk_tree_view_column_set_min_width(column, 230);
+#else
+ gtk_tree_view_column_set_min_width(column, 70);
+#endif
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (list_view, column);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Description", renderer,
+ "text", DESC_COLUMN,
+ NULL);
+
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 260);
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (list_view, column);
+
+#ifdef HAVE_PCAP_CREATE
+ /*
+ * XXX - for some reason, this doesn't show up.
+ */
+ renderer = gtk_cell_renderer_toggle_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Default to monitor mode", renderer,
+ "active", DEF_MONITOR_MODE_COLUMN,
+ NULL);
+
+ gtk_tree_view_column_set_resizable(column, FALSE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (list_view, column);
+#endif
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Default link-layer", renderer,
+ "text", DEF_LINK_LAYER_COLUMN,
+ NULL);
+
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 230);
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (list_view, column);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Comment", renderer,
+ "text", COMMENT_COLUMN,
+ NULL);
+
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (list_view, column);
+
+ renderer = gtk_cell_renderer_toggle_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Hide?", renderer,
+ "active", HIDE_COLUMN,
+ NULL);
+
+ gtk_tree_view_column_set_resizable(column, FALSE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (list_view, column);
+
+#if 0
+ /* Don't show the DLT column */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("DLT", renderer,
+ "text", DLT_COLUMN,
+ NULL);
+
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 40);
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (list_view, column);
+#endif
+ /* Setup the selection handler */
+ selection = gtk_tree_view_get_selection(list_view);
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+
+ cur_list = list;
+ gtk_container_add(GTK_CONTAINER(cur_scr_win), cur_list);
+
+ if_selection = selection;
+
+ g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
+ G_CALLBACK (ifopts_edit_ifsel_cb),
+ NULL);
+
+ gtk_widget_show(cur_list);
+
+ /* add interface names to cell */
+ ifopts_if_liststore_add();
+
+ /* create edit options frame */
+ ed_opts_fr = gtk_frame_new("Properties");
+ gtk_box_pack_start(GTK_BOX(main_vb), ed_opts_fr, FALSE, FALSE, 0);
+ gtk_widget_show(ed_opts_fr);
+
+ main_hb = gtk_hbox_new(TRUE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(main_hb), 3);
+ gtk_container_add(GTK_CONTAINER(ed_opts_fr), main_hb);
+ gtk_widget_show(main_hb);
+
+ /* table to hold description text entry and hide button */
+ main_tb = gtk_table_new(IFOPTS_TABLE_ROWS, 4, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_hb), main_tb, TRUE, FALSE, 10);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 10);
+ gtk_widget_show(main_tb);
+
+ if_dev_lb = gtk_label_new("Device:");
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), if_dev_lb, 0, 1, row, row+1);
+ gtk_misc_set_alignment(GTK_MISC(if_dev_lb), 1.0f, 0.5f);
+ gtk_widget_show(if_dev_lb);
+
+ if_dev_lb = gtk_label_new("");
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), if_dev_lb, 1, 2, row, row+1);
+ gtk_misc_set_alignment(GTK_MISC(if_dev_lb), 0.0f, 0.5f);
+ gtk_widget_show(if_dev_lb);
+ row++;
+
+ if_name_lb = gtk_label_new("Description:");
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), if_name_lb, 0, 1, row, row+1);
+ gtk_misc_set_alignment(GTK_MISC(if_name_lb), 1.0f, 0.5f);
+ gtk_widget_show(if_name_lb);
+
+ if_name_lb = gtk_label_new("");
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), if_name_lb, 1, 2, row, row+1);
+ gtk_misc_set_alignment(GTK_MISC(if_name_lb), 0.0f, 0.5f);
+ gtk_widget_show(if_name_lb);
+ row++;
+
+#ifdef HAVE_PCAP_CREATE
+ /* create "monitor mode" label and button */
+ if_monitor_lb = gtk_label_new("Monitor mode:");
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), if_monitor_lb, 0, 1, row, row+1);
+ gtk_misc_set_alignment(GTK_MISC(if_monitor_lb), 1.0f, 0.5f);
+ gtk_widget_show(if_monitor_lb);
+
+ if_monitor_cb = gtk_check_button_new();
+ g_signal_connect(if_monitor_cb, "toggled", G_CALLBACK(ifopts_edit_monitor_changed_cb),
+ cur_list);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), if_monitor_cb, 1, 2, row, row+1);
+ gtk_widget_show(if_monitor_cb);
+ row++;
+#endif
+
+ if_linktype_lb = gtk_label_new("Default link-layer header type:");
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), if_linktype_lb, 0, 1, row, row+1);
+ gtk_misc_set_alignment(GTK_MISC(if_linktype_lb), 1.0f, 0.5f);
+ gtk_widget_show(if_linktype_lb);
+
+ if_linktype_cb = gtk_combo_box_text_new();
+ num_linktypes = 0;
+ interfaces_info_nochange = FALSE;
+ g_signal_connect(if_linktype_cb, "changed", G_CALLBACK(ifopts_edit_linktype_changed_cb),
+ cur_list);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), if_linktype_cb, 1, 2, row, row+1);
+ gtk_widget_show(if_linktype_cb);
+ row++;
+
+ /* create interface description label and text entry */
+ if_descr_lb = gtk_label_new("Comment:");
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), if_descr_lb, 0, 1, row, row+1);
+ gtk_misc_set_alignment(GTK_MISC(if_descr_lb), 1.0f, 0.5f);
+ gtk_widget_show(if_descr_lb);
+
+ if_descr_te = gtk_entry_new();
+ g_signal_connect(if_descr_te, "changed", G_CALLBACK(ifopts_edit_descr_changed_cb),
+ cur_list);
+ gtk_entry_set_max_length(GTK_ENTRY(if_descr_te), IFOPTS_MAX_DESCR_LEN);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), if_descr_te, 1, 2, row, row+1);
+ gtk_widget_show(if_descr_te);
+ row++;
+
+ /* create "hide interface" label and button */
+ if_hide_lb = gtk_label_new("Hide interface?:");
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), if_hide_lb, 0, 1, row, row+1);
+ gtk_misc_set_alignment(GTK_MISC(if_hide_lb), 1.0f, 0.5f);
+ gtk_widget_show(if_hide_lb);
+
+ if_hide_cb = gtk_check_button_new();
+ g_signal_connect(if_hide_cb, "toggled", G_CALLBACK(ifopts_edit_hide_changed_cb),
+ cur_list);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), if_hide_cb, 1, 2, row, row+1);
+ gtk_widget_show(if_hide_cb);
+ row++;
+
+ /* button row: OK and Cancel buttons */
+ bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+
+ ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ gtk_widget_set_tooltip_text(ok_bt, "Save changes and exit dialog");
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(ifopts_edit_ok_cb), ifopts_edit_dlg);
+
+ cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ gtk_widget_set_tooltip_text(cancel_bt, "Cancel and exit dialog");
+ window_set_cancel_button(ifopts_edit_dlg, cancel_bt, window_cancel_button_cb);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb),
+ (gpointer)HELP_CAPTURE_INTERFACE_OPTIONS_DIALOG);
+ gtk_widget_set_tooltip_text (help_bt, "Show topic specific help");
+
+ gtk_widget_grab_default(ok_bt);
+
+ g_signal_connect(ifopts_edit_dlg, "delete_event", G_CALLBACK(window_delete_event_cb),
+ NULL);
+ /* Call a handler when we're destroyed, so we can inform
+ our caller, if any, that we've been destroyed. */
+ g_signal_connect(ifopts_edit_dlg, "destroy", G_CALLBACK(ifopts_edit_destroy_cb), NULL);
+
+ /* Set the key for the new dialog to point to our caller. */
+ g_object_set_data(G_OBJECT(ifopts_edit_dlg), IFOPTS_CALLER_PTR_KEY, caller);
+ /* Set the key for the caller to point to us */
+ g_object_set_data(G_OBJECT(caller), IFOPTS_DIALOG_PTR_KEY, ifopts_edit_dlg);
+
+ gtk_widget_show(ifopts_edit_dlg); /* triggers ifopts_edit_ifsel_cb() with the */
+ /* "interfaces" TreeView first row selected */
+ window_present(ifopts_edit_dlg);
+}
+
+/*
+ * User selected "OK". Create/write preferences strings.
+ */
+static void
+ifopts_edit_ok_cb(GtkWidget *w _U_, gpointer parent_w)
+{
+ if (if_selection){ /* XXX: Cannot be NULL ?? */
+#ifdef HAVE_PCAP_CREATE
+ /* create/write new monitor-mode interfaces string */
+ ifopts_write_new_monitor_mode();
+#endif
+
+ /* create/write new interfaces link-layer string */
+ ifopts_write_new_linklayer();
+
+ /* create/write new interfaces description string */
+ ifopts_write_new_descr();
+
+ /* create/write new "hidden" interfaces string */
+ ifopts_write_new_hide();
+ }
+
+ /* Update welcome page */
+ welcome_if_panel_reload ();
+
+ /* Now nuke this window. */
+ gtk_grab_remove(GTK_WIDGET(parent_w));
+ window_destroy(GTK_WIDGET(parent_w));
+}
+
+static void
+ifopts_edit_destroy_cb(GtkWidget *win, gpointer data _U_)
+{
+ GtkWidget *caller;
+
+ /* Get the widget that requested that we be popped up, if any.
+ (It should arrange to destroy us if it's destroyed, so
+ that we don't get a pointer to a non-existent window here.) */
+ caller = g_object_get_data(G_OBJECT(win), IFOPTS_CALLER_PTR_KEY);
+
+ if (caller != NULL) {
+ /* Tell it we no longer exist. */
+ g_object_set_data(G_OBJECT(caller), IFOPTS_DIALOG_PTR_KEY, NULL);
+ }
+}
+
+static gint
+ifopts_description_to_val (const char *if_name, gboolean monitor_mode,
+ const char *descr)
+{
+ if_capabilities_t *caps;
+ int dlt = -1;
+
+ caps = capture_get_if_capabilities(if_name, monitor_mode, NULL);
+ if (caps != NULL) {
+ if (caps->data_link_types != NULL) {
+ GList *lt_entry;
+ /* XXX: Code skips first entry because that's the default ??? */
+ for (lt_entry = g_list_next(caps->data_link_types);
+ lt_entry != NULL;
+ lt_entry = g_list_next(lt_entry)) {
+ data_link_info_t *dli_p = lt_entry->data;
+ if (dli_p->description) {
+ if (strcmp(dli_p->description, descr) == 0) {
+ dlt = dli_p->dlt;
+ break;
+ }
+ } else {
+ if (strcmp(dli_p->name, descr) == 0) {
+ dlt = dli_p->dlt;
+ break;
+ }
+ }
+ }
+ }
+ free_if_capabilities(caps);
+ }
+ return dlt;
+}
+
+/*
+ * Interface selected callback; update displayed widgets.
+ */
+static void
+ifopts_edit_ifsel_cb(GtkTreeSelection *selection _U_,
+ gpointer data _U_)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ gchar *desc, *comment, *text;
+ gchar *if_name, *linktype;
+#ifdef HAVE_PCAP_CREATE
+ gboolean monitor_mode;
+#endif
+ gboolean hide;
+ if_capabilities_t *caps;
+ gint selected = 0;
+
+ /* Get list_store data for currently selected interface */
+ if (!gtk_tree_selection_get_selected (if_selection, &model, &iter)){
+ return;
+ }
+ gtk_tree_model_get(model, &iter,
+ DEVICE_COLUMN, &if_name,
+ DESC_COLUMN, &desc,
+#ifdef HAVE_PCAP_CREATE
+ DEF_MONITOR_MODE_COLUMN, &monitor_mode,
+#endif
+ DEF_LINK_LAYER_COLUMN, &linktype,
+ COMMENT_COLUMN, &comment,
+ HIDE_COLUMN, &hide,
+ -1);
+
+ /* display the interface device from current interfaces selection */
+ gtk_label_set_text(GTK_LABEL(if_dev_lb), if_name);
+
+ /* display the interface name from current interfaces selection */
+ gtk_label_set_text(GTK_LABEL(if_name_lb), desc);
+
+ /* Ignore "changed" callbacks while we update the Properties widgets */
+ interfaces_info_nochange = TRUE;
+
+ /* display the link-layer header type from current interfaces selection */
+ /* -- remove old linktype list (if any) from the ComboBox */
+ while (num_linktypes > 0) {
+ num_linktypes--;
+ gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(if_linktype_cb), num_linktypes);
+ }
+
+ /*
+ * -- set the state and sensitivity of the monitor-mode checkbox,
+ * and build and add to the ComboBox a linktype list, corresponding
+ * to the interface capabilities of the selected interface
+ */
+#ifdef HAVE_PCAP_CREATE
+ caps = capture_get_if_capabilities(if_name, monitor_mode, NULL);
+#else
+ caps = capture_get_if_capabilities(if_name, FALSE, NULL);
+#endif
+ if (caps != NULL) {
+#ifdef HAVE_PCAP_CREATE
+ gtk_widget_set_sensitive(if_monitor_lb, caps->can_set_rfmon);
+ gtk_widget_set_sensitive(if_monitor_cb, caps->can_set_rfmon);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(if_monitor_cb), monitor_mode);
+#endif
+ if (caps->data_link_types != NULL) {
+ GList *lt_entry;
+ for (lt_entry = caps->data_link_types; lt_entry != NULL;
+ lt_entry = g_list_next(lt_entry)) {
+ data_link_info_t *dli_p = lt_entry->data;
+ text = (dli_p->description != NULL) ? dli_p->description : dli_p->name;
+ if (strcmp(linktype, text) == 0) {
+ selected = num_linktypes;
+ }
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(if_linktype_cb), text);
+ num_linktypes++;
+ }
+ gtk_widget_set_sensitive(if_linktype_lb, num_linktypes >= 2);
+ gtk_widget_set_sensitive(if_linktype_cb, num_linktypes >= 2);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(if_linktype_cb), selected);
+ }
+ free_if_capabilities(caps);
+ }
+#ifdef HAVE_PCAP_CREATE
+ else {
+ gtk_widget_set_sensitive(if_monitor_lb, FALSE);
+ gtk_widget_set_sensitive(if_monitor_cb, FALSE);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(if_monitor_cb), FALSE);
+ }
+#endif
+
+ /* display the interface description from current interfaces selection */
+ gtk_entry_set_text(GTK_ENTRY(if_descr_te), comment);
+
+ /* display the "hide interface" button state from current interfaces selection */
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(if_hide_cb), hide);
+
+ interfaces_info_nochange = FALSE;
+
+ g_free(if_name);
+ g_free(desc);
+ g_free(linktype);
+ g_free(comment);
+}
+
+#ifdef HAVE_PCAP_CREATE
+/*
+ * Monitor-mode toggle button changed callback; update displayed widgets
+ * (the list of link-layer types might change) and list_store for currently
+ * selected interface.
+ */
+static void
+ifopts_edit_monitor_changed_cb(GtkToggleButton *tbt, gpointer udata)
+{
+ GtkTreeModel *list_model;
+ GtkTreeIter list_iter;
+ GtkListStore *list_store;
+ gchar *if_name, *text;
+ gboolean monitor_mode;
+ if_capabilities_t *caps;
+
+ if (interfaces_info_nochange)
+ return;
+
+ if (if_selection == NULL) /* XXX: Cannot be NULL ?? */
+ return;
+
+ if (!gtk_tree_selection_get_selected (if_selection, &list_model, &list_iter)){
+ return;
+ }
+ gtk_tree_model_get(list_model, &list_iter,
+ DEVICE_COLUMN, &if_name,
+ -1);
+
+ /* Ignore "changed" callbacks while we update the Properties widgets */
+ interfaces_info_nochange = TRUE;
+
+ /* display the link-layer header type from current interfaces selection */
+ /* -- remove old linktype list (if any) from the ComboBox */
+ while (num_linktypes > 0) {
+ num_linktypes--;
+ gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(if_linktype_cb), num_linktypes);
+ }
+
+ list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (udata))); /* Get store */
+
+#ifdef HAVE_PCAP_CREATE
+ /* get "monitor mode" button state and set status in list_store for currently selected interface */
+ monitor_mode = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tbt));
+ gtk_list_store_set (list_store, &list_iter,
+ DEF_MONITOR_MODE_COLUMN, monitor_mode,
+ -1);
+ caps = capture_get_if_capabilities(if_name, monitor_mode, NULL);
+#else
+ /* no monitor-mode support */
+ caps = capture_get_if_capabilities(if_name, FALSE, NULL);
+#endif
+
+ /*
+ * -- set the sensitivity of the monitor-mode checkbox, and
+ * build and add to the ComboBox a linktype list for the current
+ * interfaces selection, based on the interface capabilities
+ */
+ if (caps != NULL) {
+#ifdef HAVE_PCAP_CREATE
+ gtk_widget_set_sensitive(if_monitor_lb, caps->can_set_rfmon);
+ gtk_widget_set_sensitive(if_monitor_cb, caps->can_set_rfmon);
+#endif
+ if (caps->data_link_types != NULL) {
+ GList *lt_entry;
+ for (lt_entry = caps->data_link_types; lt_entry != NULL;
+ lt_entry = g_list_next(lt_entry)) {
+ data_link_info_t *dli_p = lt_entry->data;
+ text = (dli_p->description != NULL) ? dli_p->description : dli_p->name;
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(if_linktype_cb), text);
+ num_linktypes++;
+ }
+ gtk_widget_set_sensitive(if_linktype_lb, num_linktypes >= 2);
+ gtk_widget_set_sensitive(if_linktype_cb, num_linktypes >= 2);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(if_linktype_cb), 0);
+ }
+ free_if_capabilities(caps);
+ }
+#ifdef HAVE_PCAP_CREATE
+ else {
+ gtk_widget_set_sensitive(if_monitor_lb, FALSE);
+ gtk_widget_set_sensitive(if_monitor_cb, FALSE);
+ }
+#endif
+
+ interfaces_info_nochange = FALSE;
+ g_signal_emit_by_name(if_linktype_cb, "changed");
+
+ g_free(if_name);
+}
+#endif
+
+/*
+ * Link-layer entry changed callback; update list_store for currently selected interface.
+ */
+static void
+ifopts_edit_linktype_changed_cb(GtkComboBox *cb, gpointer udata)
+{
+ gchar *ifnm, *text;
+#ifdef HAVE_PCAP_CREATE
+ gboolean monitor_mode;
+#endif
+ gint linktype;
+ GtkTreeModel *list_model;
+ GtkTreeIter list_iter;
+ GtkListStore *list_store;
+
+ if (interfaces_info_nochange)
+ return;
+
+ if (if_selection == NULL) /* XXX: Cannot be NULL ?? */
+ return;
+
+ if (!gtk_tree_selection_get_selected (if_selection, &list_model, &list_iter)){
+ return;
+ }
+
+ gtk_tree_model_get(list_model, &list_iter,
+ DEVICE_COLUMN, &ifnm,
+#ifdef HAVE_PCAP_CREATE
+ DEF_MONITOR_MODE_COLUMN, &monitor_mode,
+#endif
+ -1);
+
+ /* get current description text and set value in list_store for currently selected interface */
+ text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT(cb));
+ if (text) {
+#ifdef HAVE_PCAP_CREATE
+ linktype = ifopts_description_to_val(ifnm, monitor_mode, text);
+#else
+ linktype = ifopts_description_to_val(ifnm, FALSE, text);
+#endif
+ list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (udata))); /* Get store */
+ gtk_list_store_set (list_store, &list_iter,
+ DEF_LINK_LAYER_COLUMN, text,
+ DLT_COLUMN, linktype,
+ -1);
+ g_free(text);
+ }
+}
+
+/*
+ * Comment text entry changed callback; update list_store for currently selected interface.
+ */
+static void
+ifopts_edit_descr_changed_cb(GtkEditable *ed, gpointer udata)
+{
+ gchar *text;
+ GtkTreeModel *list_model;
+ GtkTreeIter list_iter;
+ GtkListStore *list_store;
+
+ if (interfaces_info_nochange)
+ return;
+
+ if (if_selection == NULL) /* XXX: Cannot be NULL ?? */
+ return;
+
+ if (!gtk_tree_selection_get_selected (if_selection, &list_model, &list_iter)){
+ return;
+ }
+
+ /* get current description text and set value in list_store for currently selected interface */
+ text = gtk_editable_get_chars(GTK_EDITABLE(ed), 0, -1);
+ /* replace any reserved formatting characters "()," with spaces */
+ g_strdelimit(text, "(),", ' ');
+
+ list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (udata))); /* Get store */
+ gtk_list_store_set (list_store, &list_iter,
+ COMMENT_COLUMN, text,
+ -1);
+
+ g_free(text);
+}
+
+/*
+ * Hide toggle button changed callback; update list_store for currently selected interface .
+ */
+static void
+ifopts_edit_hide_changed_cb(GtkToggleButton *tbt, gpointer udata)
+{
+ GtkTreeModel *list_model;
+ GtkTreeIter list_iter;
+ GtkListStore *list_store;
+
+ if (interfaces_info_nochange)
+ return;
+
+ if (if_selection == NULL) /* XXX: Cannot be NULL ?? */
+ return;
+
+ if (!gtk_tree_selection_get_selected (if_selection, &list_model, &list_iter)){
+ return;
+ }
+
+ list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (udata))); /* Get store */
+ /* get "hide" button state and set status in list_store for currently selected interface */
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tbt)) == TRUE)
+ gtk_list_store_set (list_store, &list_iter,
+ HIDE_COLUMN, TRUE,
+ -1);
+ else
+ gtk_list_store_set (list_store, &list_iter,
+ HIDE_COLUMN, FALSE,
+ -1);
+}
+
+/*
+ * Add any saved interface options that apply to interfaces ListStore.
+ *
+ * NOTE:
+ * Interfaces that have been removed from the machine or disabled and
+ * no longer apply are ignored. Therefore, if the user subsequently
+ * selects "OK", the options for these interfaces are lost (they're
+ * lost permanently if "Save" is selected).
+ */
+static void
+ifopts_options_add(GtkListStore *list_store, if_info_t *if_info)
+{
+ gchar *p;
+ gchar *ifnm;
+ gchar *desc;
+ gchar *pr_descr;
+ gchar *text[] = { NULL, NULL, NULL, NULL };
+ if_capabilities_t *caps;
+#ifdef HAVE_PCAP_CREATE
+ gboolean monitor_mode;
+#endif
+ gint linktype;
+ gboolean hide;
+ GtkTreeIter iter;
+
+ /* set device name text */
+ text[0] = g_strdup(if_info->name);
+
+ /* set OS description */
+ if (if_info->description != NULL)
+ text[1] = g_strdup(if_info->description);
+ else
+ text[1] = g_strdup("");
+
+#ifdef HAVE_PCAP_CREATE
+ /* get default monitor mode setting */
+ monitor_mode = prefs_capture_device_monitor_mode(if_info->name);
+ caps = capture_get_if_capabilities(if_info->name, monitor_mode, NULL);
+#else
+ /* no monitor-mode support */
+ caps = capture_get_if_capabilities(if_info->name, FALSE, NULL);
+#endif
+
+ /* set default link-layer header type */
+ linktype = capture_dev_user_linktype_find(if_info->name);
+ if (caps != NULL) {
+ if (caps->data_link_types != NULL) {
+ GList *lt_entry;
+ for (lt_entry = caps->data_link_types; lt_entry != NULL;
+ lt_entry = g_list_next(lt_entry)) {
+ data_link_info_t *dli_p = lt_entry->data;
+ /* If we have no previous link-layer header type we use the first one */
+ if (linktype == -1 || linktype == dli_p->dlt) {
+ if (dli_p->description) {
+ text[2] = g_strdup(dli_p->description);
+ } else {
+ text[2] = g_strdup(dli_p->name);
+ }
+ break;
+ }
+ }
+ }
+ free_if_capabilities(caps);
+ }
+ /* if we have no link-layer */
+ if (text[2] == NULL)
+ text[2] = g_strdup("");
+
+ /* add interface descriptions */
+ if (prefs.capture_devices_descr != NULL) {
+ /* create working copy of device descriptions */
+ pr_descr = g_strdup(prefs.capture_devices_descr);
+
+ /* if we find a description for this interface */
+ if ((ifnm = strstr(pr_descr, if_info->name)) != NULL) {
+ p = ifnm;
+ while (*p != '\0') {
+ /* found left parenthesis, start of description */
+ if (*p == '(') {
+ p++;
+ /* if syntax error */
+ if ((*p == '\0') || (*p == ',') || (*p == '(') || (*p == ')'))
+ break;
+
+ /* save pointer to beginning of description */
+ desc = p;
+ p++;
+ /* skip to end of description */
+ while (*p != '\0') {
+ /* if syntax error */
+ if ((*p == ',') || (*p == '('))
+ break;
+
+ /* end of description */
+ else if (*p == ')') {
+ /* terminate and set description text */
+ *p = '\0';
+ text[3] = g_strdup(desc);
+ break;
+ }
+ p++;
+ }
+ /* get out */
+ break;
+ } else
+ p++;
+ }
+ }
+
+ g_free(pr_descr);
+ }
+
+ /* if we have no description */
+ if (text[3] == NULL)
+ text[3] = g_strdup("");
+
+ /* check if interface is "hidden" */
+ hide = prefs_is_capture_device_hidden(if_info->name);
+
+ /* add row to ListStore */
+
+ gtk_list_store_insert_with_values( list_store , &iter, G_MAXINT,
+ DEVICE_COLUMN, text[0],
+ DESC_COLUMN, text[1],
+#ifdef HAVE_PCAP_CREATE
+ DEF_MONITOR_MODE_COLUMN, monitor_mode,
+#endif
+ DEF_LINK_LAYER_COLUMN, text[2],
+ COMMENT_COLUMN, text[3],
+ HIDE_COLUMN, hide,
+ DLT_COLUMN, linktype,
+ -1);
+
+ ifopts_options_free(text);
+}
+
+static void
+ifopts_options_free(gchar *text[])
+{
+ gint i;
+
+ for (i=0; i < IFOPTS_LIST_TEXT_COLS; i++) {
+ if (text[i] != NULL) {
+ g_free(text[i]);
+ text[i] = NULL;
+ }
+ }
+}
+
+/*
+ * Add all interfaces to interfaces ListStore.
+ */
+static void
+ifopts_if_liststore_add(void)
+{
+ GList *if_list, *ifl_p;
+ int err;
+ gchar *err_str;
+
+ if_list = capture_interface_list(&err, &err_str); /* if_list = ptr to first element of list (or NULL) */
+ if (if_list == NULL) {
+ if (err != NO_INTERFACES_FOUND) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_str);
+ }
+ g_free(err_str);
+ return;
+ }
+
+ /* We have an interface list. */
+ /* add OS description + interface name text to ListStore */
+ for (ifl_p = if_list; ifl_p != NULL; ifl_p = g_list_next(ifl_p)) {
+ /* should never happen, but just in case */
+ if ((ifl_p->data) == NULL)
+ continue;
+ /* fill current options ListStore with current preference values */
+ ifopts_options_add(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (cur_list))),
+ (if_info_t *)ifl_p->data);
+ }
+ free_interface_list(if_list);
+}
+
+#ifdef HAVE_PCAP_CREATE
+/*
+ * Create/write new "monitor mode" interfaces string based on current CList.
+ * Put it into the preferences value.
+ */
+static void
+ifopts_write_new_monitor_mode(void)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ gboolean more_items = TRUE;
+ gint first_if = TRUE; /* flag to check if first in list */
+ gchar *ifnm;
+ gboolean monitor_mode;
+ gchar *new_monitor_mode;
+
+ /* new preferences "monitor mode" interfaces string */
+ new_monitor_mode = g_malloc0(MAX_VAL_LEN);
+
+ /* get "monitor mode" flag text for each row (interface) */
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(cur_list));
+ store = GTK_LIST_STORE(model);
+ if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
+ while (more_items) {
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
+ DEVICE_COLUMN, &ifnm,
+ DEF_MONITOR_MODE_COLUMN, &monitor_mode,
+ -1);
+
+ /* if flag text is "No", skip this interface */
+ if (!monitor_mode){
+ more_items = gtk_tree_model_iter_next (model,&iter);
+ continue;
+ }
+
+ /*
+ * create/cat interface to new string
+ */
+ if (first_if != TRUE)
+ g_strlcat (new_monitor_mode, ",", MAX_VAL_LEN);
+ g_strlcat (new_monitor_mode, ifnm, MAX_VAL_LEN);
+
+ /* set first-in-list flag to false */
+ first_if = FALSE;
+ more_items = gtk_tree_model_iter_next (model,&iter);
+ }
+
+ /* write new "hidden" string to preferences */
+ if (strlen(new_monitor_mode) > 0) {
+ g_free(prefs.capture_devices_monitor_mode);
+ prefs.capture_devices_monitor_mode = new_monitor_mode;
+ }
+ /* no "hidden" interfaces */
+ else {
+ g_free(prefs.capture_devices_monitor_mode);
+ g_free(new_monitor_mode);
+ prefs.capture_devices_monitor_mode = NULL;
+ }
+ }
+}
+#endif
+
+/*
+ * Create/write new interfaces link-layer string based on current CList.
+ * Put it into the preferences value.
+ */
+static void
+ifopts_write_new_linklayer(void)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ gboolean more_items = TRUE, first_if = TRUE; /* flag to check if first in list */
+ gchar *ifnm;
+ gint linktype;
+ gchar *tmp_linklayer;
+ gchar *new_linklayer;
+
+ /* new preferences interfaces link-layer string */
+ new_linklayer = g_malloc0(MAX_VAL_LEN);
+
+ /* get link-layer for each row (interface) */
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(cur_list));
+ store = GTK_LIST_STORE(model);
+ if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
+
+ while (more_items) {
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
+ DEVICE_COLUMN, &ifnm,
+ DLT_COLUMN, &linktype,
+ -1);
+
+ if (linktype == -1){
+ more_items = gtk_tree_model_iter_next (model,&iter);
+ continue;
+ }
+
+ if (first_if != TRUE) {
+ g_strlcat (new_linklayer, ",", MAX_VAL_LEN);
+ }
+ /*
+ * create/cat interface link-layer to new string
+ * (leave space for parens, comma and terminator)
+ */
+ tmp_linklayer = g_strdup_printf("%s(%d)", ifnm, linktype);
+ g_strlcat(new_linklayer, tmp_linklayer, MAX_VAL_LEN);
+ g_free(tmp_linklayer);
+ g_free(ifnm);
+ /* set first-in-list flag to false */
+ first_if = FALSE;
+ more_items = gtk_tree_model_iter_next (model,&iter);
+ }
+
+ /* write new link-layer string to preferences */
+ if (strlen(new_linklayer) > 0) {
+ g_free(prefs.capture_devices_linktypes);
+ prefs.capture_devices_linktypes = new_linklayer;
+ }
+ /* no link-layers */
+ else {
+ g_free(prefs.capture_devices_linktypes);
+ g_free(new_linklayer);
+ prefs.capture_devices_linktypes = NULL;
+ }
+ }
+}
+
+/*
+ * Create/write new interfaces description string based on current CList.
+ * Put it into the preferences value.
+ */
+static void
+ifopts_write_new_descr(void)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ gboolean more_items = TRUE;
+ gboolean first_if = TRUE; /* flag to check if first in list */
+ gchar *ifnm;
+ gchar *desc;
+ gchar *tmp_descr;
+ gchar *new_descr;
+
+ /* new preferences interfaces description string */
+ new_descr = g_malloc0(MAX_VAL_LEN);
+
+ /* get description for each row (interface) */
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(cur_list));
+ store = GTK_LIST_STORE(model);
+ if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
+ while (more_items) {
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
+ DEVICE_COLUMN, &ifnm,
+ COMMENT_COLUMN, &desc,
+ -1);
+
+ /* if no description, skip this interface */
+ if (strlen(desc) == 0){
+ more_items = gtk_tree_model_iter_next (model,&iter);
+ continue;
+ }
+ /*
+ * create/cat interface description to new string
+ * (leave space for parens, comma and terminator)
+ */
+ if (first_if != TRUE) {
+ g_strlcat (new_descr, ",", MAX_VAL_LEN);
+ }
+
+ tmp_descr = g_strdup_printf("%s(%s)", ifnm, desc);
+ g_strlcat(new_descr, tmp_descr, MAX_VAL_LEN);
+ g_free(tmp_descr);
+
+ /* set first-in-list flag to false */
+ first_if = FALSE;
+ more_items = gtk_tree_model_iter_next (model,&iter);
+ }
+
+ /* write new description string to preferences */
+ if (strlen(new_descr) > 0) {
+ g_free(prefs.capture_devices_descr);
+ prefs.capture_devices_descr = new_descr;
+ }
+ /* no descriptions */
+ else {
+ g_free(prefs.capture_devices_descr);
+ g_free(new_descr);
+ prefs.capture_devices_descr = NULL;
+ }
+ }
+}
+
+/*
+ * Create/write new "hidden" interfaces string based on current CList.
+ * Put it into the preferences value.
+ */
+static void
+ifopts_write_new_hide(void)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ gboolean more_items = TRUE;
+ gint first_if = TRUE; /* flag to check if first in list */
+ gchar *ifnm;
+ gboolean hide;
+ gchar *new_hide;
+
+ /* new preferences "hidden" interfaces string */
+ new_hide = g_malloc0(MAX_VAL_LEN);
+
+ /* get "hide" flag text for each row (interface) */
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(cur_list));
+ store = GTK_LIST_STORE(model);
+ if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
+ while (more_items) {
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
+ DEVICE_COLUMN, &ifnm,
+ HIDE_COLUMN, &hide,
+ -1);
+
+ /* if flag text is "No", skip this interface */
+ if (!hide){
+ more_items = gtk_tree_model_iter_next (model,&iter);
+ continue;
+ }
+
+ /*
+ * create/cat interface to new string
+ */
+ if (first_if != TRUE)
+ g_strlcat (new_hide, ",", MAX_VAL_LEN);
+ g_strlcat (new_hide, ifnm, MAX_VAL_LEN);
+
+ /* set first-in-list flag to false */
+ first_if = FALSE;
+ more_items = gtk_tree_model_iter_next (model,&iter);
+ }
+
+ /* write new "hidden" string to preferences */
+ if (strlen(new_hide) > 0) {
+ g_free(prefs.capture_devices_hide);
+ prefs.capture_devices_hide = new_hide;
+ }
+ /* no "hidden" interfaces */
+ else {
+ g_free(prefs.capture_devices_hide);
+ g_free(new_hide);
+ prefs.capture_devices_hide = NULL;
+ }
+ }
+}
+
+#endif /* HAVE_LIBPCAP */
diff --git a/ui/gtk/prefs_capture.h b/ui/gtk/prefs_capture.h
new file mode 100644
index 0000000000..7f19e4d5da
--- /dev/null
+++ b/ui/gtk/prefs_capture.h
@@ -0,0 +1,57 @@
+/* capture_prefs.h
+ * Definitions for capture preferences window
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __CAPTURE_PREFS_H__
+#define __CAPTURE_PREFS_H__
+
+/** @file
+ * "Capture" preferences page.
+ * @ingroup prefs_group
+ */
+
+/** Build a capture preferences page.
+ *
+ * @return the new capture preferences page
+ */
+GtkWidget *capture_prefs_show(void);
+
+/** Fetch preference values from page.
+ *
+ * @param widget widget from capture_prefs_show()
+ */
+void capture_prefs_fetch(GtkWidget *widget);
+
+/** Apply preference values from page.
+ *
+ * @param widget widget from capture_prefs_show()
+ */
+void capture_prefs_apply(GtkWidget *widget);
+
+/** Destroy preference values from page.
+ *
+ * @param widget widget from capture_prefs_show()
+ */
+void capture_prefs_destroy(GtkWidget *widget);
+
+#endif
diff --git a/ui/gtk/prefs_column.c b/ui/gtk/prefs_column.c
new file mode 100644
index 0000000000..1e919564c5
--- /dev/null
+++ b/ui/gtk/prefs_column.c
@@ -0,0 +1,808 @@
+/* column_prefs.c
+ * Dialog box for column preferences
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/prefs.h>
+#include <epan/column_info.h>
+#include <epan/column.h>
+#include <epan/strutil.h>
+
+#include "../globals.h"
+
+#include "ui/gtk/prefs_column.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/new_packet_list.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/filter_autocomplete.h"
+#include "ui/gtk/old-gtk-compat.h"
+
+static GtkWidget *remove_bt, *field_te, *field_lb, *occurrence_te, *occurrence_lb, *fmt_cmb;
+static gulong column_menu_changed_handler_id;
+static gulong column_field_changed_handler_id;
+static gulong column_occurrence_changed_handler_id;
+static gulong column_row_deleted_handler_id;
+
+static void column_list_new_cb(GtkWidget *, gpointer);
+static void column_list_delete_cb(GtkWidget *, gpointer);
+static void column_list_select_cb(GtkTreeSelection *, gpointer);
+static void column_menu_changed_cb(GtkWidget *, gpointer);
+static void column_field_changed_cb(GtkEditable *, gpointer);
+static void column_occurrence_changed_cb(GtkEditable *, gpointer);
+static void column_dnd_row_deleted_cb(GtkTreeModel *, GtkTreePath *, gpointer);
+static gboolean column_title_changed_cb(GtkCellRendererText *, const gchar *, const gchar *, gpointer);
+
+static char custom_occurrence_str[8] = "";
+
+enum {
+ VISIBLE_COLUMN,
+ TITLE_COLUMN,
+ FORMAT_COLUMN,
+ DATA_COLUMN,
+ N_COLUMN /* The number of columns */
+};
+
+/* Visible toggled */
+static void
+visible_toggled(GtkCellRendererToggle *cell _U_, gchar *path_str, gpointer data)
+{
+ GtkTreeModel *model = (GtkTreeModel *)data;
+ GtkTreeIter iter;
+ GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
+ GList *clp;
+ fmt_data *cfmt;
+
+ gtk_tree_model_get_iter(model, &iter, path);
+ gtk_tree_model_get(model, &iter, DATA_COLUMN, &clp, -1);
+
+ cfmt = (fmt_data *) clp->data;
+ if (cfmt->visible)
+ cfmt->visible = FALSE;
+ else
+ cfmt->visible = TRUE;
+
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, VISIBLE_COLUMN, cfmt->visible, -1);
+ cfile.cinfo.columns_changed = TRUE;
+
+ gtk_tree_path_free(path);
+} /* visible_toggled */
+
+/*
+ * Create and display the column selection widgets.
+ * Called as part of the creation of the Preferences notebook ( Edit ! Preferences )
+ */
+GtkWidget *
+column_prefs_show(GtkWidget *prefs_window) {
+ GtkWidget *main_vb, *bottom_hb, *column_l, *add_bt, *tb, *lb;
+ GtkWidget *list_vb, *list_lb, *list_sc;
+ GtkWidget *add_remove_vb;
+ GtkWidget *props_fr, *props_hb;
+ GList *clp;
+ fmt_data *cfmt;
+ gint i;
+ gchar *fmt;
+ const gchar *column_titles[] = {"Displayed", "Title", "Field type"};
+ GtkListStore *store;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *sel;
+ GtkTreeIter iter;
+ GtkTreeIter first_iter;
+ gint first_row = TRUE;
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_widget_show(main_vb);
+
+ list_vb = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (list_vb), 5);
+ gtk_widget_show (list_vb);
+ gtk_box_pack_start (GTK_BOX (main_vb), list_vb, TRUE, TRUE, 0);
+
+ list_lb = gtk_label_new (("[The first list entry will be displayed as the leftmost column - Drag and drop entries to change column order]"));
+ gtk_widget_show (list_lb);
+ gtk_box_pack_start (GTK_BOX (list_vb), list_lb, FALSE, FALSE, 0);
+
+ list_sc = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(list_sc), GTK_SHADOW_IN);
+ gtk_container_add(GTK_CONTAINER(list_vb), list_sc);
+ gtk_widget_show(list_sc);
+
+ store = gtk_list_store_new(N_COLUMN,
+ G_TYPE_BOOLEAN,
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER);
+ column_row_deleted_handler_id =
+ g_signal_connect(GTK_TREE_MODEL(store), "row-deleted", G_CALLBACK(column_dnd_row_deleted_cb), NULL);
+
+ column_l = tree_view_new(GTK_TREE_MODEL(store));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(column_l), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(column_l), FALSE);
+ gtk_tree_view_set_reorderable(GTK_TREE_VIEW(column_l), TRUE);
+ gtk_widget_set_tooltip_text(column_l, "Click on a title to change its name.\nDrag an item to change its order.");
+
+ renderer = gtk_cell_renderer_toggle_new();
+ g_signal_connect(renderer, "toggled", G_CALLBACK(visible_toggled), store);
+ column = gtk_tree_view_column_new_with_attributes(column_titles[VISIBLE_COLUMN], renderer, "active", VISIBLE_COLUMN, NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(column_l), column);
+
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(G_OBJECT(renderer), "editable", TRUE, NULL);
+ g_signal_connect (renderer, "edited", G_CALLBACK(column_title_changed_cb), GTK_TREE_MODEL(store));
+ column = gtk_tree_view_column_new_with_attributes(column_titles[TITLE_COLUMN], renderer, "text", TITLE_COLUMN, NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(column_l), column);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(column_titles[FORMAT_COLUMN], renderer, "text", FORMAT_COLUMN, NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(column_l), column);
+
+ /* XXX - make this match the packet list prefs? */
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(column_l));
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+ g_signal_connect(sel, "changed", G_CALLBACK(column_list_select_cb), NULL);
+
+ gtk_container_add(GTK_CONTAINER(list_sc), column_l);
+ gtk_widget_show(column_l);
+
+ clp = g_list_first(prefs.col_list);
+ while (clp) {
+ cfmt = (fmt_data *) clp->data;
+ if (cfmt->fmt == COL_CUSTOM) {
+ if (cfmt->custom_occurrence) {
+ fmt = g_strdup_printf("%s (%s#%d)", col_format_desc(cfmt->fmt), cfmt->custom_field, cfmt->custom_occurrence);
+ } else {
+ fmt = g_strdup_printf("%s (%s)", col_format_desc(cfmt->fmt), cfmt->custom_field);
+ }
+ } else {
+ if (cfmt->custom_field) {
+ /* Delete custom_field from previous changes */
+ g_free (cfmt->custom_field);
+ cfmt->custom_field = NULL;
+ cfmt->custom_occurrence = 0;
+ }
+ fmt = g_strdup_printf("%s", col_format_desc(cfmt->fmt));
+ }
+ gtk_list_store_insert_with_values(store, &iter, G_MAXINT,
+ VISIBLE_COLUMN, cfmt->visible,
+ TITLE_COLUMN, cfmt->title, FORMAT_COLUMN, fmt, DATA_COLUMN, clp, -1);
+
+ if (first_row) {
+ first_iter = iter;
+ first_row = FALSE;
+ }
+ clp = clp->next;
+ g_free (fmt);
+ }
+ g_object_unref(G_OBJECT(store));
+
+ /* Bottom row: Add/remove buttons and properties */
+ bottom_hb = gtk_hbox_new(FALSE, 5);
+ gtk_box_pack_start (GTK_BOX (main_vb), bottom_hb, FALSE, TRUE, 0);
+ gtk_widget_show(bottom_hb);
+
+ /* Add / remove buttons */
+ add_remove_vb = gtk_vbox_new (TRUE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (add_remove_vb), 5);
+ gtk_box_pack_start (GTK_BOX (bottom_hb), add_remove_vb, FALSE, FALSE, 0);
+ gtk_widget_show(add_remove_vb);
+
+ add_bt = gtk_button_new_from_stock(GTK_STOCK_ADD);
+ g_signal_connect(add_bt, "clicked", G_CALLBACK(column_list_new_cb), column_l);
+ gtk_box_pack_start (GTK_BOX (add_remove_vb), add_bt, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text(add_bt, "Add a new column at the end of the list.");
+ gtk_widget_show(add_bt);
+
+ remove_bt = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
+ gtk_widget_set_sensitive(remove_bt, FALSE);
+ g_signal_connect(remove_bt, "clicked", G_CALLBACK(column_list_delete_cb), column_l);
+ gtk_box_pack_start (GTK_BOX (add_remove_vb), remove_bt, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text(remove_bt, "Remove the selected column.");
+ gtk_widget_show(remove_bt);
+
+ /* properties frame */
+ props_fr = gtk_frame_new("Properties");
+ gtk_box_pack_start (GTK_BOX (bottom_hb), props_fr, TRUE, TRUE, 0);
+ gtk_widget_show(props_fr);
+
+ /* Column name entry and format selection */
+ tb = gtk_table_new(2, 4, FALSE);
+ gtk_container_set_border_width(GTK_CONTAINER(tb), 5);
+ gtk_container_add(GTK_CONTAINER(props_fr), tb);
+ gtk_table_set_row_spacings(GTK_TABLE(tb), 10);
+ gtk_table_set_col_spacings(GTK_TABLE(tb), 15);
+ gtk_widget_show(tb);
+
+ lb = gtk_label_new("Field type:");
+ gtk_misc_set_alignment(GTK_MISC(lb), 0.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(tb), lb, 0, 1, 0, 1);
+ gtk_widget_set_tooltip_text(lb, "Select which packet information to present in the column.");
+ gtk_widget_show(lb);
+
+ props_hb = gtk_hbox_new(FALSE, 5);
+ gtk_table_attach(GTK_TABLE(tb), props_hb, 1, 2, 0, 1, GTK_FILL, GTK_SHRINK, 0, 0);
+ gtk_widget_set_tooltip_text(props_hb, "Select which packet information to present in the column.");
+ gtk_widget_show(props_hb);
+
+ field_lb = gtk_label_new("Field name:");
+ gtk_misc_set_alignment(GTK_MISC(field_lb), 0.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(tb), field_lb, 0, 1, 1, 2);
+ gtk_widget_set_sensitive(field_lb, FALSE);
+ gtk_widget_set_tooltip_text(field_lb,
+ "Field name used when field type is \"Custom\". "
+ "This string has the same syntax as a display filter string.");
+ gtk_widget_show(field_lb);
+
+ field_te = gtk_entry_new();
+ g_object_set_data (G_OBJECT(field_te), E_FILT_FIELD_NAME_ONLY_KEY, "");
+ g_signal_connect(field_te, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
+
+ /* XXX: column_field_changed_cb will be called for every character entered in the entry box. */
+ /* Consider Changing logic so that the field is "accepted" only when a return is entered ?? */
+ /* Also: entry shouldn't be accepted if it's not a valid filter ? */
+ column_field_changed_handler_id =
+ g_signal_connect(field_te, "changed", G_CALLBACK(column_field_changed_cb), column_l);
+
+ g_object_set_data(G_OBJECT(main_vb), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+ g_signal_connect(field_te, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
+ g_signal_connect(prefs_window, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
+ colorize_filter_te_as_empty(field_te);
+ gtk_table_attach_defaults(GTK_TABLE(tb), field_te, 1, 2, 1, 2);
+ gtk_widget_set_sensitive(field_te, FALSE);
+ gtk_widget_set_tooltip_text(field_te,
+ "Field name used when field type is \"Custom\". "
+ "This string has the same syntax as a display filter string.");
+ gtk_widget_show(field_te);
+
+ occurrence_lb = gtk_label_new("Field occurrence:");
+ gtk_misc_set_alignment(GTK_MISC(occurrence_lb), 0.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(tb), occurrence_lb, 2, 3, 1, 2);
+ gtk_widget_set_sensitive(occurrence_lb, FALSE);
+ gtk_widget_set_tooltip_text(occurrence_lb,
+ "Field occurence to use. "
+ "0=all (default), 1=first, 2=second, ..., -1=last.");
+ gtk_widget_show(occurrence_lb);
+
+ occurrence_te = gtk_entry_new();
+ gtk_entry_set_max_length (GTK_ENTRY(occurrence_te),4);
+ g_object_set_data (G_OBJECT(occurrence_te), "occurrence", "");
+
+ /* XXX: column_occurrence_changed_cb will be called for every character entered in the entry box. */
+ /* Consider Changing logic so that the field is "accepted" only when a return is entered ?? */
+ column_occurrence_changed_handler_id =
+ g_signal_connect(occurrence_te, "changed", G_CALLBACK(column_occurrence_changed_cb), column_l);
+
+ gtk_table_attach_defaults(GTK_TABLE(tb), occurrence_te, 3, 4, 1, 2);
+ gtk_widget_set_sensitive(occurrence_te, FALSE);
+ gtk_widget_set_tooltip_text(occurrence_te,
+ "Field occurence to use. "
+ "0=all (default), 1=first, 2=second, ..., -1=last.");
+ gtk_widget_show(occurrence_te);
+
+ fmt_cmb = gtk_combo_box_text_new();
+
+ for (i = 0; i < NUM_COL_FMTS; i++)
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(fmt_cmb), col_format_desc(i));
+
+ column_menu_changed_handler_id = g_signal_connect(fmt_cmb, "changed", G_CALLBACK(column_menu_changed_cb), column_l);
+
+ gtk_widget_set_sensitive(fmt_cmb, FALSE);
+ gtk_box_pack_start(GTK_BOX(props_hb), fmt_cmb, FALSE, FALSE, 0);
+ gtk_widget_show(fmt_cmb);
+
+ /* select the first menu list row. */
+ /* Triggers call to column_list_select_cb(). */
+ gtk_tree_selection_select_iter(sel, &first_iter);
+
+ return(main_vb);
+}
+
+void
+column_prefs_add_custom(gint fmt, const gchar *title, const gchar *custom_field, gint custom_occurrence)
+{
+ GList *clp;
+ fmt_data *cfmt, *last_cfmt;
+
+ cfmt = (fmt_data *) g_malloc(sizeof(fmt_data));
+ /*
+ * Because a single underscore is interpreted as a signal that the next character
+ * is going to be marked as accelerator for this header (i.e. is going to be
+ * shown underlined), escape it be inserting a second consecutive underscore.
+ */
+ cfmt->title = g_strdup(title);
+ cfmt->fmt = fmt;
+ cfmt->custom_field = g_strdup(custom_field);
+ cfmt->custom_occurrence = custom_occurrence;
+ cfmt->resolved = TRUE;
+
+ if (custom_field) {
+ cfmt->visible = TRUE;
+ clp = g_list_last(prefs.col_list);
+ last_cfmt = (fmt_data *) clp->data;
+ if (last_cfmt->fmt == COL_INFO) {
+ /* Last column is COL_INFO, add custom column before this */
+ prefs.col_list = g_list_insert(prefs.col_list, cfmt, g_list_length(prefs.col_list)-1);
+ } else {
+ prefs.col_list = g_list_append(prefs.col_list, cfmt);
+ }
+ } else {
+ cfmt->visible = FALSE; /* Will be set to TRUE in visible_toggled() when added to list */
+ prefs.col_list = g_list_append(prefs.col_list, cfmt);
+ }
+}
+
+void
+column_prefs_remove(gint col)
+{
+ GList *clp = g_list_nth(prefs.col_list, col);
+ fmt_data *cfmt = (fmt_data *) clp->data;
+
+ g_free(cfmt->title);
+ g_free(cfmt->custom_field);
+ g_free(cfmt);
+ prefs.col_list = g_list_remove_link(prefs.col_list, clp);
+}
+
+/* To do: add input checking to each of these callbacks */
+
+static void
+column_list_new_cb(GtkWidget *w _U_, gpointer data) {
+ gint cur_fmt;
+ const gchar *title = "New Column";
+ GtkTreeView *column_l = GTK_TREE_VIEW(data);
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ GtkTreeViewColumn *title_column;
+
+ cur_fmt = COL_NUMBER; /* Set the default new column type */
+ column_prefs_add_custom (cur_fmt, title, NULL, 0);
+
+ model = gtk_tree_view_get_model(column_l);
+ gtk_list_store_insert_with_values(GTK_LIST_STORE(model), &iter, G_MAXINT,
+ VISIBLE_COLUMN, TRUE,
+ TITLE_COLUMN, title,
+ FORMAT_COLUMN, col_format_desc(cur_fmt),
+ DATA_COLUMN, g_list_last(prefs.col_list),
+ -1);
+
+ /* Triggers call to column_list_select_cb() */
+ gtk_tree_selection_select_iter(gtk_tree_view_get_selection(column_l), &iter);
+
+ /* Set the cursor to the 'Title' column of the newly added row and enable editing */
+ /* XXX: If displaying the new title ["New column"] widens the title column of the */
+ /* treeview, then the set_cursor below doesn't properly generate an entry */
+ /* box around the title text. The width of the box appears to be the column */
+ /* width before the treeview title column was widened. Seems like a bug... */
+ /* I haven't found a work-around. */
+ path = gtk_tree_model_get_path(model, &iter);
+ title_column = gtk_tree_view_get_column(column_l, 0);
+ gtk_tree_view_set_cursor(column_l, path, title_column, TRUE);
+ gtk_tree_path_free(path);
+
+ cfile.cinfo.columns_changed = TRUE;
+}
+
+
+static void
+column_list_delete_cb(GtkWidget *w _U_, gpointer data) {
+ GtkTreeView *column_l = GTK_TREE_VIEW(data);
+ GList *clp;
+ fmt_data *cfmt;
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeIter new_iter;
+
+ sel = gtk_tree_view_get_selection(column_l);
+ if (gtk_tree_selection_get_selected(sel, &model, &iter))
+ {
+ gtk_tree_model_get(model, &iter, DATA_COLUMN, &clp, -1);
+
+ cfmt = (fmt_data *) clp->data;
+ g_free(cfmt->title);
+ g_free(cfmt->custom_field);
+ g_free(cfmt);
+ prefs.col_list = g_list_remove_link(prefs.col_list, clp);
+
+ /* Change the row selection to the next row (if available) or */
+ /* the previous row (if available). If there's only one row */
+ /* in the store (no previous and no next), then the selection */
+ /* will not be changed. */
+
+ /* Note that gtk_tree_selection_select_iter() will trigger a */
+ /* call to column_list_select_cb(). */
+
+ new_iter = iter;
+ if ( gtk_tree_model_iter_next(model, &new_iter)) {
+ gtk_tree_selection_select_iter(sel, &new_iter);
+ } else { /* "gtk_tree_model_iter_prev" */
+ GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
+ if (gtk_tree_path_prev(path)) {
+ gtk_tree_model_get_iter(model, &new_iter, path);
+ gtk_tree_selection_select_iter(sel, &new_iter);
+ }
+ gtk_tree_path_free(path);
+ }
+
+ /* Remove the row from the list store. */
+ /* We prevent triggering call to column_row_deleted_cb() since */
+ /* the entry has already been removed from prefs.col_list and */
+ /* since rebuilding the list is not needed because the order */
+ /* of the list hasn't changed. */
+
+ g_signal_handler_block(model, column_row_deleted_handler_id);
+
+ /* list_store_remove below will trigger a call to */
+ /* column_list_select_cb() only when deleting the last entry in */
+ /* the column list. */
+ /* (This is because the selection in this case changes to */
+ /* "nothing selected" when the last row is removed. */
+
+ gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
+
+ g_signal_handler_unblock (model, column_row_deleted_handler_id);
+
+ cfile.cinfo.columns_changed = TRUE;
+ }
+}
+
+
+static gboolean
+column_title_changed_cb(GtkCellRendererText *cell _U_, const gchar *str_path, const gchar *new_title, gpointer data) {
+ fmt_data *cfmt;
+ GList *clp;
+ GtkTreeModel *model = (GtkTreeModel *)data;
+ GtkTreePath *path = gtk_tree_path_new_from_string (str_path);
+ GtkTreeIter iter;
+
+ gtk_tree_model_get_iter(model, &iter, path);
+
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, TITLE_COLUMN, new_title, -1);
+
+ gtk_tree_model_get(model, &iter, DATA_COLUMN, &clp, -1);
+ if (clp) {
+ cfmt = (fmt_data *) clp->data;
+ g_free(cfmt->title);
+ cfmt->title = g_strdup(new_title);
+ }
+
+ gtk_tree_path_free (path);
+ cfile.cinfo.columns_changed = TRUE;
+ return TRUE;
+}
+
+/*
+ * column list row selection changed.
+ * Set the "Properties" widgets to match the currently selected column row item.
+ */
+
+static void
+column_list_select_cb(GtkTreeSelection *sel, gpointer data _U_)
+{
+ fmt_data *cfmt;
+ GList *clp;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ /* if something was selected */
+ if (gtk_tree_selection_get_selected(sel, &model, &iter))
+ {
+ gtk_tree_model_get(model, &iter, DATA_COLUMN, &clp, -1);
+ g_assert(clp != NULL);
+ cfmt = (fmt_data *) clp->data;
+
+ g_signal_handler_block (fmt_cmb, column_menu_changed_handler_id);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(fmt_cmb), cfmt->fmt);
+ g_signal_handler_unblock(fmt_cmb, column_menu_changed_handler_id);
+
+ g_signal_handler_block (field_te, column_field_changed_handler_id);
+ g_signal_handler_block (occurrence_te, column_occurrence_changed_handler_id);
+ if (cfmt->fmt == COL_CUSTOM) {
+ gtk_entry_set_text(GTK_ENTRY(field_te), cfmt->custom_field);
+ gtk_widget_set_sensitive(field_lb, TRUE);
+ gtk_widget_set_sensitive(field_te, TRUE);
+ g_snprintf(custom_occurrence_str, sizeof(custom_occurrence_str), "%d", cfmt->custom_occurrence);
+ gtk_entry_set_text(GTK_ENTRY(occurrence_te), custom_occurrence_str);
+ gtk_widget_set_sensitive(occurrence_lb, TRUE);
+ gtk_widget_set_sensitive(occurrence_te, TRUE);
+ } else {
+ gtk_editable_delete_text(GTK_EDITABLE(field_te), 0, -1);
+ gtk_widget_set_sensitive(field_lb, FALSE);
+ gtk_widget_set_sensitive(field_te, FALSE);
+ gtk_editable_delete_text(GTK_EDITABLE(occurrence_te), 0, -1);
+ gtk_widget_set_sensitive(occurrence_lb, FALSE);
+ gtk_widget_set_sensitive(occurrence_te, FALSE);
+ }
+ g_signal_handler_unblock(occurrence_te, column_occurrence_changed_handler_id);
+ g_signal_handler_unblock(field_te, column_field_changed_handler_id);
+
+ gtk_widget_set_sensitive(remove_bt, TRUE);
+ gtk_widget_set_sensitive(fmt_cmb, TRUE);
+ }
+ else
+ {
+ gtk_editable_delete_text(GTK_EDITABLE(field_te), 0, -1);
+ gtk_editable_delete_text(GTK_EDITABLE(occurrence_te), 0, -1);
+
+ gtk_widget_set_sensitive(remove_bt, FALSE);
+ gtk_widget_set_sensitive(field_te, FALSE);
+ gtk_widget_set_sensitive(occurrence_te, FALSE);
+ gtk_widget_set_sensitive(fmt_cmb, FALSE);
+ }
+}
+
+
+/*
+ * The user selected a new entry in the format combo-box;
+ * Note: column_menu_changed_cb is expected to be called only
+ * when the user changes the format combo-box.
+ * Action:
+ * If no selection active in the column list:
+ * Hide 'field"; desensitize buttons.
+ * XXX: Can this happen ?
+ * If a column list selection is active:
+ * 1. Update the display of "field" as req'd (depending upon
+ * whether the active combo-box entry is the "custom"
+ * format type).
+ * 2. Update the column_list and prefs.col_formats.
+ * Set columns_changed = TRUE.
+ */
+
+static void
+column_menu_changed_cb(GtkWidget *w, gpointer data) {
+ GtkTreeView *column_l = GTK_TREE_VIEW(data);
+ fmt_data *cfmt;
+ gint cur_cb_fmt;
+ GList *clp;
+ gchar *fmt;
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ sel = gtk_tree_view_get_selection(column_l);
+ if (! (gtk_tree_selection_get_selected(sel, &model, &iter)))
+ return; /* no column list selection [Can this happen ?]: ignore callback */
+
+ cur_cb_fmt = gtk_combo_box_get_active(GTK_COMBO_BOX(w));
+ gtk_tree_model_get(model, &iter, DATA_COLUMN, &clp, -1);
+ cfmt = (fmt_data *) clp->data;
+
+ g_assert(cur_cb_fmt != cfmt->fmt);
+
+ /* The User has selected a new format in the combo-box */
+ /* (IE: combo-box format != current selected row format) */
+ /* Update field widgets, list_store, column format array */
+ /* entry as appropriate. */
+ g_signal_handler_block (field_te, column_field_changed_handler_id);
+ g_signal_handler_block (occurrence_te, column_occurrence_changed_handler_id);
+ if (cfmt->fmt == COL_CUSTOM) {
+ /* Changing from custom to non-custom */
+ gtk_editable_delete_text(GTK_EDITABLE(field_te), 0, -1);
+ gtk_editable_delete_text(GTK_EDITABLE(occurrence_te), 0, -1);
+ fmt = g_strdup_printf("%s", col_format_desc(cur_cb_fmt));
+ gtk_widget_set_sensitive(field_lb, FALSE);
+ gtk_widget_set_sensitive(field_te, FALSE);
+ gtk_widget_set_sensitive(occurrence_lb, FALSE);
+ gtk_widget_set_sensitive(occurrence_te, FALSE);
+
+ } else if (cur_cb_fmt == COL_CUSTOM) {
+ /* Changing from non-custom to custom */
+ if (cfmt->custom_field == NULL)
+ cfmt->custom_field = g_strdup("");
+ /* The following doesn't trigger a call to menu_field_changed_cb() */
+ gtk_entry_set_text(GTK_ENTRY(field_te), cfmt->custom_field);
+ g_snprintf(custom_occurrence_str, sizeof(custom_occurrence_str), "%d", cfmt->custom_occurrence);
+ gtk_entry_set_text(GTK_ENTRY(occurrence_te), custom_occurrence_str);
+
+ if (cfmt->custom_occurrence) {
+ fmt = g_strdup_printf("%s (%s#%d)", col_format_desc(cur_cb_fmt), cfmt->custom_field, cfmt->custom_occurrence);
+ } else {
+ fmt = g_strdup_printf("%s (%s)", col_format_desc(cur_cb_fmt), cfmt->custom_field);
+ }
+ gtk_widget_set_sensitive(field_lb, TRUE);
+ gtk_widget_set_sensitive(field_te, TRUE);
+ gtk_widget_set_sensitive(occurrence_lb, TRUE);
+ gtk_widget_set_sensitive(occurrence_te, TRUE);
+
+ } else {
+ /* Changing from non-custom to non-custom */
+ fmt = g_strdup_printf("%s", col_format_desc(cur_cb_fmt));
+ }
+ g_signal_handler_unblock(occurrence_te, column_occurrence_changed_handler_id);
+ g_signal_handler_unblock(field_te, column_field_changed_handler_id);
+
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, FORMAT_COLUMN, fmt, -1);
+ g_free(fmt);
+ cfmt->fmt = cur_cb_fmt;
+ cfile.cinfo.columns_changed = TRUE;
+}
+
+
+/*
+ * The user changed the custom field entry box or
+ * the field entry box has been updated because a new
+ * column row with custom format has been selected.
+ * If the current field entry matches that of the current
+ * column row, this is just an update because a new
+ * column row has been selected. Do nothing.
+ * If the two are different, then update the column row & etc.
+ */
+static void
+column_field_changed_cb(GtkEditable *te, gpointer data) {
+ fmt_data *cfmt;
+ GList *clp;
+ gchar *field, *fmt;
+ GtkTreeView *tree = (GtkTreeView *)data;
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ sel = gtk_tree_view_get_selection(tree);
+ if ( ! (gtk_tree_selection_get_selected(sel, &model, &iter))) {
+ return;
+ }
+
+ field = gtk_editable_get_chars(te, 0, -1);
+ gtk_tree_model_get(model, &iter, DATA_COLUMN, &clp, -1);
+ cfmt = (fmt_data *) clp->data;
+ if (strcmp(cfmt->custom_field, field) == 0) {
+ return; /* no action req'd */
+ }
+
+ /* The user has entered a new value in the field entry box: make the req'd changes */
+ if (cfmt->custom_occurrence) {
+ fmt = g_strdup_printf("%s (%s#%d)", col_format_desc(cfmt->fmt), field, cfmt->custom_occurrence);
+ } else {
+ fmt = g_strdup_printf("%s (%s)", col_format_desc(cfmt->fmt), field);
+ }
+
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, FORMAT_COLUMN, fmt, -1);
+ g_free(fmt);
+ g_free(cfmt->custom_field);
+ cfmt->custom_field = field;
+ cfile.cinfo.columns_changed = TRUE;
+}
+
+
+/*
+ * The user changed the custom field occurrence entry box or
+ * the field occurrece entry box has been updated because a new
+ * column row with custom format has been selected.
+ * If the current field entry matches that of the current
+ * column row, this is just an update because a new
+ * column row has been selected. Do nothing.
+ * If the two are different, then update the column row & etc.
+ */
+static void
+column_occurrence_changed_cb(GtkEditable *te, gpointer data) {
+ fmt_data *cfmt;
+ gint occurrence;
+ GList *clp;
+ gchar *fmt;
+ GtkTreeView *tree = (GtkTreeView *)data;
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ sel = gtk_tree_view_get_selection(tree);
+ if ( ! (gtk_tree_selection_get_selected(sel, &model, &iter))) {
+ return;
+ }
+
+ occurrence = (gint)strtol(gtk_editable_get_chars(te, 0, -1), NULL, 10);
+ gtk_tree_model_get(model, &iter, DATA_COLUMN, &clp, -1);
+ cfmt = (fmt_data *) clp->data;
+ if (cfmt->custom_occurrence == occurrence) {
+ return; /* no action req'd */
+ }
+
+ /* The user has entered a new value in the field occurrence entry box: make the req'd changes */
+ if (occurrence) {
+ fmt = g_strdup_printf("%s (%s#%d)", col_format_desc(cfmt->fmt), cfmt->custom_field, occurrence);
+ } else {
+ fmt = g_strdup_printf("%s (%s)", col_format_desc(cfmt->fmt), cfmt->custom_field);
+ }
+
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, FORMAT_COLUMN, fmt, -1);
+ g_free(fmt);
+ cfmt->custom_occurrence = occurrence;
+ cfile.cinfo.columns_changed = TRUE;
+}
+
+
+/*
+ * Callback for the "row-deleted" signal emitted when a list item is dragged.
+ * http://library.gnome.org/devel/gtk/stable/GtkTreeModel.html#GtkTreeModel-rows-reordered
+ * says that DND deletes, THEN inserts the row.
+ *
+ * XXX: For the record: For Gtk+ 2.16.0 testing shows the actual sequence for drag-and-drop to be as follows:
+ * 1. Insert a new, empty row at the destination;
+ * 2. Emit a "row-inserted" signal on the model; invoke any row-inserted callbacks & etc;
+ * 3. Copy the source row data to the new (empty) destination row;
+ * 4. Delete the source row;
+ * 5. Emit a "row-deleted" signal; invoke any row-deleted callbacks & etc.
+ *
+ * The code below (invoked as a consequence of a "row-deleted" signal) rebuilds
+ * prefs.col_list by iterating over the (re-ordered) tree model.
+ */
+static void
+column_dnd_row_deleted_cb(GtkTreeModel *model, GtkTreePath *path _U_, gpointer data _U_) {
+ GtkTreeIter iter;
+ GList *clp, *new_col_list = NULL;
+ gboolean items_left;
+
+ /*
+ * XXX - This rebuilds prefs.col_list based on the current model. We
+ * might just want to do this when the prefs are applied
+ */
+ for (items_left = gtk_tree_model_get_iter_first (model, &iter);
+ items_left;
+ items_left = gtk_tree_model_iter_next (model, &iter)) {
+
+ gtk_tree_model_get(model, &iter, DATA_COLUMN, &clp, -1);
+ if (clp) {
+ prefs.col_list = g_list_remove_link(prefs.col_list, clp);
+ new_col_list = g_list_concat(new_col_list, clp);
+ }
+ }
+ if (prefs.col_list) {
+ g_warning("column_dnd_row_deleted_cb: prefs.col_list has %d leftover data",
+ g_list_length(prefs.col_list));
+ g_list_free(prefs.col_list);
+ }
+
+ prefs.col_list = new_col_list;
+ cfile.cinfo.columns_changed = TRUE;
+}
+
+
+void
+column_prefs_fetch(GtkWidget *w _U_) {
+}
+
+
+void
+column_prefs_apply(GtkWidget *w _U_)
+{
+ /* Redraw the packet list if the columns were changed */
+ if(cfile.cinfo.columns_changed) {
+ new_packet_list_recreate();
+ cfile.cinfo.columns_changed = FALSE; /* Reset value */
+ }
+}
+
+
+void
+column_prefs_destroy(GtkWidget *w _U_) {
+}
diff --git a/ui/gtk/prefs_column.h b/ui/gtk/prefs_column.h
new file mode 100644
index 0000000000..5e8cc0cb32
--- /dev/null
+++ b/ui/gtk/prefs_column.h
@@ -0,0 +1,74 @@
+/* prefs_column.h
+ * Definitions for column preferences window
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __COLUMN_PREFS_H__
+#define __COLUMN_PREFS_H__
+
+/** @file
+ * "User Interface: Columns" preferences page.
+ * @ingroup prefs_group
+ */
+
+/** Build a column preferences page.
+ *
+ * @return the new column preferences page
+ */
+GtkWidget *column_prefs_show(GtkWidget *prefs_window);
+
+/** Fetch preference values from page.
+ *
+ * @param widget widget from column_prefs_show()
+ */
+void column_prefs_fetch(GtkWidget *widget);
+
+/** Apply preference values from page.
+ *
+ * @param widget widget from column_prefs_show()
+ */
+void column_prefs_apply(GtkWidget *widget);
+
+/** Destroy preference values from page.
+ *
+ * @param widget widget from column_prefs_show()
+ */
+void column_prefs_destroy(GtkWidget *widget);
+
+/** Add a custom column.
+ *
+ * @param fmt column format
+ * @param title column title
+ * @param custom_field column custom field
+ */
+void column_prefs_add_custom(gint fmt, const gchar *title,
+ const gchar *custom_field,
+ gint custom_occurrence);
+
+/** Remove a column.
+ *
+ * @param col column id
+ */
+void column_prefs_remove(gint col);
+
+#endif
diff --git a/ui/gtk/prefs_dlg.c b/ui/gtk/prefs_dlg.c
new file mode 100644
index 0000000000..e519456b57
--- /dev/null
+++ b/ui/gtk/prefs_dlg.c
@@ -0,0 +1,1834 @@
+/* prefs_dlg.c
+ * Routines for handling preferences
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <string.h>
+
+#include <epan/filesystem.h>
+#include <epan/packet.h>
+#include <epan/prefs.h>
+#include <epan/strutil.h>
+#include <epan/prefs-int.h>
+
+#include "../file.h"
+#include "../print.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/main.h"
+#include "ui/gtk/prefs_column.h"
+#include "ui/gtk/prefs_dlg.h"
+#include "ui/gtk/prefs_filter_expressions.h"
+#include "ui/gtk/prefs_print.h"
+#include "ui/gtk/prefs_stream.h"
+#include "ui/gtk/prefs_gui.h"
+#include "ui/gtk/prefs_layout.h"
+#include "ui/gtk/prefs_capture.h"
+#include "ui/gtk/prefs_nameres.h"
+#include "ui/gtk/prefs_taps.h"
+#include "ui/gtk/prefs_protocols.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/uat_gui.h"
+#include "ui/gtk/old-gtk-compat.h"
+
+#ifdef HAVE_LIBPCAP
+#ifdef _WIN32
+#include "capture-wpcap.h"
+#endif /* _WIN32 */
+#ifdef HAVE_AIRPCAP
+#include "airpcap.h"
+#include "airpcap_loader.h"
+#include "airpcap_gui_utils.h"
+#endif
+#endif
+
+static void prefs_main_ok_cb(GtkWidget *, gpointer);
+static void prefs_main_apply_cb(GtkWidget *, gpointer);
+static void prefs_main_save_cb(GtkWidget *, gpointer);
+static void prefs_main_cancel_cb(GtkWidget *, gpointer);
+static gboolean prefs_main_delete_event_cb(GtkWidget *, GdkEvent *, gpointer);
+static void prefs_main_destroy_cb(GtkWidget *, gpointer);
+static void prefs_tree_select_cb(GtkTreeSelection *, gpointer);
+
+
+#define E_PREFSW_SCROLLW_KEY "prefsw_scrollw"
+#define E_PREFSW_TREE_KEY "prefsw_tree"
+#define E_PREFSW_NOTEBOOK_KEY "prefsw_notebook"
+#define E_PREFSW_SAVE_BT_KEY "prefsw_save_bt"
+#define E_PAGE_ITER_KEY "page_iter"
+#define E_PAGE_MODULE_KEY "page_module"
+#define E_PAGESW_FRAME_KEY "pagesw_frame"
+
+#define E_GUI_PAGE_KEY "gui_options_page"
+#define E_GUI_LAYOUT_PAGE_KEY "gui_layout_page"
+#define E_GUI_COLUMN_PAGE_KEY "gui_column_options_page"
+#define E_GUI_FONT_PAGE_KEY "gui_font_options_page"
+#define E_GUI_COLORS_PAGE_KEY "gui_colors_options_page"
+#define E_CAPTURE_PAGE_KEY "capture_options_page"
+#define E_PRINT_PAGE_KEY "printer_options_page"
+#define E_NAMERES_PAGE_KEY "nameres_options_page"
+#define E_TAPS_PAGE_KEY "taps_options_page"
+#define E_PROTOCOLS_PAGE_KEY "protocols_options_page"
+#define E_FILTER_EXPRESSIONS_PAGE_KEY "filter_expressions_page"
+
+/*
+ * Keep a static pointer to the current "Preferences" window, if any, so that
+ * if somebody tries to do "Edit:Preferences" while there's already a
+ * "Preferences" window up, we just pop up the existing one, rather than
+ * creating a new one.
+ */
+static GtkWidget *prefs_w;
+
+/*
+ * Save the value of the preferences as of when the preferences dialog
+ * box was first popped up, so we can revert to those values if the
+ * user selects "Cancel".
+ */
+static e_prefs saved_prefs;
+
+struct ct_struct {
+ GtkWidget *main_vb;
+ GtkWidget *notebook;
+ GtkWidget *tree;
+ GtkTreeIter iter;
+ gint page;
+ gboolean is_protocol;
+};
+
+static gint protocols_page = 0;
+
+static guint
+pref_exists(pref_t *pref _U_, gpointer user_data _U_)
+{
+ return 1;
+}
+
+/* show a single preference on the GtkTable of a preference page */
+static guint
+pref_show(pref_t *pref, gpointer user_data)
+{
+ GtkWidget *main_tb = user_data;
+ const char *title;
+ char *label_string;
+ size_t label_len;
+ char uint_str[10+1];
+
+ /* Give this preference a label which is its title, followed by a colon,
+ and left-align it. */
+ title = pref->title;
+ label_len = strlen(title) + 2;
+ label_string = g_malloc(label_len);
+ g_strlcpy(label_string, title, label_len);
+
+ /*
+ * Sometimes we don't want to append a ':' after a static text string...
+ * If it is needed, we will specify it in the string itself.
+ */
+ if(pref->type != PREF_STATIC_TEXT)
+ g_strlcat(label_string, ":", label_len);
+
+ /* Save the current value of the preference, so that we can revert it if
+ the user does "Apply" and then "Cancel", and create the control for
+ editing the preference. */
+ switch (pref->type) {
+
+ case PREF_UINT:
+ pref->saved_val.uint = *pref->varp.uint;
+
+ /* XXX - there are no uint spinbuttons, so we can't use a spinbutton.
+ Even more annoyingly, even if there were, GLib doesn't define
+ G_MAXUINT - but I think ANSI C may define UINT_MAX, so we could
+ use that. */
+ switch (pref->info.base) {
+
+ case 10:
+ g_snprintf(uint_str, sizeof(uint_str), "%u", pref->saved_val.uint);
+ break;
+
+ case 8:
+ g_snprintf(uint_str, sizeof(uint_str), "%o", pref->saved_val.uint);
+ break;
+
+ case 16:
+ g_snprintf(uint_str, sizeof(uint_str), "%x", pref->saved_val.uint);
+ break;
+ }
+ pref->control = create_preference_entry(main_tb, pref->ordinal,
+ label_string, pref->description,
+ uint_str);
+ break;
+
+ case PREF_BOOL:
+ pref->saved_val.boolval = *pref->varp.boolp;
+ pref->control = create_preference_check_button(main_tb, pref->ordinal,
+ label_string, pref->description,
+ pref->saved_val.boolval);
+ break;
+
+ case PREF_ENUM:
+ pref->saved_val.enumval = *pref->varp.enump;
+ if (pref->info.enum_info.radio_buttons) {
+ /* Show it as radio buttons. */
+ pref->control = create_preference_radio_buttons(main_tb, pref->ordinal,
+ label_string, pref->description,
+ pref->info.enum_info.enumvals,
+ pref->saved_val.enumval);
+ } else {
+ /* Show it as an option menu. */
+ pref->control = create_preference_option_menu(main_tb, pref->ordinal,
+ label_string, pref->description,
+ pref->info.enum_info.enumvals,
+ pref->saved_val.enumval);
+ }
+ break;
+
+ case PREF_STRING:
+ g_free(pref->saved_val.string);
+ pref->saved_val.string = g_strdup(*pref->varp.string);
+ pref->control = create_preference_entry(main_tb, pref->ordinal,
+ label_string, pref->description,
+ pref->saved_val.string);
+ break;
+
+ case PREF_RANGE:
+ {
+ char *range_str_p;
+
+ g_free(pref->saved_val.range);
+ pref->saved_val.range = range_copy(*pref->varp.range);
+ range_str_p = range_convert_range(*pref->varp.range);
+ pref->control = create_preference_entry(main_tb, pref->ordinal,
+ label_string, pref->description,
+ range_str_p);
+ break;
+ }
+
+ case PREF_STATIC_TEXT:
+ {
+ pref->control = create_preference_static_text(main_tb, pref->ordinal,
+ label_string, pref->description);
+ break;
+ }
+
+ case PREF_UAT:
+ {
+ pref->control = create_preference_uat(main_tb, pref->ordinal,
+ label_string, pref->description,
+ pref->varp.uat);
+ break;
+ }
+
+ case PREF_OBSOLETE:
+ g_assert_not_reached();
+ break;
+ }
+ g_free(label_string);
+
+ return 0;
+}
+
+#define MAX_TREE_NODE_NAME_LEN 64
+/* show prefs page for each registered module (protocol) */
+static guint
+module_prefs_show(module_t *module, gpointer user_data)
+{
+ struct ct_struct *cts = user_data;
+ struct ct_struct child_cts;
+ GtkWidget *main_vb, *main_tb, *frame, *main_sw;
+ gchar label_str[MAX_TREE_NODE_NAME_LEN];
+ GtkTreeStore *model;
+ GtkTreeIter iter;
+
+ /*
+ * Is this module a subtree, with modules underneath it?
+ */
+ if (!prefs_module_has_submodules(module)) {
+ /*
+ * No.
+ * Does it have any preferences (other than possibly obsolete ones)?
+ */
+ if (prefs_pref_foreach(module, pref_exists, NULL) == 0) {
+ /*
+ * No. Don't put the module into the preferences window.
+ * XXX - we should do the same for subtrees; if a subtree has
+ * nothing under it that will be displayed, don't put it into
+ * the window.
+ */
+ return 0;
+ }
+ }
+
+ /*
+ * Add this module to the tree.
+ */
+ g_strlcpy(label_str, module->title, MAX_TREE_NODE_NAME_LEN);
+ model = GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(cts->tree)));
+ if (prefs_module_has_submodules(module) && !cts->iter.stamp)
+ gtk_tree_store_append(model, &iter, NULL);
+ else
+ gtk_tree_store_append(model, &iter, &cts->iter);
+
+ /*
+ * Is this a subtree?
+ */
+ if (prefs_module_has_submodules(module)) {
+ /*
+ * Yes.
+ */
+
+ gtk_tree_store_set(model, &iter, 0, label_str, 1, -1, -1);
+
+ /*
+ * Walk the subtree and attach stuff to it.
+ */
+ child_cts = *cts;
+ child_cts.iter = iter;
+ if (module == protocols_module)
+ child_cts.is_protocol = TRUE;
+ prefs_modules_foreach_submodules(module, module_prefs_show, &child_cts);
+
+ /* keep the page count right */
+ cts->page = child_cts.page;
+
+ }
+ if(module->prefs) {
+ /*
+ * Has preferences. Create a notebook page for it.
+ */
+
+ /* Scrolled window */
+ main_sw = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(main_sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+
+ /* Frame */
+ frame = gtk_frame_new(module->description);
+ gtk_container_set_border_width(GTK_CONTAINER(frame), 5);
+ gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(main_sw), frame);
+ g_object_set_data(G_OBJECT(main_sw), E_PAGESW_FRAME_KEY, frame);
+
+ /* Main vertical box */
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(frame), main_vb);
+
+ /* Main table */
+ main_tb = gtk_table_new(module->numprefs, 2, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
+
+ /* Add items for each of the preferences */
+ prefs_pref_foreach(module, pref_show, main_tb);
+
+ /* Associate this module with the page's frame. */
+ g_object_set_data(G_OBJECT(frame), E_PAGE_MODULE_KEY, module);
+
+ /* Add the page to the notebook */
+ gtk_notebook_append_page(GTK_NOTEBOOK(cts->notebook), main_sw, NULL);
+
+ /* Attach the page to the tree item */
+ gtk_tree_store_set(model, &iter, 0, label_str, 1, cts->page, -1);
+ g_object_set_data(G_OBJECT(frame), E_PAGE_ITER_KEY, gtk_tree_iter_copy(&iter));
+
+ cts->page++;
+
+ /* Show 'em what we got */
+ gtk_widget_show_all(main_sw);
+ } else {
+ /* show the protocols page */
+
+ gtk_tree_store_set(model, &iter, 0, label_str, 1, protocols_page, -1);
+
+ }
+
+ return 0;
+}
+
+
+#define prefs_tree_iter GtkTreeIter
+
+/* add a page to the tree */
+static prefs_tree_iter
+prefs_tree_page_add(const gchar *title, gint page_nr,
+ gpointer store, prefs_tree_iter *parent_iter)
+{
+ prefs_tree_iter iter;
+
+ gtk_tree_store_append(store, &iter, parent_iter);
+ gtk_tree_store_set(store, &iter, 0, title, 1, page_nr, -1);
+ return iter;
+}
+
+/* add a page to the notebook */
+static GtkWidget *
+prefs_nb_page_add(GtkWidget *notebook, const gchar *title, GtkWidget *page, const char *page_key)
+{
+ GtkWidget *frame;
+
+ frame = gtk_frame_new(title);
+ gtk_widget_show(frame);
+ if(page) {
+ gtk_container_add(GTK_CONTAINER(frame), page);
+ g_object_set_data(G_OBJECT(prefs_w), page_key, page);
+ }
+ gtk_notebook_append_page (GTK_NOTEBOOK(notebook), frame, NULL);
+
+ return frame;
+}
+
+
+/* show the dialog */
+void
+prefs_cb(GtkWidget *w, gpointer dummy)
+{
+ prefs_page_cb (w, dummy, PREFS_PAGE_USER_INTERFACE);
+}
+
+void
+prefs_page_cb(GtkWidget *w _U_, gpointer dummy _U_, PREFS_PAGE_E prefs_page)
+{
+ GtkWidget *top_hb, *bbox, *prefs_nb, *ct_sb,
+ *ok_bt, *apply_bt, *save_bt, *cancel_bt, *help_bt;
+ GtkWidget *gui_font_pg;
+ gchar label_str[MAX_TREE_NODE_NAME_LEN];
+ struct ct_struct cts;
+ GtkTreeStore *store;
+ GtkTreeSelection *selection;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ gint col_offset;
+ prefs_tree_iter gui_iter, layout_iter, columns_iter;
+ gint layout_page, columns_page;
+
+
+ if (prefs_w != NULL) {
+ /* There's already a "Preferences" dialog box; reactivate it. */
+ reactivate_window(prefs_w);
+ return;
+ }
+
+ /* Save the current preferences, so we can revert to those values
+ if the user presses "Cancel". */
+ copy_prefs(&saved_prefs, &prefs);
+
+ prefs_w = dlg_conf_window_new("Wireshark: Preferences");
+
+ /*
+ * Unfortunately, we can't arrange that a GtkTable widget wrap an event box
+ * around a table row, so the spacing between the preference item's label
+ * and its control widgets is inactive and the tooltip doesn't pop up when
+ * the mouse is over it.
+ */
+
+ /* Container for each row of widgets */
+ cts.main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(cts.main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(prefs_w), cts.main_vb);
+ gtk_widget_show(cts.main_vb);
+
+ /* Top row: Preferences tree and notebook */
+ top_hb = gtk_hbox_new(FALSE, 10);
+ gtk_container_add(GTK_CONTAINER(cts.main_vb), top_hb);
+ gtk_widget_show(top_hb);
+
+ /* scrolled window on the left for the categories tree */
+ ct_sb = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(ct_sb),
+ GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ct_sb),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_container_add(GTK_CONTAINER(top_hb), ct_sb);
+ gtk_widget_show(ct_sb);
+ g_object_set_data(G_OBJECT(prefs_w), E_PREFSW_SCROLLW_KEY, ct_sb);
+
+ /* categories tree */
+ store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_INT);
+ cts.tree = tree_view_new(GTK_TREE_MODEL(store));
+ cts.iter.stamp = 0; /* mark this as the toplevel */
+ g_object_set_data(G_OBJECT(prefs_w), E_PREFSW_TREE_KEY, cts.tree);
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(cts.tree), FALSE);
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(cts.tree));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+ renderer = gtk_cell_renderer_text_new();
+ col_offset = gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(cts.tree),
+ -1, "Name", renderer,
+ "text", 0, NULL);
+ column = gtk_tree_view_get_column(GTK_TREE_VIEW(cts.tree),
+ col_offset - 1);
+ gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
+ GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ g_signal_connect(selection, "changed", G_CALLBACK(prefs_tree_select_cb), NULL);
+ gtk_container_add(GTK_CONTAINER(ct_sb), cts.tree);
+ gtk_widget_show(cts.tree);
+
+ /* A notebook widget without tabs is used to flip between prefs */
+ prefs_nb = gtk_notebook_new();
+ g_object_set_data(G_OBJECT(prefs_w), E_PREFSW_NOTEBOOK_KEY, prefs_nb);
+ gtk_notebook_set_show_tabs(GTK_NOTEBOOK(prefs_nb), FALSE);
+ gtk_notebook_set_show_border(GTK_NOTEBOOK(prefs_nb), FALSE);
+ gtk_container_add(GTK_CONTAINER(top_hb), prefs_nb);
+ gtk_widget_show(prefs_nb);
+
+ cts.page = 0;
+
+ /* Preferences common for all protocols */
+ g_strlcpy(label_str, "Protocols", MAX_TREE_NODE_NAME_LEN);
+ prefs_nb_page_add(prefs_nb, label_str, protocols_prefs_show(), E_PROTOCOLS_PAGE_KEY);
+ protocols_page = cts.page++;
+
+ /* GUI prefs */
+ g_strlcpy(label_str, "User Interface", MAX_TREE_NODE_NAME_LEN);
+ prefs_nb_page_add(prefs_nb, label_str, gui_prefs_show(), E_GUI_PAGE_KEY);
+ gui_iter = prefs_tree_page_add(label_str, cts.page, store, NULL);
+ cts.page++;
+
+ /* GUI layout prefs */
+ g_strlcpy(label_str, "Layout", MAX_TREE_NODE_NAME_LEN);
+ prefs_nb_page_add(prefs_nb, label_str, layout_prefs_show(), E_GUI_LAYOUT_PAGE_KEY);
+ layout_iter = prefs_tree_page_add(label_str, cts.page, store, &gui_iter);
+ layout_page = cts.page++;
+
+ /* GUI Column prefs */
+ g_strlcpy(label_str, "Columns", MAX_TREE_NODE_NAME_LEN);
+ prefs_nb_page_add(prefs_nb, label_str, column_prefs_show(prefs_w), E_GUI_COLUMN_PAGE_KEY);
+ columns_iter = prefs_tree_page_add(label_str, cts.page, store, &gui_iter);
+ columns_page = cts.page++;
+
+ /* GUI Font prefs */
+ g_strlcpy(label_str, "Font", MAX_TREE_NODE_NAME_LEN);
+ gui_font_pg = gui_font_prefs_show();
+ prefs_nb_page_add(prefs_nb, label_str, gui_font_pg, E_GUI_FONT_PAGE_KEY);
+ prefs_tree_page_add(label_str, cts.page, store, &gui_iter);
+ cts.page++;
+
+ gtk_container_set_border_width( GTK_CONTAINER(gui_font_pg), 5 );
+
+ /* IMPORTANT: the following gtk_font_selection_set_font_name() function will
+ only work if the widget and it's corresponding window is already shown
+ (so don't put the following into gui_font_prefs_show()) !!! */
+
+ /* We set the current font now, because setting it appears not to work
+ when run before appending the frame to the notebook. */
+
+ gtk_font_selection_set_font_name(
+ GTK_FONT_SELECTION(gui_font_pg), prefs.gui_font_name);
+
+ /* GUI Colors prefs */
+ g_strlcpy(label_str, "Colors", MAX_TREE_NODE_NAME_LEN);
+ prefs_nb_page_add(prefs_nb, label_str, stream_prefs_show(), E_GUI_COLORS_PAGE_KEY);
+ prefs_tree_page_add(label_str, cts.page, store, &gui_iter);
+ cts.page++;
+
+ /* select the main GUI page as the default page and expand it's children */
+ gtk_tree_selection_select_iter(selection, &gui_iter);
+ /* (expand will only take effect, when at least one child exists) */
+ gtk_tree_view_expand_all(GTK_TREE_VIEW(cts.tree));
+
+#ifdef HAVE_LIBPCAP
+#ifdef _WIN32
+ /* Is WPcap loaded? */
+ if (has_wpcap) {
+#endif /* _WIN32 */
+ /* capture prefs */
+ g_strlcpy(label_str, "Capture", MAX_TREE_NODE_NAME_LEN);
+ prefs_nb_page_add(prefs_nb, label_str, capture_prefs_show(), E_CAPTURE_PAGE_KEY);
+ prefs_tree_page_add(label_str, cts.page, store, NULL);
+ cts.page++;
+#ifdef _WIN32
+ }
+#endif /* _WIN32 */
+#endif /* HAVE_LIBPCAP */
+
+ /* Printing prefs */
+ g_strlcpy(label_str, "Printing", MAX_TREE_NODE_NAME_LEN);
+ prefs_nb_page_add(prefs_nb, label_str, printer_prefs_show(), E_PRINT_PAGE_KEY);
+ prefs_tree_page_add(label_str, cts.page, store, NULL);
+ cts.page++;
+
+ /* Name resolution prefs */
+ g_strlcpy(label_str, "Name Resolution", MAX_TREE_NODE_NAME_LEN);
+ prefs_nb_page_add(prefs_nb, label_str, nameres_prefs_show(), E_NAMERES_PAGE_KEY);
+ prefs_tree_page_add(label_str, cts.page, store, NULL);
+ cts.page++;
+
+ /* Saved filter prefs */
+ g_strlcpy(label_str, "Filter Expressions", MAX_TREE_NODE_NAME_LEN);
+ prefs_nb_page_add(prefs_nb, label_str, filter_expressions_prefs_show(),
+ E_FILTER_EXPRESSIONS_PAGE_KEY);
+ prefs_tree_page_add(label_str, cts.page, store, NULL);
+ cts.page++;
+
+ /* TAPS player prefs */
+ g_strlcpy(label_str, "Statistics", MAX_TREE_NODE_NAME_LEN);
+ prefs_nb_page_add(prefs_nb, label_str, stats_prefs_show(), E_TAPS_PAGE_KEY);
+ prefs_tree_page_add(label_str, cts.page, store, NULL);
+ cts.page++;
+
+ /* Registered prefs */
+ cts.notebook = prefs_nb;
+ cts.is_protocol = FALSE;
+ prefs_modules_foreach_submodules(NULL, module_prefs_show, &cts);
+
+ /* Button row: OK and alike buttons */
+ bbox = dlg_button_row_new(GTK_STOCK_HELP, GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_SAVE, GTK_STOCK_CANCEL, NULL);
+ gtk_box_pack_start(GTK_BOX(cts.main_vb), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+
+ ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(prefs_main_ok_cb), prefs_w);
+
+ apply_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_APPLY);
+ g_signal_connect(apply_bt, "clicked", G_CALLBACK(prefs_main_apply_cb), prefs_w);
+
+ save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
+ g_signal_connect(save_bt, "clicked", G_CALLBACK(prefs_main_save_cb), prefs_w);
+ g_object_set_data(G_OBJECT(prefs_w), E_PREFSW_SAVE_BT_KEY, save_bt);
+
+ cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ g_signal_connect(cancel_bt, "clicked", G_CALLBACK(prefs_main_cancel_cb), prefs_w);
+ window_set_cancel_button(prefs_w, cancel_bt, NULL);
+
+ gtk_widget_grab_default(ok_bt);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_PREFERENCES_DIALOG);
+
+ g_signal_connect(prefs_w, "delete_event", G_CALLBACK(prefs_main_delete_event_cb), NULL);
+ g_signal_connect(prefs_w, "destroy", G_CALLBACK(prefs_main_destroy_cb), prefs_w);
+
+ gtk_widget_show(prefs_w);
+
+ /* hide the Save button if the user uses implicit save */
+ if(!prefs.gui_use_pref_save) {
+ gtk_widget_hide(save_bt);
+ }
+
+ window_present(prefs_w);
+
+ switch (prefs_page) {
+ case PREFS_PAGE_LAYOUT:
+ gtk_tree_selection_select_iter(selection, &layout_iter);
+ gtk_notebook_set_current_page(g_object_get_data(G_OBJECT(prefs_w), E_PREFSW_NOTEBOOK_KEY), layout_page);
+ break;
+ case PREFS_PAGE_COLUMNS:
+ gtk_tree_selection_select_iter(selection, &columns_iter);
+ gtk_notebook_set_current_page(g_object_get_data(G_OBJECT(prefs_w), E_PREFSW_NOTEBOOK_KEY), columns_page);
+ break;
+ default:
+ /* Not implemented yet */
+ break;
+ }
+
+ g_object_unref(G_OBJECT(store));
+}
+
+static void
+set_option_label(GtkWidget *main_tb, int table_position,
+ const gchar *label_text, const gchar *tooltip_text)
+{
+ GtkWidget *label;
+ GtkWidget *event_box;
+
+ label = gtk_label_new(label_text);
+ gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
+ gtk_widget_show(label);
+
+ event_box = gtk_event_box_new();
+ gtk_event_box_set_visible_window (GTK_EVENT_BOX(event_box), FALSE);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), event_box, 0, 1,
+ table_position, table_position + 1);
+ if (tooltip_text != NULL)
+ gtk_widget_set_tooltip_text(event_box, tooltip_text);
+ gtk_container_add(GTK_CONTAINER(event_box), label);
+ gtk_widget_show(event_box);
+}
+
+GtkWidget *
+create_preference_check_button(GtkWidget *main_tb, int table_position,
+ const gchar *label_text, const gchar *tooltip_text, gboolean active)
+{
+ GtkWidget *check_box;
+
+ set_option_label(main_tb, table_position, label_text, tooltip_text);
+
+ check_box = gtk_check_button_new();
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_box), active);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), check_box, 1, 2,
+ table_position, table_position + 1);
+ if (tooltip_text != NULL)
+ gtk_widget_set_tooltip_text(check_box, tooltip_text);
+
+ return check_box;
+}
+
+GtkWidget *
+create_preference_radio_buttons(GtkWidget *main_tb, int table_position,
+ const gchar *label_text, const gchar *tooltip_text,
+ const enum_val_t *enumvals, gint current_val)
+{
+ GtkWidget *radio_button_hbox, *button = NULL;
+ GSList *rb_group;
+ int idx;
+ const enum_val_t *enum_valp;
+ GtkWidget *event_box;
+
+ set_option_label(main_tb, table_position, label_text, tooltip_text);
+
+ radio_button_hbox = gtk_hbox_new(FALSE, 0);
+ rb_group = NULL;
+ for (enum_valp = enumvals, idx = 0; enum_valp->name != NULL;
+ enum_valp++, idx++) {
+ button = gtk_radio_button_new_with_label(rb_group,
+ enum_valp->description);
+ gtk_widget_show(button);
+ rb_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
+ gtk_box_pack_start(GTK_BOX(radio_button_hbox), button, FALSE,
+ FALSE, 10);
+ if (enum_valp->value == current_val) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
+ TRUE);
+ }
+ }
+ gtk_widget_show(radio_button_hbox);
+
+ event_box = gtk_event_box_new();
+ gtk_event_box_set_visible_window (GTK_EVENT_BOX(event_box), FALSE);
+ gtk_container_add(GTK_CONTAINER(event_box), radio_button_hbox);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), event_box, 1, 2,
+ table_position, table_position+1);
+ if (tooltip_text != NULL)
+ gtk_widget_set_tooltip_text(event_box, tooltip_text);
+ gtk_widget_show(event_box);
+
+ /*
+ * It doesn't matter which of the buttons we return - we fetch
+ * the value by looking at the entire radio button group to
+ * which it belongs, and we can get that from any button.
+ */
+ return button;
+}
+
+static gint
+label_to_enum_val(GtkWidget *label, const enum_val_t *enumvals)
+{
+ const gchar *label_string;
+ int i;
+
+ /* Get the label's text, and translate it to a value.
+ We match only the descriptions, as those are what appear in
+ the option menu items or as labels for radio buttons.
+ We fail if we don't find a match, as that "can't happen". */
+ label_string = gtk_label_get_text(GTK_LABEL(label));
+
+ for (i = 0; enumvals[i].name != NULL; i++) {
+ if (g_ascii_strcasecmp(label_string, enumvals[i].description) == 0) {
+ return enumvals[i].value;
+ }
+ }
+ g_assert_not_reached();
+ return -1;
+}
+
+gint
+fetch_preference_radio_buttons_val(GtkWidget *button,
+ const enum_val_t *enumvals)
+{
+ GSList *rb_group;
+ GSList *rb_entry;
+
+ /*
+ * Go through the list of of radio buttons in the button's group,
+ * and find the first one that's active.
+ */
+ rb_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button));
+ button = NULL;
+ for (rb_entry = rb_group; rb_entry != NULL;
+ rb_entry = g_slist_next(rb_entry)) {
+ button = rb_entry->data;
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
+ break;
+ }
+
+ /* OK, now return the value corresponding to that button's label. */
+ return label_to_enum_val(gtk_bin_get_child(GTK_BIN(button)), enumvals);
+}
+
+GtkWidget *
+create_preference_option_menu(GtkWidget *main_tb, int table_position,
+ const gchar *label_text, const gchar *tooltip_text,
+ const enum_val_t *enumvals, gint current_val)
+{
+ GtkWidget *menu_box, *combo_box;
+ int menu_idx, idx;
+ const enum_val_t *enum_valp;
+ GtkWidget *event_box;
+
+ set_option_label(main_tb, table_position, label_text, tooltip_text);
+
+ /* Create a menu from the enumvals */
+ combo_box = gtk_combo_box_text_new();
+ if (tooltip_text != NULL)
+ gtk_widget_set_tooltip_text(combo_box, tooltip_text);
+ menu_idx = 0;
+ for (enum_valp = enumvals, idx = 0; enum_valp->name != NULL;
+ enum_valp++, idx++) {
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), enum_valp->description);
+ if (enum_valp->value == current_val)
+ menu_idx = idx;
+ }
+ /* Set the current value active */
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), menu_idx);
+
+ /*
+ * Put the combo box in an hbox, so that it's only as wide
+ * as the widest entry, rather than being as wide as the table
+ * space.
+ */
+ menu_box = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(menu_box), combo_box, FALSE, FALSE, 0);
+
+ event_box = gtk_event_box_new();
+ gtk_event_box_set_visible_window (GTK_EVENT_BOX(event_box), FALSE);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), event_box,
+ 1, 2, table_position, table_position + 1);
+ if (tooltip_text != NULL)
+ gtk_widget_set_tooltip_text(event_box, tooltip_text);
+ gtk_container_add(GTK_CONTAINER(event_box), menu_box);
+
+ return combo_box;
+}
+
+gint
+fetch_preference_option_menu_val(GtkWidget *combo_box, const enum_val_t *enumvals)
+{
+ /*
+ * OK, now return the value corresponding to the label for the
+ * currently active entry in the combo box.
+ */
+ int i;
+
+ i = gtk_combo_box_get_active (GTK_COMBO_BOX(combo_box));
+
+ return enumvals[i].value;
+}
+
+GtkWidget *
+create_preference_entry(GtkWidget *main_tb, int table_position,
+ const gchar *label_text, const gchar *tooltip_text, char *value)
+{
+ GtkWidget *entry;
+
+ set_option_label(main_tb, table_position, label_text, tooltip_text);
+
+ entry = gtk_entry_new();
+ if (value != NULL)
+ gtk_entry_set_text(GTK_ENTRY(entry), value);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), entry, 1, 2,
+ table_position, table_position + 1);
+ if (tooltip_text != NULL)
+ gtk_widget_set_tooltip_text(entry, tooltip_text);
+ gtk_widget_show(entry);
+
+ return entry;
+}
+
+GtkWidget *
+create_preference_static_text(GtkWidget *main_tb, int table_position,
+ const gchar *label_text, const gchar *tooltip_text)
+{
+ GtkWidget *label;
+
+ if(label_text != NULL)
+ label = gtk_label_new(label_text);
+ else
+ label = gtk_label_new("");
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 2,
+ table_position, table_position + 1);
+ if (tooltip_text != NULL)
+ gtk_widget_set_tooltip_text(label, tooltip_text);
+ gtk_widget_show(label);
+
+ return label;
+}
+
+GtkWidget *
+create_preference_uat(GtkWidget *main_tb, int table_position,
+ const gchar *label_text, const gchar *tooltip_text, void* uat)
+{
+ GtkWidget *button = NULL;
+
+ set_option_label(main_tb, table_position, label_text, tooltip_text);
+
+ button = gtk_button_new_from_stock(WIRESHARK_STOCK_EDIT);
+
+ g_signal_connect(button, "clicked", G_CALLBACK(uat_window_cb), uat);
+
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), button, 1, 2,
+ table_position, table_position+1);
+ if (tooltip_text != NULL)
+ gtk_widget_set_tooltip_text(button, tooltip_text);
+ gtk_widget_show(button);
+
+ return button;
+}
+
+
+static guint
+pref_check(pref_t *pref, gpointer user_data)
+{
+ const char *str_val;
+ char *p;
+ pref_t **badpref = user_data;
+
+ /* Fetch the value of the preference, and check whether it's valid. */
+ switch (pref->type) {
+
+ case PREF_UINT:
+ str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
+ errno = 0;
+
+ /* XXX: The following ugly hack prevents a gcc warning
+ "ignoring return value of 'strtoul', declared with attribute warn_unused_result"
+ which can occur when using certain gcc configurations (see -D_FORTIFY_SOURCE).
+ A dummy variable is not used because when using gcc 4.6 with -Wextra a
+ "set but not used [-Wunused-but-set-variable]" warning will occur.
+ (Coverity & CLang apparently do not object to this hack).
+
+ [Guy Harris comment:
+ "... perhaps either using spin buttons for numeric preferences, or otherwise making
+ it impossible to type something that's not a number into the GUI for those preferences,
+ and thus avoiding the need to check whether it's a valid number, would also be a good idea."
+ ]
+ */
+ if(strtoul(str_val, &p, pref->info.base)){}
+ if (p == str_val || *p != '\0' || errno != 0) {
+ *badpref = pref;
+ return PREFS_SET_SYNTAX_ERR; /* number was bad */
+ }
+ break;
+
+ case PREF_BOOL:
+ /* Value can't be bad. */
+ break;
+
+ case PREF_ENUM:
+ /* Value can't be bad. */
+ break;
+
+ case PREF_STRING:
+ /* Value can't be bad. */
+ break;
+
+ case PREF_RANGE:
+ str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
+
+ if (strlen(str_val) != 0) {
+ range_t *newrange;
+
+ if (range_convert_str(&newrange, str_val, pref->info.max_value) != CVT_NO_ERROR) {
+ *badpref = pref;
+ return PREFS_SET_SYNTAX_ERR; /* range was bad */
+ }
+ g_free(newrange);
+ }
+ break;
+
+ case PREF_STATIC_TEXT:
+ case PREF_UAT:
+ /* Value can't be bad. */
+ break;
+
+ case PREF_OBSOLETE:
+ g_assert_not_reached();
+ break;
+ }
+ return 0;
+}
+
+static guint
+module_prefs_check(module_t *module, gpointer user_data)
+{
+ /* For all preferences in this module, fetch its value from this
+ module's notebook page and check whether it's valid. */
+ return prefs_pref_foreach(module, pref_check, user_data);
+}
+
+static guint
+pref_fetch(pref_t *pref, gpointer user_data)
+{
+ const char *str_val;
+ char *p;
+ guint uval;
+ gboolean bval;
+ gint enumval;
+ gboolean *pref_changed_p = user_data;
+
+ /* Fetch the value of the preference, and set the appropriate variable
+ to it. */
+ switch (pref->type) {
+
+ case PREF_UINT:
+ str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
+ uval = strtoul(str_val, &p, pref->info.base);
+#if 0
+ if (p == value || *p != '\0')
+ return PREFS_SET_SYNTAX_ERR; /* number was bad */
+#endif
+ if (*pref->varp.uint != uval) {
+ *pref_changed_p = TRUE;
+ *pref->varp.uint = uval;
+ }
+ break;
+
+ case PREF_BOOL:
+ bval = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pref->control));
+ if (*pref->varp.boolp != bval) {
+ *pref_changed_p = TRUE;
+ *pref->varp.boolp = bval;
+ }
+ break;
+
+ case PREF_ENUM:
+ if (pref->info.enum_info.radio_buttons) {
+ enumval = fetch_preference_radio_buttons_val(pref->control,
+ pref->info.enum_info.enumvals);
+ } else {
+ enumval = fetch_preference_option_menu_val(pref->control,
+ pref->info.enum_info.enumvals);
+ }
+
+ if (*pref->varp.enump != enumval) {
+ *pref_changed_p = TRUE;
+ *pref->varp.enump = enumval;
+ }
+ break;
+
+ case PREF_STRING:
+ str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
+ if (strcmp(*pref->varp.string, str_val) != 0) {
+ *pref_changed_p = TRUE;
+ g_free((void *)*pref->varp.string);
+ *pref->varp.string = g_strdup(str_val);
+ }
+ break;
+
+ case PREF_RANGE:
+ {
+ range_t *newrange;
+ convert_ret_t ret;
+
+ str_val = gtk_entry_get_text(GTK_ENTRY(pref->control));
+ ret = range_convert_str(&newrange, str_val, pref->info.max_value);
+ if (ret != CVT_NO_ERROR)
+#if 0
+ return PREFS_SET_SYNTAX_ERR; /* range was bad */
+#else
+ return 0; /* XXX - should fail */
+#endif
+
+ if (!ranges_are_equal(*pref->varp.range, newrange)) {
+ *pref_changed_p = TRUE;
+ g_free(*pref->varp.range);
+ *pref->varp.range = newrange;
+ } else
+ g_free(newrange);
+
+ break;
+ }
+
+ case PREF_STATIC_TEXT:
+ case PREF_UAT:
+ break;
+
+ case PREF_OBSOLETE:
+ g_assert_not_reached();
+ break;
+ }
+ return 0;
+}
+
+static guint
+module_prefs_fetch(module_t *module, gpointer user_data)
+{
+ gboolean *must_redissect_p = user_data;
+
+ /* For all preferences in this module, fetch its value from this
+ module's notebook page. Find out whether any of them changed. */
+ module->prefs_changed = FALSE; /* assume none of them changed */
+ prefs_pref_foreach(module, pref_fetch, &module->prefs_changed);
+
+ /* If any of them changed, indicate that we must redissect and refilter
+ the current capture (if we have one), as the preference change
+ could cause packets to be dissected differently. */
+ if (module->prefs_changed)
+ *must_redissect_p = TRUE;
+
+ return 0; /* keep fetching module preferences */
+}
+
+#ifdef HAVE_AIRPCAP
+/*
+ * This function is used to apply changes and update the Wireless Toolbar
+ * whenever we apply some changes to the WEP preferences
+ */
+static void
+prefs_airpcap_update(void)
+{
+ GtkWidget *decryption_cm;
+ gint cur_active;
+ gboolean wireshark_decryption_was_enabled = FALSE;
+ gboolean airpcap_decryption_was_enabled = FALSE;
+ gboolean wireshark_decryption_is_now_enabled = FALSE;
+
+ decryption_cm = GTK_WIDGET(g_object_get_data(G_OBJECT(airpcap_tb),AIRPCAP_TOOLBAR_DECRYPTION_KEY));
+
+ if (decryption_cm == NULL) {
+ return;
+ }
+
+ cur_active = gtk_combo_box_get_active(GTK_COMBO_BOX(decryption_cm));
+
+ if (cur_active < 0) {
+ return;
+ }
+
+ switch(cur_active) {
+ /* XXX - Don't use magic numbers here. cf airpcap_dlg.c:on_decryption_mode_cb_changed() */
+ case 1: /* Wireshark */
+ wireshark_decryption_was_enabled = TRUE;
+ airpcap_decryption_was_enabled = FALSE;
+ break;
+ case 2: /* Driver */
+ wireshark_decryption_was_enabled = FALSE;
+ airpcap_decryption_was_enabled = TRUE;
+ break;
+ default:
+ wireshark_decryption_was_enabled = FALSE;
+ airpcap_decryption_was_enabled = FALSE;
+ break;
+ }
+
+ wireshark_decryption_is_now_enabled = wireshark_decryption_on();
+
+ if(wireshark_decryption_is_now_enabled && airpcap_decryption_was_enabled)
+ {
+ set_airpcap_decryption(FALSE);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(decryption_cm), 1);
+ }
+ if(wireshark_decryption_is_now_enabled && !airpcap_decryption_was_enabled)
+ {
+ set_airpcap_decryption(FALSE);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(decryption_cm), 1);
+ }
+ else if(!wireshark_decryption_is_now_enabled && wireshark_decryption_was_enabled)
+ {
+ if(airpcap_decryption_was_enabled)
+ {
+ set_airpcap_decryption(TRUE);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(decryption_cm), 2);
+ }
+ else
+ {
+ set_airpcap_decryption(FALSE);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(decryption_cm), 0);
+ }
+ }
+}
+#endif
+
+static guint
+pref_clean(pref_t *pref, gpointer user_data _U_)
+{
+ switch (pref->type) {
+
+ case PREF_UINT:
+ break;
+
+ case PREF_BOOL:
+ break;
+
+ case PREF_ENUM:
+ break;
+
+ case PREF_STRING:
+ if (pref->saved_val.string != NULL) {
+ g_free(pref->saved_val.string);
+ pref->saved_val.string = NULL;
+ }
+ break;
+
+ case PREF_RANGE:
+ if (pref->saved_val.range != NULL) {
+ g_free(pref->saved_val.range);
+ pref->saved_val.range = NULL;
+ }
+ break;
+
+ case PREF_STATIC_TEXT:
+ case PREF_UAT:
+ break;
+
+ case PREF_OBSOLETE:
+ g_assert_not_reached();
+ break;
+ }
+ return 0;
+}
+
+static guint
+module_prefs_clean(module_t *module, gpointer user_data _U_)
+{
+ /* For all preferences in this module, clean up any cruft allocated for
+ use by the GUI code. */
+ prefs_pref_foreach(module, pref_clean, NULL);
+ return 0; /* keep cleaning modules */
+}
+
+/* fetch all pref values from all pages */
+static gboolean
+prefs_main_fetch_all(GtkWidget *dlg, gboolean *must_redissect)
+{
+ pref_t *badpref;
+
+ /* First, check that the values are all valid. */
+ /* XXX - check the non-registered preferences too */
+ switch (prefs_modules_foreach(module_prefs_check, (gpointer)&badpref)) {
+
+ case PREFS_SET_SYNTAX_ERR:
+ switch (badpref->type) {
+
+ case PREF_UINT:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "The value for \"%s\" isn't a valid number.",
+ badpref->title);
+ return FALSE;
+
+ case PREF_RANGE:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "The value for \"%s\" isn't a valid range.",
+ badpref->title);
+ return FALSE;
+
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ }
+
+ /* Fetch the preferences (i.e., make sure all the values set in all of
+ the preferences panes have been copied to "prefs" and the registered
+ preferences). */
+ gui_prefs_fetch(g_object_get_data(G_OBJECT(dlg), E_GUI_PAGE_KEY));
+ layout_prefs_fetch(g_object_get_data(G_OBJECT(dlg), E_GUI_LAYOUT_PAGE_KEY));
+ column_prefs_fetch(g_object_get_data(G_OBJECT(dlg), E_GUI_COLUMN_PAGE_KEY));
+ stream_prefs_fetch(g_object_get_data(G_OBJECT(dlg), E_GUI_COLORS_PAGE_KEY));
+
+#ifdef HAVE_LIBPCAP
+#ifdef _WIN32
+ /* Is WPcap loaded? */
+ if (has_wpcap) {
+#endif /* _WIN32 */
+ capture_prefs_fetch(g_object_get_data(G_OBJECT(dlg), E_CAPTURE_PAGE_KEY));
+#ifdef _WIN32
+ }
+#endif /* _WIN32 */
+#endif /* HAVE_LIBPCAP */
+ printer_prefs_fetch(g_object_get_data(G_OBJECT(dlg), E_PRINT_PAGE_KEY));
+ nameres_prefs_fetch(g_object_get_data(G_OBJECT(dlg), E_NAMERES_PAGE_KEY));
+ filter_expressions_prefs_fetch(g_object_get_data(G_OBJECT(dlg),
+ E_FILTER_EXPRESSIONS_PAGE_KEY));
+ stats_prefs_fetch(g_object_get_data(G_OBJECT(dlg), E_TAPS_PAGE_KEY));
+ protocols_prefs_fetch(g_object_get_data(G_OBJECT(dlg), E_PROTOCOLS_PAGE_KEY));
+ prefs_modules_foreach(module_prefs_fetch, must_redissect);
+
+ return TRUE;
+}
+
+/* apply all pref values to the real world */
+static void
+prefs_main_apply_all(GtkWidget *dlg, gboolean redissect)
+{
+ GtkWidget *save_bt;
+
+ /*
+ * Apply the protocol preferences first - "gui_prefs_apply()" could
+ * cause redissection, and we have to make sure the protocol
+ * preference changes have been fully applied.
+ */
+ prefs_apply_all();
+
+ gui_prefs_apply(g_object_get_data(G_OBJECT(dlg), E_GUI_PAGE_KEY), redissect);
+ layout_prefs_apply(g_object_get_data(G_OBJECT(dlg), E_GUI_LAYOUT_PAGE_KEY));
+ column_prefs_apply(g_object_get_data(G_OBJECT(dlg), E_GUI_COLUMN_PAGE_KEY));
+ stream_prefs_apply(g_object_get_data(G_OBJECT(dlg), E_GUI_COLORS_PAGE_KEY));
+
+#ifdef HAVE_LIBPCAP
+#ifdef _WIN32
+ /* Is WPcap loaded? */
+ if (has_wpcap) {
+#endif /* _WIN32 */
+ capture_prefs_apply(g_object_get_data(G_OBJECT(dlg), E_CAPTURE_PAGE_KEY));
+#ifdef _WIN32
+ }
+#endif /* _WIN32 */
+#endif /* HAVE_LIBPCAP */
+ printer_prefs_apply(g_object_get_data(G_OBJECT(dlg), E_PRINT_PAGE_KEY));
+ nameres_prefs_apply(g_object_get_data(G_OBJECT(dlg), E_NAMERES_PAGE_KEY));
+ stats_prefs_apply(g_object_get_data(G_OBJECT(dlg), E_TAPS_PAGE_KEY));
+ protocols_prefs_apply(g_object_get_data(G_OBJECT(dlg), E_PROTOCOLS_PAGE_KEY));
+
+ /* show/hide the Save button - depending on setting */
+ save_bt = g_object_get_data(G_OBJECT(prefs_w), E_PREFSW_SAVE_BT_KEY);
+ if(prefs.gui_use_pref_save) {
+ gtk_widget_show(save_bt);
+ } else {
+ gtk_widget_hide(save_bt);
+ }
+}
+
+
+/* destroy all preferences ressources from all pages */
+static void
+prefs_main_destroy_all(GtkWidget *dlg)
+{
+ int page_num;
+ GtkWidget *frame;
+
+ for (page_num = 0;
+ (frame = gtk_notebook_get_nth_page(g_object_get_data(G_OBJECT(prefs_w), E_PREFSW_NOTEBOOK_KEY), page_num)) != NULL;
+ page_num++) {
+ if(g_object_get_data(G_OBJECT(frame), E_PAGE_ITER_KEY))
+ gtk_tree_iter_free(g_object_get_data(G_OBJECT(frame), E_PAGE_ITER_KEY));
+ }
+
+ gui_prefs_destroy(g_object_get_data(G_OBJECT(dlg), E_GUI_PAGE_KEY));
+ layout_prefs_destroy(g_object_get_data(G_OBJECT(dlg), E_GUI_LAYOUT_PAGE_KEY));
+ column_prefs_destroy(g_object_get_data(G_OBJECT(dlg), E_GUI_COLUMN_PAGE_KEY));
+ stream_prefs_destroy(g_object_get_data(G_OBJECT(dlg), E_GUI_COLORS_PAGE_KEY));
+
+#ifdef HAVE_LIBPCAP
+#ifdef _WIN32
+ /* Is WPcap loaded? */
+ if (has_wpcap) {
+#endif /* _WIN32 */
+ capture_prefs_destroy(g_object_get_data(G_OBJECT(dlg), E_CAPTURE_PAGE_KEY));
+#ifdef _WIN32
+ }
+#endif /* _WIN32 */
+#endif /* HAVE_LIBPCAP */
+ printer_prefs_destroy(g_object_get_data(G_OBJECT(dlg), E_PRINT_PAGE_KEY));
+ nameres_prefs_destroy(g_object_get_data(G_OBJECT(dlg), E_NAMERES_PAGE_KEY));
+ stats_prefs_destroy(g_object_get_data(G_OBJECT(dlg), E_TAPS_PAGE_KEY));
+
+ /* Free up the saved preferences (both for "prefs" and for registered
+ preferences). */
+ free_prefs(&saved_prefs);
+ prefs_modules_foreach(module_prefs_clean, NULL);
+ protocols_prefs_destroy(g_object_get_data(G_OBJECT(dlg), E_PROTOCOLS_PAGE_KEY));
+}
+
+
+static guint
+pref_copy(pref_t *pref, gpointer user_data _U_)
+{
+ switch (pref->type) {
+
+ case PREF_UINT:
+ pref->saved_val.uint = *pref->varp.uint;
+ break;
+
+ case PREF_BOOL:
+ pref->saved_val.boolval = *pref->varp.boolp;
+ break;
+
+ case PREF_ENUM:
+ pref->saved_val.enumval = *pref->varp.enump;
+ break;
+
+ case PREF_STRING:
+ g_free(pref->saved_val.string);
+ pref->saved_val.string = g_strdup(*pref->varp.string);
+ break;
+
+ case PREF_RANGE:
+ g_free(pref->saved_val.range);
+ pref->saved_val.range = range_copy(*pref->varp.range);
+ break;
+
+ case PREF_STATIC_TEXT:
+ case PREF_UAT:
+ break;
+
+ case PREF_OBSOLETE:
+ g_assert_not_reached();
+ break;
+ }
+ return 0;
+}
+
+static guint
+module_prefs_copy(module_t *module, gpointer user_data _U_)
+{
+ /* For all preferences in this module, (re)save current value */
+ prefs_pref_foreach(module, pref_copy, NULL);
+ return 0; /* continue making copies */
+}
+
+/* Copy prefs to saved values so we can revert to these values */
+/* if the user selects Cancel. */
+static void prefs_copy(void) {
+ free_prefs(&saved_prefs);
+ copy_prefs(&saved_prefs, &prefs);
+ prefs_modules_foreach(module_prefs_copy, NULL);
+}
+
+
+void
+prefs_main_write(void)
+{
+ int err;
+ char *pf_dir_path;
+ char *pf_path;
+
+ /* Create the directory that holds personal configuration files, if
+ necessary. */
+ if (create_persconffile_dir(&pf_dir_path) == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't create directory\n\"%s\"\nfor preferences file: %s.", pf_dir_path,
+ g_strerror(errno));
+ g_free(pf_dir_path);
+ } else {
+ /* Write the preferencs out. */
+ err = write_prefs(&pf_path);
+ if (err != 0) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't open preferences file\n\"%s\": %s.", pf_path,
+ g_strerror(err));
+ g_free(pf_path);
+ }
+ }
+
+#ifdef HAVE_AIRPCAP
+ /*
+ * Load the Wireshark decryption keys (just set) and save
+ * the changes to the adapters' registry
+ */
+ airpcap_load_decryption_keys(airpcap_if_list);
+#endif
+}
+
+
+static void
+prefs_main_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w)
+{
+ gboolean must_redissect = FALSE;
+
+ if (!prefs_main_fetch_all(parent_w, &must_redissect))
+ return; /* Errors in some preference setting - already reported */
+
+ /* if we don't have a Save button, just save the settings now */
+ if (!prefs.gui_use_pref_save) {
+ prefs_main_write();
+ }
+
+ prefs_main_apply_all(parent_w, must_redissect);
+
+ /* Fill in capture options with values from the preferences */
+ prefs_to_capture_opts();
+
+#ifdef HAVE_AIRPCAP
+ prefs_airpcap_update();
+#endif
+
+ /* Now destroy the "Preferences" dialog. */
+ window_destroy(GTK_WIDGET(parent_w));
+
+ if (must_redissect) {
+ /* Redissect all the packets, and re-evaluate the display filter. */
+ redissect_packets();
+ }
+
+}
+
+static void
+prefs_main_apply_cb(GtkWidget *apply_bt _U_, gpointer parent_w)
+{
+ gboolean must_redissect = FALSE;
+
+ if (!prefs_main_fetch_all(parent_w, &must_redissect))
+ return; /* Errors in some preference setting - already reported */
+
+ /* if we don't have a Save button, just save the settings now */
+ if (!prefs.gui_use_pref_save) {
+ prefs_main_write();
+ prefs_copy(); /* save prefs for reverting if Cancel */
+ }
+
+ prefs_main_apply_all(parent_w, must_redissect);
+
+ /* Fill in capture options with values from the preferences */
+ prefs_to_capture_opts();
+
+#ifdef HAVE_AIRPCAP
+ prefs_airpcap_update();
+#endif
+
+ if (must_redissect) {
+ /* Redissect all the packets, and re-evaluate the display filter. */
+ redissect_packets();
+ }
+}
+
+static void
+prefs_main_save_cb(GtkWidget *save_bt _U_, gpointer parent_w)
+{
+ gboolean must_redissect = FALSE;
+
+ if (!prefs_main_fetch_all(parent_w, &must_redissect))
+ return; /* Errors in some preference setting - already reported */
+
+ prefs_main_write();
+ prefs_copy(); /* save prefs for reverting if Cancel */
+
+ /* Now apply those preferences.
+ XXX - should we do this? The user didn't click "OK" or "Apply".
+ However:
+
+ 1) by saving the preferences they presumably indicate that they
+ like them;
+
+ 2) the next time they fire Wireshark up, those preferences will
+ apply;
+
+ 3) we'd have to buffer "must_redissect" so that if they do
+ "Apply" after this, we know we have to redissect;
+
+ 4) we did apply the protocol preferences, at least, in the past. */
+ prefs_main_apply_all(parent_w, must_redissect);
+
+ /* Fill in capture options with values from the preferences */
+ prefs_to_capture_opts();
+
+ if (must_redissect) {
+ /* Redissect all the packets, and re-evaluate the display filter. */
+ redissect_packets();
+ }
+}
+
+static guint
+pref_revert(pref_t *pref, gpointer user_data)
+{
+ gboolean *pref_changed_p = user_data;
+
+ /* Revert the preference to its saved value. */
+ switch (pref->type) {
+
+ case PREF_UINT:
+ if (*pref->varp.uint != pref->saved_val.uint) {
+ *pref_changed_p = TRUE;
+ *pref->varp.uint = pref->saved_val.uint;
+ }
+ break;
+
+ case PREF_BOOL:
+ if (*pref->varp.boolp != pref->saved_val.boolval) {
+ *pref_changed_p = TRUE;
+ *pref->varp.boolp = pref->saved_val.boolval;
+ }
+ break;
+
+ case PREF_ENUM:
+ if (*pref->varp.enump != pref->saved_val.enumval) {
+ *pref_changed_p = TRUE;
+ *pref->varp.enump = pref->saved_val.enumval;
+ }
+ break;
+
+ case PREF_STRING:
+ if (strcmp(*pref->varp.string, pref->saved_val.string) != 0) {
+ *pref_changed_p = TRUE;
+ g_free((void *)*pref->varp.string);
+ *pref->varp.string = g_strdup(pref->saved_val.string);
+ }
+ break;
+
+ case PREF_RANGE:
+ if (!ranges_are_equal(*pref->varp.range, pref->saved_val.range)) {
+ *pref_changed_p = TRUE;
+ g_free(*pref->varp.range);
+ *pref->varp.range = range_copy(pref->saved_val.range);
+ }
+ break;
+
+ case PREF_STATIC_TEXT:
+ case PREF_UAT:
+ break;
+
+ case PREF_OBSOLETE:
+ g_assert_not_reached();
+ break;
+ }
+ return 0;
+}
+
+static guint
+module_prefs_revert(module_t *module, gpointer user_data)
+{
+ gboolean *must_redissect_p = user_data;
+
+ /* For all preferences in this module, revert its value to the value
+ it had when we popped up the Preferences dialog. Find out whether
+ this changes any of them. */
+ module->prefs_changed = FALSE; /* assume none of them changed */
+ prefs_pref_foreach(module, pref_revert, &module->prefs_changed);
+
+ /* If any of them changed, indicate that we must redissect and refilter
+ the current capture (if we have one), as the preference change
+ could cause packets to be dissected differently. */
+ if (module->prefs_changed)
+ *must_redissect_p = TRUE;
+ return 0; /* keep processing modules */
+}
+
+/* cancel button pressed, revert prefs to saved and exit dialog */
+static void
+prefs_main_cancel_cb(GtkWidget *cancel_bt _U_, gpointer parent_w)
+{
+ gboolean must_redissect = FALSE;
+
+ /* Free up the current preferences and copy the saved preferences to the
+ current preferences. */
+ free_prefs(&prefs);
+ copy_prefs(&prefs, &saved_prefs);
+ cfile.cinfo.columns_changed = FALSE; /* [XXX: "columns_changed" should treally be stored in prefs struct ??] */
+
+ /* Now revert the registered preferences. */
+ prefs_modules_foreach(module_prefs_revert, &must_redissect);
+
+ /* Now apply the reverted-to preferences. */
+ prefs_main_apply_all(parent_w, must_redissect);
+
+ window_destroy(GTK_WIDGET(parent_w));
+
+ if (must_redissect) {
+ /* Redissect all the packets, and re-evaluate the display filter. */
+ redissect_packets();
+ }
+}
+
+/* Treat this as a cancel, by calling "prefs_main_cancel_cb()" */
+static gboolean
+prefs_main_delete_event_cb(GtkWidget *prefs_w_lcl, GdkEvent *event _U_,
+ gpointer user_data _U_)
+{
+ prefs_main_cancel_cb(NULL, prefs_w_lcl);
+ return FALSE;
+}
+
+
+/* dialog *is* already destroyed, clean up memory and such */
+static void
+prefs_main_destroy_cb(GtkWidget *win _U_, gpointer parent_w)
+{
+ prefs_main_destroy_all(parent_w);
+
+ /* Note that we no longer have a "Preferences" dialog box. */
+ prefs_w = NULL;
+}
+
+struct properties_data {
+ const char *title;
+ module_t *module;
+};
+
+static guint
+module_search_properties(module_t *module, gpointer user_data)
+{
+ struct properties_data *p = (struct properties_data *)user_data;
+
+ /* If this module has the specified title, remember it. */
+ if (strcmp(module->title, p->title) == 0) {
+ p->module = module;
+ return 1; /* stops the search */
+ }
+
+ if(prefs_module_has_submodules(module))
+ return prefs_modules_foreach_submodules(module, module_search_properties, p);
+
+ return 0;
+}
+
+static void
+tree_expand_row(GtkTreeModel *model, GtkTreeView *tree_view, GtkTreeIter *iter)
+{
+ GtkTreeIter parent;
+ GtkTreePath *path;
+
+ /* expand the parent first */
+ if(gtk_tree_model_iter_parent(model, &parent, iter))
+ tree_expand_row(model, tree_view, &parent);
+
+ path = gtk_tree_model_get_path(model, iter);
+ gtk_tree_view_expand_row(tree_view, path, FALSE);
+ /*expand_tree(tree_view, &parent, NULL, NULL);*/
+
+ gtk_tree_path_free(path);
+}
+
+/* select a node in the tree view */
+/* XXX - this is almost 100% copied from byte_view_select() in proto_draw.c,
+ * find a way to combine both to have a generic function for this */
+static void
+tree_select_node(GtkWidget *tree, prefs_tree_iter *iter)
+{
+ GtkTreeIter local_iter = *iter;
+ GtkTreeView *tree_view = GTK_TREE_VIEW(tree);
+ GtkTreeModel *model;
+ GtkTreePath *first_path;
+
+ model = gtk_tree_view_get_model(tree_view);
+
+ /* Expand our field's row */
+ first_path = gtk_tree_model_get_path(model, &local_iter);
+
+ /* expand from the top down */
+ tree_expand_row(model, tree_view, &local_iter);
+
+ /* select our field's row */
+ gtk_tree_selection_select_path(gtk_tree_view_get_selection(tree_view),
+ first_path);
+
+ /* And position the window so the selection is visible.
+ * Position the selection in the middle of the viewable
+ * pane. */
+ gtk_tree_view_scroll_to_cell(tree_view, first_path, NULL, TRUE, 0.5f, 0.0f);
+
+ gtk_tree_path_free(first_path);
+}
+
+
+/* search the corresponding protocol page of the currently selected field */
+void
+properties_cb(GtkWidget *w, gpointer dummy)
+{
+ header_field_info *hfinfo;
+ const gchar *title;
+ struct properties_data p;
+ int page_num;
+ GtkWidget *sw;
+ GtkWidget *frame;
+ module_t *page_module;
+
+ if (cfile.finfo_selected == NULL) {
+ /* There is no field selected */
+ return;
+ }
+
+ /* Find the title for the protocol for the selected field. */
+ hfinfo = cfile.finfo_selected->hfinfo;
+ if (hfinfo->parent == -1)
+ title = prefs_get_title_by_name(hfinfo->abbrev);
+ else
+ title = prefs_get_title_by_name(proto_registrar_get_abbrev(hfinfo->parent));
+ if (!title)
+ return; /* Couldn't find it. XXX - just crash? "Can't happen"? */
+
+ /* Find the module for that protocol by searching for one with that title.
+ XXX - should we just associate protocols with modules directly? */
+ p.title = title;
+ p.module = NULL;
+ prefs_modules_foreach_submodules(protocols_module, module_search_properties,
+ &p);
+ if (p.module == NULL) {
+ /* We didn't find it - that protocol probably has no preferences. */
+ return;
+ }
+
+ /* Create a preferences window, or pop up an existing one. */
+ if (prefs_w != NULL) {
+ reactivate_window(prefs_w);
+ } else {
+ prefs_cb(w, dummy);
+ }
+
+ /* Search all the pages in that window for the one with the specified
+ module. */
+ for (page_num = 0;
+ (sw = gtk_notebook_get_nth_page(g_object_get_data(G_OBJECT(prefs_w), E_PREFSW_NOTEBOOK_KEY), page_num)) != NULL;
+ page_num++) {
+ /* Get the frame from the scrollable window */
+ frame = g_object_get_data(G_OBJECT(sw), E_PAGESW_FRAME_KEY);
+ /* Get the module for this page (non-protocol prefs don't have one). */
+ if(frame) {
+ page_module = g_object_get_data(G_OBJECT(frame), E_PAGE_MODULE_KEY);
+ if (page_module != NULL) {
+ if (page_module == p.module) {
+ tree_select_node(
+ g_object_get_data(G_OBJECT(prefs_w), E_PREFSW_TREE_KEY),
+ g_object_get_data(G_OBJECT(frame), E_PAGE_ITER_KEY));
+ return;
+ }
+ }
+ }
+ }
+}
+
+/* Prefs tree selection callback. The node data has been loaded with
+ the proper notebook page to load. */
+static void
+prefs_tree_select_cb(GtkTreeSelection *sel, gpointer dummy _U_)
+{
+ gint page;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected(sel, &model, &iter))
+ {
+ gtk_tree_model_get(model, &iter, 1, &page, -1);
+ if (page >= 0)
+ gtk_notebook_set_current_page(g_object_get_data(G_OBJECT(prefs_w), E_PREFSW_NOTEBOOK_KEY), page);
+ }
+}
+
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 2
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=2 tabstop=8 expandtab:
+ * :indentSize=2:tabSize=8:noTabs=true:
+ */
diff --git a/ui/gtk/prefs_dlg.h b/ui/gtk/prefs_dlg.h
new file mode 100644
index 0000000000..ccce7784f5
--- /dev/null
+++ b/ui/gtk/prefs_dlg.h
@@ -0,0 +1,191 @@
+/* prefs_dlg.h
+ * Definitions for preference handling routines
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PREFS_DLG_H__
+#define __PREFS_DLG_H__
+
+/** @defgroup prefs_group Preferences
+ *
+ * All GUI related preferences things. Please note, that some GUI related things
+ * are saved in the recent file, which is processed in recent.h.
+ *
+ * The Preference dialog has the following page submodules:
+ @dot
+ digraph prefs_pages {
+ node [shape=record, fontname=Helvetica, fontsize=10];
+ dialog [ label="Preferences dialog" URL="\ref prefs_dlg.h"];
+ ui [ label="User Interface" URL="\ref prefs_gui.h"];
+ layout [ label="UI: Layout" URL="\ref prefs_layout.h"];
+ columns [ label="UI: Columns" URL="\ref prefs_column.h"];
+ font [ label="UI: Font" URL="\ref prefs_gui.h"];
+ colors [ label="UI: Colors" URL="\ref prefs_stream.h"];
+ capture [ label="Capture" URL="\ref prefs_capture.h"];
+ print [ label="Printing" URL="\ref prefs_print.h"];
+ nameres [ label="Name resolution" URL="\ref prefs_nameres.h"];
+ protocols [ label="Protocols" URL="\ref prefs_dlg.h"];
+ dialog -> ui [ arrowhead="open", style="solid" ];
+ dialog -> layout [ arrowhead="open", style="solid" ];
+ dialog -> columns [ arrowhead="open", style="solid" ];
+ dialog -> font [ arrowhead="open", style="solid" ];
+ dialog -> colors [ arrowhead="open", style="solid" ];
+ dialog -> capture [ arrowhead="open", style="solid" ];
+ dialog -> print [ arrowhead="open", style="solid" ];
+ dialog -> nameres [ arrowhead="open", style="solid" ];
+ dialog -> protocols [ arrowhead="open", style="solid" ];
+ }
+ @enddot
+ */
+
+/** @file
+ * "Preferences" and "Protocol properties" dialog boxes.
+ * @ingroup dialog_group
+ * @ingroup prefs_group
+ */
+
+typedef enum {
+ PREFS_PAGE_USER_INTERFACE,
+ PREFS_PAGE_LAYOUT,
+ PREFS_PAGE_COLUMNS
+} PREFS_PAGE_E;
+
+/** Show the preferences dialog.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void prefs_cb(GtkWidget *widget, gpointer data);
+
+/** Show the preferences dialog in given page.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ * @param prefs_page page to show
+ */
+extern void prefs_page_cb(GtkWidget *widget, gpointer data, PREFS_PAGE_E prefs_page);
+
+/** Show the protocol properties dialog.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void properties_cb(GtkWidget *widget, gpointer data);
+
+/** Create a check button for a preferences page.
+ *
+ * @param main_tb the table to put this button into
+ * @param table_row row in the table
+ * @param label_text the label text for the left side
+ * @param tooltip_text the tooltip for this check button
+ * @param active the check button is initially active
+ * @return the new check button
+ */
+extern GtkWidget *create_preference_check_button(GtkWidget *main_tb, int table_row,
+ const gchar *label_text, const gchar *tooltip_text, gboolean active);
+
+/** Create a radio button for a preferences page.
+ *
+ * @param main_tb the table to put this button into
+ * @param table_row row in the table
+ * @param label_text the label text for the left side
+ * @param tooltip_text the tooltip for this radio button
+ * @param enumvals the values
+ * @param current_val the initially selected value
+ * @return the new radio button
+ */
+extern GtkWidget *create_preference_radio_buttons(GtkWidget *main_tb, int table_row,
+ const gchar *label_text, const gchar *tooltip_text,
+ const enum_val_t *enumvals, gint current_val);
+
+/** Get the currently selected value from a radio button.
+ *
+ * @param button the button from create_preference_radio_buttons()
+ * @param enumvals the same enum vals as in create_preference_radio_buttons()
+ * @return the index of the currently selected item
+ */
+extern gint fetch_preference_radio_buttons_val(GtkWidget *button, const enum_val_t *enumvals);
+
+/** Create an option menu for a preferences page.
+ *
+ * @param main_tb the table to put this menu into
+ * @param table_row row in the table
+ * @param label_text the label text for the left side
+ * @param tooltip_text the tooltip for this option menu
+ * @param enumvals the values
+ * @param current_val the initially selected value
+ * @return the new option menu
+ */
+extern GtkWidget *create_preference_option_menu(GtkWidget *main_tb, int table_row,
+ const gchar *label_text, const gchar *tooltip_text,
+ const enum_val_t *enumvals, gint current_val);
+
+/** Get the currently selected value from an option menu.
+ *
+ * @param optmenu the option menu from create_preference_option_menu()
+ * @param enumvals the same enum vals as in create_preference_option_menu()
+ * @return the index of the currently selected item
+ */
+extern gint fetch_preference_option_menu_val(GtkWidget *optmenu, const enum_val_t *enumvals);
+
+/** Create a text entry for a preferences page.
+ *
+ * @param main_tb the table to put this entry into
+ * @param table_row row in the table
+ * @param label_text the label text for the left side
+ * @param tooltip_text the tooltip for this text entry
+ * @param value the initially value
+ * @return the new text entry
+ */
+extern GtkWidget *create_preference_entry(GtkWidget *main_tb, int table_row,
+ const gchar *label_text, const gchar *tooltip_text, char *value);
+
+/** Create a static text for a preferences page.
+ *
+ * @param main_tb the table to put this entry into
+ * @param table_position row in the table
+ * @param label_text the label text
+ * @param tooltip_text the tooltip for this text (not needed at all...)
+ * @return the new static text label
+ */
+GtkWidget *
+create_preference_static_text(GtkWidget *main_tb, int table_position,
+ const gchar *label_text, const gchar *tooltip_text);
+
+/** Create a UAT button for a preferences page.
+ *
+ * @param main_tb the table to put this entry into
+ * @param table_position row in the table
+ * @param label_text the label text
+ * @param tooltip_text the tooltip for this text
+ * @param uat pointer to the UAT
+ * @return the new UAT button
+ */
+GtkWidget *
+create_preference_uat(GtkWidget *main_tb, int table_position,
+ const gchar *label_text, const gchar *tooltip_text, void *uat);
+
+/** Save all preferences */
+void
+prefs_main_write(void);
+
+#endif
diff --git a/ui/gtk/prefs_filter_expressions.c b/ui/gtk/prefs_filter_expressions.c
new file mode 100644
index 0000000000..91af3664bd
--- /dev/null
+++ b/ui/gtk/prefs_filter_expressions.c
@@ -0,0 +1,386 @@
+/* prefs_filter_expressions.c
+ * Submitted by Edwin Groothuis <wireshark@mavetju.org>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/prefs.h>
+#include <epan/column_info.h>
+#include <epan/column.h>
+#include <epan/strutil.h>
+#include <epan/filter_expressions.h>
+
+#include "../globals.h"
+
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/new_packet_list.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/filter_autocomplete.h"
+#include "ui/gtk/filter_expression_save_dlg.h"
+#include "ui/gtk/old-gtk-compat.h"
+
+static void filter_expressions_list_new_cb(GtkWidget *, gpointer);
+static void filter_expressions_list_remove_cb(GtkWidget *, gpointer);
+static gboolean filter_expressions_label_changed_cb(GtkCellRendererText *, const gchar *, const gchar *, gpointer);
+static gboolean filter_expressions_expression_changed_cb(GtkCellRendererText *, const gchar *, const gchar *, gpointer);
+
+#define E_FILTER_EXPRESSION_COLUMNL "filter_expression_columnl"
+#define E_FILTER_EXPRESSION_STORE "filter_expression_store"
+
+enum {
+ ENABLED_COLUMN,
+ LABEL_COLUMN,
+ EXPRESSION_COLUMN,
+ DATA_COLUMN,
+ N_COLUMN /* The number of columns */
+};
+
+/* Visible toggled */
+static void
+enable_toggled(GtkCellRendererToggle *cell _U_, gchar *path_str, gpointer data)
+{
+ GtkTreeModel *model = (GtkTreeModel *)data;
+ GtkTreeIter iter;
+ GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
+ struct filter_expression *fe;
+
+ gtk_tree_model_get_iter(model, &iter, path);
+ gtk_tree_model_get(model, &iter, DATA_COLUMN, &fe, -1);
+
+ fe->enabled = !fe->enabled;
+
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, ENABLED_COLUMN,
+ fe->enabled, -1);
+
+ gtk_tree_path_free(path);
+} /* visible_toggled */
+
+/*
+ * Create and display the column selection widgets.
+ * Called as part of the creation of the Preferences notebook ( Edit ! Preferences )
+ */
+GtkWidget *
+filter_expressions_prefs_show(void) {
+ GtkWidget *main_vb, *bottom_hb, *column_l, *add_bt, *remove_bt;
+ GtkWidget *list_vb, *list_lb, *list_sc;
+ GtkWidget *add_remove_hb;
+ GtkListStore *store;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *sel;
+ GtkTreeIter iter;
+ GtkTreeIter first_iter;
+ gint first_row = TRUE;
+ struct filter_expression *fe;
+ const gchar *column_titles[] = {"Enabled", "Label",
+ "Filter Expression"};
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_widget_show(main_vb);
+
+ list_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(list_vb), 5);
+ gtk_widget_show(list_vb);
+ gtk_box_pack_start(GTK_BOX(main_vb), list_vb, TRUE, TRUE, 0);
+
+ list_lb = gtk_label_new(("[The first list entry will be displayed as the "
+ "first button right of the Save button - Drag and drop entries to "
+ "change column order]"));
+ gtk_widget_show(list_lb);
+ gtk_box_pack_start(GTK_BOX(list_vb), list_lb, FALSE, FALSE, 0);
+
+ list_sc = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(list_sc),
+ GTK_SHADOW_IN);
+ gtk_container_add(GTK_CONTAINER(list_vb), list_sc);
+ gtk_widget_show(list_sc);
+
+ store = gtk_list_store_new(N_COLUMN,
+ G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_POINTER);
+
+ column_l = tree_view_new(GTK_TREE_MODEL(store));
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(column_l), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(column_l), FALSE);
+ gtk_tree_view_set_reorderable(GTK_TREE_VIEW(column_l), TRUE);
+ gtk_widget_set_tooltip_text(column_l, "Click on a label or expression to "
+ "change its name.\nDrag an item to change its order.\nTick 'Enable' "
+ "to enable the filter in the buttons.");
+
+ /* Enabled buton */
+ renderer = gtk_cell_renderer_toggle_new();
+ g_signal_connect(renderer, "toggled", G_CALLBACK(enable_toggled), store);
+ column = gtk_tree_view_column_new_with_attributes(
+ column_titles[ENABLED_COLUMN], renderer, "active", ENABLED_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(column_l), column);
+
+ /* Label editor */
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(G_OBJECT(renderer), "editable", TRUE, NULL);
+ g_signal_connect(renderer, "edited",
+ G_CALLBACK(filter_expressions_label_changed_cb), GTK_TREE_MODEL(store));
+ column = gtk_tree_view_column_new_with_attributes(
+ column_titles[LABEL_COLUMN], renderer, "text", LABEL_COLUMN, NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(column_l), column);
+
+ /* Expression editor */
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(G_OBJECT(renderer), "editable", TRUE, NULL);
+ g_signal_connect(renderer, "edited",
+ G_CALLBACK(filter_expressions_expression_changed_cb),
+ GTK_TREE_MODEL(store));
+ column = gtk_tree_view_column_new_with_attributes(
+ column_titles[EXPRESSION_COLUMN], renderer, "text", EXPRESSION_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(column_l), column);
+
+ /* XXX - make this match the packet list prefs? */
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(column_l));
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+
+ gtk_container_add(GTK_CONTAINER(list_sc), column_l);
+ gtk_widget_show(column_l);
+
+ fe = *pfilter_expression_head;
+ while (fe != NULL) {
+ fe->index = -1;
+ gtk_list_store_insert_with_values(store, &iter, G_MAXINT,
+ ENABLED_COLUMN, fe->enabled,
+ LABEL_COLUMN, fe->label,
+ EXPRESSION_COLUMN, fe->expression,
+ DATA_COLUMN, fe,
+ -1);
+
+ if (first_row) {
+ first_iter = iter;
+ first_row = FALSE;
+ }
+ fe = fe->next;
+ }
+ g_object_unref(G_OBJECT(store));
+
+ /* Bottom row: Add/remove buttons */
+ bottom_hb = gtk_hbox_new(FALSE, 5);
+ gtk_box_pack_start(GTK_BOX(main_vb), bottom_hb, FALSE, TRUE, 0);
+ gtk_widget_show(bottom_hb);
+
+ /* Add button */
+ add_remove_hb = gtk_hbox_new(TRUE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(add_remove_hb), 5);
+ gtk_box_pack_start(GTK_BOX(bottom_hb), add_remove_hb, FALSE, FALSE, 0);
+ gtk_widget_show(add_remove_hb);
+
+ add_bt = gtk_button_new_from_stock(GTK_STOCK_ADD);
+ g_signal_connect(add_bt, "clicked",
+ G_CALLBACK(filter_expressions_list_new_cb), column_l);
+ gtk_box_pack_start(GTK_BOX(add_remove_hb), add_bt, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text(add_bt,
+ "Add a new row at the end of the list.");
+ gtk_widget_show(add_bt);
+
+ /* Remove button */
+ remove_bt = gtk_button_new_from_stock(GTK_STOCK_REMOVE);
+ g_signal_connect(remove_bt, "clicked",
+ G_CALLBACK(filter_expressions_list_remove_cb), column_l);
+ gtk_box_pack_start(GTK_BOX(add_remove_hb), remove_bt, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text(remove_bt, "Remove the selected row.");
+ gtk_widget_show(remove_bt);
+
+ /* select the first menu list row. */
+ /* Triggers call to column_list_select_cb(). */
+ if (first_row == FALSE)
+ gtk_tree_selection_select_iter(sel, &first_iter);
+
+ g_object_set_data(G_OBJECT(main_vb), E_FILTER_EXPRESSION_COLUMNL,
+ column_l);
+ g_object_set_data(G_OBJECT(main_vb), E_FILTER_EXPRESSION_STORE,
+ store);
+
+ return(main_vb);
+}
+
+static void
+filter_expressions_list_remove_cb(GtkWidget *w _U_, gpointer data)
+{
+ GtkTreeView *column_l = GTK_TREE_VIEW(data);
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ struct filter_expression *fe;
+
+ sel = gtk_tree_view_get_selection(column_l);
+ if (!gtk_tree_selection_get_selected(sel, &model, &iter))
+ return;
+
+ gtk_tree_model_get(model, &iter, DATA_COLUMN, &fe, -1);
+ fe->deleted = TRUE;
+
+ gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
+}
+
+static void
+filter_expressions_list_new_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ const gchar *label = "New Label";
+ const gchar *expression = "New Expression";
+ GtkTreeView *fe_l = GTK_TREE_VIEW(data);
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ GtkTreeViewColumn *label_column;
+ struct filter_expression *fe;
+
+ fe = filter_expression_new(label, expression, TRUE);
+
+ model = gtk_tree_view_get_model(fe_l);
+ gtk_list_store_insert_with_values(GTK_LIST_STORE(model), &iter, G_MAXINT,
+ ENABLED_COLUMN, fe->enabled,
+ LABEL_COLUMN, fe->label,
+ EXPRESSION_COLUMN, fe->expression,
+ DATA_COLUMN, fe,
+ -1);
+
+ /* Triggers call to column_list_select_cb() */
+ gtk_tree_selection_select_iter(gtk_tree_view_get_selection(fe_l), &iter);
+
+ /* Set the cursor to the 'Title' column of the newly added row and enable
+ * editing
+ * XXX: If displaying the new title ["New column"] widens the title column
+ * of the treeview, then the set_cursor below doesn't properly generate an
+ * entry box around the title text. The width of the box appears to be the
+ * column width before the treeview title column was widened. Seems like a
+ * bug...
+ *
+ * I haven't found a work-around.
+ */
+ path = gtk_tree_model_get_path(model, &iter);
+ label_column = gtk_tree_view_get_column(fe_l, 2);
+ gtk_tree_view_set_cursor(fe_l, path, label_column, TRUE);
+ gtk_tree_path_free(path);
+}
+
+
+static gboolean
+filter_expressions_expression_changed_cb(GtkCellRendererText *cell _U_, const gchar *str_path, const gchar *new_expression, gpointer data)
+{
+ struct filter_expression *fe;
+ GtkTreeModel *model = (GtkTreeModel *)data;
+ GtkTreePath *path = gtk_tree_path_new_from_string(str_path);
+ GtkTreeIter iter;
+
+ gtk_tree_model_get_iter(model, &iter, path);
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, EXPRESSION_COLUMN,
+ new_expression, -1);
+
+ gtk_tree_model_get(model, &iter, DATA_COLUMN, &fe, -1);
+ if (fe != NULL) {
+ g_free(fe->expression);
+ fe->expression = g_strdup(new_expression);
+ }
+
+ gtk_tree_path_free(path);
+ return(TRUE);
+}
+
+static gboolean
+filter_expressions_label_changed_cb(GtkCellRendererText *cell _U_, const gchar *str_path, const gchar *new_label, gpointer data)
+{
+ struct filter_expression *fe;
+ GtkTreeModel *model = (GtkTreeModel *)data;
+ GtkTreePath *path = gtk_tree_path_new_from_string(str_path);
+ GtkTreeIter iter;
+
+ gtk_tree_model_get_iter(model, &iter, path);
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, LABEL_COLUMN, new_label,
+ -1);
+
+ gtk_tree_model_get(model, &iter, DATA_COLUMN, &fe, -1);
+ if (fe != NULL) {
+ g_free(fe->label);
+ fe->label = g_strdup(new_label);
+ }
+
+ gtk_tree_path_free(path);
+ return TRUE;
+}
+
+
+void
+filter_expressions_prefs_fetch(GtkWidget *w)
+{
+ gboolean items_left;
+ GtkTreeModel *model;
+ GtkTreeView *column_l;
+ GtkTreeIter iter;
+ GtkListStore *store;
+ struct filter_expression *fe;
+ gint first_row = TRUE;
+ gint index = 0;
+
+ column_l = (GtkTreeView *)g_object_get_data(G_OBJECT(w),
+ E_FILTER_EXPRESSION_COLUMNL);
+ model = gtk_tree_view_get_model(column_l);
+ store = (GtkListStore *)g_object_get_data(G_OBJECT(w),
+ E_FILTER_EXPRESSION_STORE);
+
+ /* Record the order of the items in the list. */
+ items_left = gtk_tree_model_get_iter_first(model, &iter);
+ while (items_left) {
+ gtk_tree_model_get(model, &iter, DATA_COLUMN, &fe, -1);
+ if (fe != NULL)
+ fe->index = index++;
+ items_left = gtk_tree_model_iter_next (model, &iter);
+ }
+
+ filter_expression_reinit(FILTER_EXPRESSION_REINIT_DESTROY | FILTER_EXPRESSION_REINIT_CREATE);
+
+ gtk_list_store_clear(store);
+ fe = *pfilter_expression_head;
+ while (fe != NULL) {
+ fe->index = -1;
+ gtk_list_store_insert_with_values(store, &iter, G_MAXINT,
+ ENABLED_COLUMN, fe->enabled,
+ LABEL_COLUMN, fe->label,
+ EXPRESSION_COLUMN, fe->expression,
+ DATA_COLUMN, fe,
+ -1);
+
+ if (first_row) {
+ first_row = FALSE;
+ }
+ fe = fe->next;
+ }
+}
diff --git a/ui/gtk/prefs_filter_expressions.h b/ui/gtk/prefs_filter_expressions.h
new file mode 100644
index 0000000000..6db77da9f8
--- /dev/null
+++ b/ui/gtk/prefs_filter_expressions.h
@@ -0,0 +1,45 @@
+/* prefs_filter_expressions.h
+ * Submitted by Edwin Groothuis <wireshark@mavetju.org>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PREFS_FILTER_EXPRESSIONS_H__
+#define __PREFS_FILTER_EXPRESSIONS_H__
+
+/** @file
+ * "Name resolution" preferences page.
+ * @ingroup prefs_group
+ */
+
+/** Build a Filter Save preferences page.
+ *
+ * @return the new preferences page
+ */
+GtkWidget *filter_expressions_prefs_show(void);
+
+/** Fetch preference values from page.
+ *
+ * @param widget widget from filtersave_prefs_show()
+ */
+void filter_expressions_prefs_fetch(GtkWidget *widget);
+
+#endif
diff --git a/ui/gtk/prefs_gui.c b/ui/gtk/prefs_gui.c
new file mode 100644
index 0000000000..fd3ab2fc5d
--- /dev/null
+++ b/ui/gtk/prefs_gui.c
@@ -0,0 +1,699 @@
+/* gui_prefs.c
+ * Dialog box for GUI preferences
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/prefs.h>
+
+#include "../simple_dialog.h"
+
+#include "ui/gtk/prefs_gui.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/supported_protos_dlg.h"
+#include "ui/gtk/prefs_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/new_packet_list.h"
+#include "ui/gtk/main_proto_draw.h"
+#include "ui/gtk/main_toolbar.h"
+#include "ui/gtk/font_utils.h"
+#include "ui/gtk/recent.h"
+#include "ui/gtk/webbrowser.h"
+#include "ui/gtk/main_welcome.h"
+
+
+static gint fetch_enum_value(gpointer control, const enum_val_t *enumvals);
+static gboolean fileopen_dir_changed_cb(GtkWidget *myentry, GdkEvent *event _U_, gpointer parent_w _U_);
+static gboolean fileopen_preview_changed_cb(GtkWidget *myentry _U_, GdkEvent *event, gpointer parent_w);
+static void fileopen_selected_cb(GtkWidget *mybutton_rb _U_, gpointer parent_w);
+static gboolean recent_files_count_changed_cb(GtkWidget *recent_files_entry _U_,
+ GdkEvent *event _U_, gpointer parent_w);
+static gboolean recent_df_entries_changed_cb(GtkWidget *recent_df_entry _U_,
+ GdkEvent *event _U_, gpointer parent_w);
+static gint scroll_percent_changed_cb(GtkWidget *recent_df_entry _U_,
+ GdkEvent *event _U_, gpointer parent_w);
+#define PLIST_SEL_BROWSE_KEY "plist_sel_browse"
+#define PTREE_SEL_BROWSE_KEY "ptree_sel_browse"
+#define GEOMETRY_POSITION_KEY "geometry_position"
+#define GEOMETRY_SIZE_KEY "geometry_size"
+#define GEOMETRY_MAXIMIZED_KEY "geometry_maximized"
+
+#define MACOSX_STYLE_KEY "macosx_style"
+
+#define GUI_CONSOLE_OPEN_KEY "console_open"
+#define GUI_FILEOPEN_KEY "fileopen_behavior"
+#define GUI_FILEOPEN_PREVIEW_KEY "fileopen_preview_timeout"
+#define GUI_RECENT_FILES_COUNT_KEY "recent_files_count"
+#define GUI_RECENT_DF_ENTRIES_KEY "recent_display_filter_entries"
+#define GUI_FILEOPEN_DIR_KEY "fileopen_directory"
+#define GUI_ASK_UNSAVED_KEY "ask_unsaved"
+#define GUI_WEBBROWSER_KEY "webbrowser"
+#define GUI_FIND_WRAP_KEY "find_wrap"
+#define GUI_USE_PREF_SAVE_KEY "use_pref_save"
+#define GUI_SHOW_VERSION_KEY "show_version"
+#define GUI_EXPERT_EYECANDY_KEY "expert_eyecandy"
+#define GUI_AUTO_SCROLL_KEY "auto_scroll_on_expand"
+#define GUI_SCROLL_PERCENT_KEY "scroll_percent_on_expand"
+
+static const enum_val_t scrollbar_placement_vals[] _U_ = {
+ { "FALSE", "Left", FALSE },
+ { "TRUE", "Right", TRUE },
+ { NULL, NULL, 0 }
+};
+
+static const enum_val_t selection_mode_vals[] = {
+ { "FALSE", "Selects", FALSE },
+ { "TRUE", "Browses", TRUE },
+ { NULL, NULL, 0 }
+};
+
+static const enum_val_t altern_colors_vals[] _U_ = {
+ { "FALSE", "No", FALSE },
+ { "TRUE", "Yes", TRUE },
+ { NULL, NULL, 0 }
+};
+
+static const enum_val_t filter_toolbar_placement_vals[] _U_ = {
+ { "FALSE", "Below the main toolbar", FALSE },
+ { "TRUE", "Insert into statusbar", TRUE },
+ { NULL, NULL, 0 }
+};
+
+static const enum_val_t highlight_style_vals[] _U_ = {
+ { "FALSE", "Bold", FALSE },
+ { "TRUE", "Inverse", TRUE },
+ { NULL, NULL, 0 }
+};
+
+
+static const enum_val_t toolbar_style_vals[] _U_ = {
+ { "ICONS", "Icons only", TB_STYLE_ICONS },
+ { "TEXT", "Text only", TB_STYLE_TEXT },
+ { "BOTH", "Icons & Text", TB_STYLE_BOTH },
+ { NULL, NULL, 0 }
+};
+
+#ifdef _WIN32
+static const enum_val_t gui_console_open_vals[] = {
+ { "NEVER", "Never", console_open_never },
+ { "AUTOMATIC", "Automatic (advanced user)", console_open_auto },
+ { "ALWAYS", "Always (debugging)", console_open_always },
+ { NULL, NULL, 0 }
+};
+#endif
+
+static const enum_val_t gui_fileopen_vals[] = {
+ { "LAST_OPENED", "Remember last directory", FO_STYLE_LAST_OPENED },
+ { "SPECIFIED", "Always start in:", FO_STYLE_SPECIFIED },
+ { NULL, NULL, 0 }
+};
+
+/* Set to FALSE initially; set to TRUE if the user ever hits "OK" on
+ the "Font..." dialog, so that we know that they (probably) changed
+ the font, and therefore that the "apply" function needs to take care
+ of that */
+static gboolean font_changed;
+
+/* Font name from the font dialog box; if "font_changed" is TRUE, this
+ has been set to the name of the font the user selected. */
+static gchar *new_font_name;
+
+static GtkWidget *font_browse_w;
+
+/* Used to contain the string from the Recent Files Count Max pref item */
+static char recent_files_count_max_str[128] = "";
+
+/* Used to contain the string from the Recent Display Filter Max Entries pref item */
+static char recent_df_entries_max_str[128] = "";
+
+/* Used to contain the string from the Open File preview timeout pref item */
+static char open_file_preview_str[128] = "";
+
+/* Used to contain the string from the Auto Scroll Percentage pref item */
+static char scroll_percent_preview_str[128] = "";
+
+#define GUI_TABLE_ROWS 4
+
+GtkWidget*
+gui_prefs_show(void)
+{
+ GtkWidget *main_tb, *main_vb;
+ GtkWidget *plist_browse_om;
+ GtkWidget *ptree_browse_om;
+#ifdef _WIN32
+ GtkWidget *console_open_om;
+#endif
+ GtkWidget *fileopen_rb, *fileopen_dir_te, *fileopen_preview_te;
+ GtkWidget *recent_files_count_max_te, *recent_df_entries_max_te, *ask_unsaved_cb, *find_wrap_cb;
+ GtkWidget *use_pref_save_cb;
+ GtkWidget *show_version_cb;
+ GtkWidget *auto_scroll_cb, *scroll_percent_te;
+ GtkWidget *webbrowser_te;
+ GtkWidget *save_position_cb, *save_size_cb, *save_maximized_cb;
+#if defined(HAVE_IGE_MAC_INTEGRATION) || defined(HAVE_GTKOSXAPPLICATION)
+ GtkWidget *macosx_style_cb;
+#endif
+ GtkWidget *expert_info_eyecandy_cb;
+
+ int pos = 0;
+ char current_val_str[128];
+
+ /* The font haven't been changed yet. */
+ font_changed = FALSE;
+
+ /* The columns haven't been changed yet */
+ cfile.cinfo.columns_changed = FALSE;
+
+ /* Main vertical box */
+ main_vb = gtk_vbox_new(FALSE, 7);
+ gtk_container_set_border_width( GTK_CONTAINER(main_vb), 5 );
+
+ /* Main table */
+ main_tb = gtk_table_new(GUI_TABLE_ROWS, 2, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
+
+ /* Packet list selection browseable */
+ plist_browse_om = create_preference_option_menu(main_tb, pos++,
+ "Packet list selection mode:",
+ "Choose to browse or select a packet for detailed dissection.",
+ selection_mode_vals, prefs.gui_plist_sel_browse);
+ g_object_set_data(G_OBJECT(main_vb), PLIST_SEL_BROWSE_KEY, plist_browse_om);
+
+ /* Proto tree selection browseable */
+ ptree_browse_om = create_preference_option_menu(main_tb, pos++,
+ "Protocol tree selection mode:",
+ "Choose to browse or select.",
+ selection_mode_vals, prefs.gui_ptree_sel_browse);
+ g_object_set_data(G_OBJECT(main_vb), PTREE_SEL_BROWSE_KEY, ptree_browse_om);
+
+ /* Geometry prefs */
+ save_position_cb = create_preference_check_button(main_tb, pos++,
+ "Save window position:",
+ "Whether to save the position of the main window.",
+ prefs.gui_geometry_save_position);
+ g_object_set_data(G_OBJECT(main_vb), GEOMETRY_POSITION_KEY, save_position_cb);
+
+ save_size_cb = create_preference_check_button(main_tb, pos++,
+ "Save window size:",
+ "Whether to save the size of the main window.",
+ prefs.gui_geometry_save_size);
+ g_object_set_data(G_OBJECT(main_vb), GEOMETRY_SIZE_KEY, save_size_cb);
+
+ save_maximized_cb = create_preference_check_button(main_tb, pos++,
+ "Save maximized state:",
+ "Whether to save the maximized state of the main window.",
+ prefs.gui_geometry_save_maximized);
+ g_object_set_data(G_OBJECT(main_vb), GEOMETRY_MAXIMIZED_KEY, save_maximized_cb);
+
+#if defined(HAVE_IGE_MAC_INTEGRATION) || defined(HAVE_GTKOSXAPPLICATION)
+ macosx_style_cb = create_preference_check_button(main_tb, pos++,
+ "Mac OS X style",
+ "Whether to create a Mac OS X look and feel. Checking this box will move the "
+ "menu bar to the top of the screen instead of the top of the Wireshark window. "
+ "Requires a restart of Wireshark to take effect.",
+ prefs.gui_macosx_style);
+ g_object_set_data(G_OBJECT(main_vb), MACOSX_STYLE_KEY, macosx_style_cb);
+#endif
+
+#ifdef _WIN32
+ /* How the console window should be opened */
+ console_open_om = create_preference_option_menu(main_tb, pos++,
+ "Open a console window",
+ "Whether to open a console window "
+ "(Automatic will open a console if messages appear).",
+ gui_console_open_vals, prefs.gui_console_open);
+ g_object_set_data(G_OBJECT(main_vb), GUI_CONSOLE_OPEN_KEY, console_open_om);
+#endif
+
+ /* Allow user to select where they want the File Open dialog to open to
+ * by default */
+ fileopen_rb = create_preference_radio_buttons(main_tb, pos++,
+ "\"File Open\" dialog behavior:",
+ "Which directory the \"File Open\" dialog should start with.",
+ gui_fileopen_vals, prefs.gui_fileopen_style);
+
+ /* Directory to default File Open dialog to */
+ fileopen_dir_te = create_preference_entry(main_tb, pos++,
+ "Directory:",
+ "The \"File Open\" dialog defaults always to this directory.",
+ prefs.gui_fileopen_dir);
+ g_object_set_data(G_OBJECT(main_vb), GUI_FILEOPEN_KEY, fileopen_rb);
+ g_object_set_data(G_OBJECT(main_vb), GUI_FILEOPEN_DIR_KEY, fileopen_dir_te);
+ g_signal_connect(fileopen_rb, "clicked", G_CALLBACK(fileopen_selected_cb), main_vb);
+ g_signal_connect(fileopen_dir_te, "focus-out-event",
+ G_CALLBACK(fileopen_dir_changed_cb), main_vb);
+
+ /* File Open dialog preview timeout */
+ fileopen_preview_te = create_preference_entry(main_tb, pos++,
+ "\"File Open\" preview timeout:",
+ "Reading preview data in the \"File Open\" dialog will be stopped after given seconds.",
+ open_file_preview_str);
+ g_snprintf(current_val_str, sizeof(current_val_str), "%d", prefs.gui_fileopen_preview);
+ gtk_entry_set_text(GTK_ENTRY(fileopen_preview_te), current_val_str);
+ g_object_set_data(G_OBJECT(main_vb), GUI_FILEOPEN_PREVIEW_KEY, fileopen_preview_te);
+ g_signal_connect(fileopen_preview_te, "focus_out_event", G_CALLBACK(fileopen_preview_changed_cb), main_vb);
+
+ /* Number of recent entries in the display filter list ... */
+ recent_df_entries_max_te = create_preference_entry(main_tb, pos++,
+ "Filter display max. list entries:",
+ "Maximum number of recent entries in filter display list.",
+ recent_df_entries_max_str);
+ g_snprintf(current_val_str, sizeof(current_val_str), "%d", prefs.gui_recent_df_entries_max);
+ gtk_entry_set_text(GTK_ENTRY(recent_df_entries_max_te), current_val_str);
+ g_object_set_data(G_OBJECT(main_vb), GUI_RECENT_DF_ENTRIES_KEY, recent_df_entries_max_te);
+ g_signal_connect(recent_df_entries_max_te, "focus_out_event", G_CALLBACK(recent_df_entries_changed_cb), main_vb);
+
+ /* Number of entries in the recent_files list ... */
+ recent_files_count_max_te = create_preference_entry(main_tb, pos++,
+ "\"Open Recent\" max. list entries:",
+ "Maximum number of entries in the \"File/Open Recent\" list.",
+ recent_files_count_max_str);
+ g_snprintf(current_val_str, sizeof(current_val_str), "%d", prefs.gui_recent_files_count_max);
+ gtk_entry_set_text(GTK_ENTRY(recent_files_count_max_te), current_val_str);
+ g_object_set_data(G_OBJECT(main_vb), GUI_RECENT_FILES_COUNT_KEY, recent_files_count_max_te);
+ g_signal_connect(recent_files_count_max_te, "focus_out_event", G_CALLBACK(recent_files_count_changed_cb), main_vb);
+
+ fileopen_selected_cb(NULL, main_vb);
+
+ /* ask for unsaved capture files? */
+ ask_unsaved_cb = create_preference_check_button(main_tb, pos++,
+ "Ask for unsaved capture files:",
+ "Whether a dialog should pop up in case of an unsaved capture file.",
+ prefs.gui_ask_unsaved);
+ g_object_set_data(G_OBJECT(main_vb), GUI_ASK_UNSAVED_KEY, ask_unsaved_cb);
+
+ /* do we want to wrap when searching for data? */
+ find_wrap_cb = create_preference_check_button(main_tb, pos++,
+ "Wrap to end/beginning of file during a find:",
+ "Whether a search should wrap in a capture file.",
+ prefs.gui_find_wrap);
+ g_object_set_data(G_OBJECT(main_vb), GUI_FIND_WRAP_KEY, find_wrap_cb);
+
+ /* show an explicit Save button for settings dialogs (preferences and alike)? */
+ use_pref_save_cb = create_preference_check_button(main_tb, pos++,
+ "Settings dialogs show a save button:",
+ "Whether the various settings dialogs (e.g. Preferences) should "
+ "use an explicit save button - for advanced users.",
+ prefs.gui_use_pref_save);
+ g_object_set_data(G_OBJECT(main_vb), GUI_USE_PREF_SAVE_KEY, use_pref_save_cb);
+
+ /* Show version in welcome screen */
+ show_version_cb = create_preference_check_button(main_tb, pos++,
+ "Welcome screen and title bar shows version:",
+ "Whether version should be shown in the start page and main screen's title bar.",
+ prefs.gui_version_in_start_page );
+ g_object_set_data(G_OBJECT(main_vb), GUI_SHOW_VERSION_KEY, show_version_cb);
+
+ /* Whether to auto scroll when expanding items */
+ auto_scroll_cb = create_preference_check_button(main_tb, pos++,
+ "Auto scroll on expansion:",
+ "Whether the details view should be automatically scrolled up when expanding an item.",
+ prefs.gui_auto_scroll_on_expand );
+ g_object_set_data(G_OBJECT(main_vb), GUI_AUTO_SCROLL_KEY, auto_scroll_cb);
+
+ /* Where to auto scroll to when expanding items */
+ scroll_percent_te = create_preference_entry(main_tb, pos++,
+ "Auto scroll percentage:",
+ "Where to scroll the expanded item to within the view e.g. 0% = top of view, 50% = center of view.",
+ scroll_percent_preview_str);
+ g_snprintf(current_val_str, sizeof(current_val_str), "%d", prefs.gui_auto_scroll_percentage);
+ gtk_entry_set_text(GTK_ENTRY(scroll_percent_te), current_val_str);
+ g_object_set_data(G_OBJECT(main_vb), GUI_SCROLL_PERCENT_KEY, scroll_percent_te);
+ g_signal_connect(scroll_percent_te, "focus_out_event", G_CALLBACK(scroll_percent_changed_cb), main_vb);
+
+ /* Webbrowser */
+ if (browser_needs_pref()) {
+ webbrowser_te = create_preference_entry(main_tb, pos++,
+ "Web browser command:",
+ "Command line to desired browser.",
+ prefs.gui_webbrowser);
+ gtk_entry_set_text(GTK_ENTRY(webbrowser_te), prefs.gui_webbrowser);
+ g_object_set_data(G_OBJECT(main_vb), GUI_WEBBROWSER_KEY, webbrowser_te);
+ }
+
+ /* Enable Expert Infos Dialog Tab Label "eye-candy" */
+ expert_info_eyecandy_cb = create_preference_check_button(main_tb, pos++,
+ "Display LEDs in the Expert Infos dialog tab labels:",
+ "Whether colored LED images should be displayed in the Expert Infos dialog tab labels.",
+ prefs.gui_expert_composite_eyecandy );
+ g_object_set_data(G_OBJECT(main_vb), GUI_EXPERT_EYECANDY_KEY, expert_info_eyecandy_cb);
+
+ /* Show 'em what we got */
+ gtk_widget_show_all(main_vb);
+
+ return(main_vb);
+}
+
+
+/* Create a font widget for browsing. */
+GtkWidget *
+gui_font_prefs_show(void)
+{
+ /* Create the font selection widget. */
+ font_browse_w = (GtkWidget *) gtk_font_selection_new();
+ gtk_widget_show(font_browse_w);
+
+ return font_browse_w;
+}
+
+
+static gboolean
+font_fetch(void)
+{
+ gchar *font_name;
+
+ font_name = g_strdup(gtk_font_selection_get_font_name(
+ GTK_FONT_SELECTION(font_browse_w)));
+ if (font_name == NULL) {
+ /* No font was selected; let the user know, but don't
+ tear down the font selection dialog, so they can
+ try again. */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "You have not selected a font.");
+ return FALSE;
+ }
+
+ if (!user_font_test(font_name)) {
+ /* The font isn't usable; "user_font_test()" has already
+ told the user why. Don't tear down the font selection
+ dialog. */
+ g_free(font_name);
+ return FALSE;
+ }
+ new_font_name = font_name;
+ return TRUE;
+}
+
+
+static gint
+fetch_enum_value(gpointer control, const enum_val_t *enumvals)
+{
+ return fetch_preference_option_menu_val(GTK_WIDGET(control), enumvals);
+}
+
+void
+gui_prefs_fetch(GtkWidget *w)
+{
+ prefs.gui_plist_sel_browse = fetch_enum_value(
+ g_object_get_data(G_OBJECT(w), PLIST_SEL_BROWSE_KEY), selection_mode_vals);
+ prefs.gui_ptree_sel_browse = fetch_enum_value(
+ g_object_get_data(G_OBJECT(w), PTREE_SEL_BROWSE_KEY), selection_mode_vals);
+ prefs.gui_geometry_save_position =
+ gtk_toggle_button_get_active(g_object_get_data(G_OBJECT(w), GEOMETRY_POSITION_KEY));
+ prefs.gui_geometry_save_size =
+ gtk_toggle_button_get_active(g_object_get_data(G_OBJECT(w), GEOMETRY_SIZE_KEY));
+ prefs.gui_geometry_save_maximized =
+ gtk_toggle_button_get_active(g_object_get_data(G_OBJECT(w), GEOMETRY_MAXIMIZED_KEY));
+
+#if defined(HAVE_IGE_MAC_INTEGRATION) || defined(HAVE_GTKOSXAPPLICATION)
+ prefs.gui_macosx_style =
+ gtk_toggle_button_get_active(g_object_get_data(G_OBJECT(w), MACOSX_STYLE_KEY));
+#endif
+
+#ifdef _WIN32
+ prefs.gui_console_open = fetch_enum_value(
+ g_object_get_data(G_OBJECT(w), GUI_CONSOLE_OPEN_KEY), gui_console_open_vals);
+#endif
+ prefs.gui_fileopen_style = fetch_preference_radio_buttons_val(
+ g_object_get_data(G_OBJECT(w), GUI_FILEOPEN_KEY), gui_fileopen_vals);
+
+ g_free(prefs.gui_fileopen_dir);
+ prefs.gui_fileopen_dir = g_strdup(gtk_entry_get_text(
+ GTK_ENTRY(g_object_get_data(G_OBJECT(w), GUI_FILEOPEN_DIR_KEY))));
+
+ prefs.gui_ask_unsaved =
+ gtk_toggle_button_get_active(g_object_get_data(G_OBJECT(w), GUI_ASK_UNSAVED_KEY));
+
+ prefs.gui_find_wrap =
+ gtk_toggle_button_get_active(g_object_get_data(G_OBJECT(w), GUI_FIND_WRAP_KEY));
+
+ prefs.gui_use_pref_save =
+ gtk_toggle_button_get_active(g_object_get_data(G_OBJECT(w), GUI_USE_PREF_SAVE_KEY));
+
+ prefs.gui_version_in_start_page =
+ gtk_toggle_button_get_active(g_object_get_data(G_OBJECT(w), GUI_SHOW_VERSION_KEY));
+
+ prefs.gui_auto_scroll_on_expand =
+ gtk_toggle_button_get_active(g_object_get_data(G_OBJECT(w), GUI_AUTO_SCROLL_KEY));
+
+ if (browser_needs_pref()) {
+ g_free(prefs.gui_webbrowser);
+ prefs.gui_webbrowser = g_strdup(gtk_entry_get_text(
+ GTK_ENTRY(g_object_get_data(G_OBJECT(w), GUI_WEBBROWSER_KEY))));
+ }
+
+ prefs.gui_expert_composite_eyecandy =
+ gtk_toggle_button_get_active(g_object_get_data(G_OBJECT(w), GUI_EXPERT_EYECANDY_KEY));
+
+ /*
+ * XXX - we need to have a way to fetch the preferences into
+ * local storage and only set the permanent preferences if there
+ * weren't any errors in those fetches, as there are several
+ * places where there *can* be a bad preference value.
+ */
+ if (font_fetch()) {
+ if (strcmp(new_font_name, prefs.gui_font_name) != 0) {
+ font_changed = TRUE;
+ g_free(prefs.gui_font_name);
+ prefs.gui_font_name = g_strdup(new_font_name);
+ }
+ }
+}
+
+
+
+void
+gui_prefs_apply(GtkWidget *w _U_ , gboolean redissect)
+{
+
+#ifdef _WIN32
+ /* user immediately wants to see a console */
+ if (prefs.gui_console_open == console_open_always) {
+ create_console();
+ }
+#endif
+
+ if (font_changed) {
+ /* This redraws the packet bytes windows. */
+ switch (user_font_apply()) {
+
+ case FA_SUCCESS:
+ break;
+
+ case FA_FONT_NOT_RESIZEABLE:
+ /* "user_font_apply()" popped up an alert box. */
+ /* turn off zooming - font can't be resized */
+ recent.gui_zoom_level = 0;
+ break;
+
+ case FA_FONT_NOT_AVAILABLE:
+ /* We assume this means that the specified size
+ isn't available. */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "That font isn't available at the specified zoom level;\n"
+ "turning zooming off.");
+ recent.gui_zoom_level = 0;
+ break;
+ }
+ } else if (!redissect) {
+ /* Redraw the packet bytes windows, in case the
+ highlight style changed, only if we aren't redissecting the whole file.
+ XXX - do it only if the highlight style *did* change. */
+ redraw_packet_bytes_all();
+ }
+
+ /* Redisplay the main window's title */
+ update_main_window_title();
+
+ /* Redisplay the default welcome header message in case the "show
+ * version" option was changed. */
+ welcome_header_set_message(NULL);
+
+ /* Redraw the help window(s). */
+ supported_redraw();
+ help_redraw();
+
+ /* XXX: redraw the toolbar only, if style changed */
+ toolbar_redraw_all();
+
+ set_scrollbar_placement_all();
+ new_packet_list_set_sel_browse(prefs.gui_plist_sel_browse, FALSE);
+ set_ptree_sel_browse_all(prefs.gui_ptree_sel_browse);
+ set_tree_styles_all();
+ main_widgets_rearrange();
+}
+
+void
+gui_prefs_destroy(GtkWidget *w _U_)
+{
+ /* Free up any saved font name. */
+ if (new_font_name != NULL) {
+ g_free(new_font_name);
+ new_font_name = NULL;
+ }
+}
+
+static gboolean
+recent_df_entries_changed_cb(GtkWidget *recent_df_entry _U_,
+ GdkEvent *event _U_, gpointer parent_w)
+{
+ GtkWidget *recent_df_entries_count_te;
+ guint newval;
+
+ recent_df_entries_count_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), GUI_RECENT_DF_ENTRIES_KEY);
+
+ /*
+ * Now, just convert the string to a number and store it in the prefs
+ * filed ...
+ */
+
+ newval = strtol(gtk_entry_get_text (GTK_ENTRY(recent_df_entries_count_te)), NULL, 10);
+
+ if (newval > 0) {
+ prefs.gui_recent_df_entries_max = newval;
+ }
+
+ /* We really should pop up a nasty dialog box if newval <= 0 */
+
+ return FALSE;
+}
+
+static gboolean
+recent_files_count_changed_cb(GtkWidget *recent_files_entry _U_,
+ GdkEvent *event _U_, gpointer parent_w)
+{
+ GtkWidget *recent_files_count_te;
+ guint newval;
+
+ recent_files_count_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), GUI_RECENT_FILES_COUNT_KEY);
+
+ /*
+ * Now, just convert the string to a number and store it in the prefs
+ * filed ...
+ */
+
+ newval = strtol(gtk_entry_get_text (GTK_ENTRY(recent_files_count_te)), NULL, 10);
+
+ if (newval > 0) {
+ prefs.gui_recent_files_count_max = newval;
+ }
+
+ /* We really should pop up a nasty dialog box if newval <= 0 */
+
+ return FALSE;
+}
+
+static gboolean
+fileopen_preview_changed_cb(GtkWidget *recent_files_entry _U_,
+ GdkEvent *event _U_, gpointer parent_w)
+{
+ GtkWidget *fileopen_preview_te;
+ guint newval;
+
+ fileopen_preview_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), GUI_FILEOPEN_PREVIEW_KEY);
+
+ /*
+ * Now, just convert the string to a number and store it in the prefs
+ * filed ...
+ */
+
+ newval = strtol(gtk_entry_get_text (GTK_ENTRY(fileopen_preview_te)), NULL, 10);
+
+ if (newval > 0) {
+ prefs.gui_fileopen_preview = newval;
+ }
+
+ /* We really should pop up a nasty dialog box if newval <= 0 */
+
+ return FALSE;
+}
+
+static gboolean
+fileopen_dir_changed_cb(GtkWidget *fileopen_dir_te, GdkEvent *event _U_, gpointer parent_w _U_)
+{
+ char *lastchar;
+ gint fileopen_dir_te_length;
+
+ fileopen_dir_te_length = (gint) strlen(gtk_entry_get_text (GTK_ENTRY(fileopen_dir_te)));
+ if (fileopen_dir_te_length == 0)
+ return FALSE;
+ lastchar = gtk_editable_get_chars(GTK_EDITABLE(fileopen_dir_te), fileopen_dir_te_length-1, -1);
+ if (strcmp(lastchar, G_DIR_SEPARATOR_S) != 0){
+ gtk_editable_insert_text(GTK_EDITABLE(fileopen_dir_te), G_DIR_SEPARATOR_S,
+ 1, /* new_text_length */
+ &fileopen_dir_te_length); /* *position */
+ }
+ return FALSE;
+}
+
+static void
+fileopen_selected_cb(GtkWidget *mybutton_rb _U_, gpointer parent_w)
+{
+ GtkWidget *fileopen_rb, *fileopen_dir_te;
+
+ fileopen_rb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), GUI_FILEOPEN_KEY);
+ fileopen_dir_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), GUI_FILEOPEN_DIR_KEY);
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(fileopen_rb)))
+ {
+ gtk_widget_set_sensitive(GTK_WIDGET(fileopen_dir_te), TRUE);
+ }
+ else
+ {
+ gtk_widget_set_sensitive(GTK_WIDGET(fileopen_dir_te), FALSE);
+ }
+ return;
+}
+
+static gboolean
+scroll_percent_changed_cb(GtkWidget *recent_files_entry _U_,
+ GdkEvent *event _U_, gpointer parent_w)
+{
+ GtkWidget *scroll_percent_te;
+ guint newval;
+
+ scroll_percent_te = (GtkWidget*)g_object_get_data(G_OBJECT(parent_w), GUI_SCROLL_PERCENT_KEY);
+
+ /*
+ * Now, just convert the string to a number and store it in the prefs field ...
+ */
+
+ newval = strtol(gtk_entry_get_text(GTK_ENTRY(scroll_percent_te)), NULL, 10);
+
+ if (newval <= 100) {
+ prefs.gui_auto_scroll_percentage = newval;
+ }
+
+ /* We really should pop up a dialog box is newval < 0 or > 100 */
+ return FALSE;
+}
diff --git a/ui/gtk/prefs_gui.h b/ui/gtk/prefs_gui.h
new file mode 100644
index 0000000000..f337ede8f2
--- /dev/null
+++ b/ui/gtk/prefs_gui.h
@@ -0,0 +1,64 @@
+/* prefs_gui.h
+ * Definitions for GUI preferences window
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GUI_PREFS_H__
+#define __GUI_PREFS_H__
+
+/** @file
+ * "User Interface" and "User Interface: Font" preferences pages.
+ * @ingroup prefs_group
+ */
+
+/** Build a User interface preferences page.
+ *
+ * @return the new preferences page
+ */
+extern GtkWidget *gui_prefs_show(void);
+
+/** Fetch preference values from page.
+ *
+ * @param widget widget from gui_prefs_show()
+ */
+extern void gui_prefs_fetch(GtkWidget *widget);
+
+/** Apply preference values from page.
+ *
+ * @param widget widget from gui_prefs_show()
+ */
+extern void gui_prefs_apply(GtkWidget *widget, gboolean);
+
+/** Destroy preference values from page.
+ *
+ * @param widget widget from gui_prefs_show()
+ */
+void gui_prefs_destroy(GtkWidget *widget);
+
+/** Build a User interface font preferences page.
+ *
+ * @return the new preferences page
+ */
+extern GtkWidget *gui_font_prefs_show(void);
+
+#endif
diff --git a/ui/gtk/prefs_layout.c b/ui/gtk/prefs_layout.c
new file mode 100644
index 0000000000..3de6d4b408
--- /dev/null
+++ b/ui/gtk/prefs_layout.c
@@ -0,0 +1,494 @@
+/* layout_prefs.c
+ * Dialog box for layout preferences
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <epan/prefs.h>
+
+#include "../ui_util.h"
+
+#include "ui/gtk/prefs_layout.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/prefs_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/dlg_utils.h"
+
+#include "../image/icon_layout_1.xpm"
+#include "../image/icon_layout_2.xpm"
+#include "../image/icon_layout_3.xpm"
+#include "../image/icon_layout_4.xpm"
+#include "../image/icon_layout_5.xpm"
+#include "../image/icon_layout_6.xpm"
+
+
+#define LAYOUT_QTY (layout_type_max - 1)
+
+
+static void layout_validate_cb(GtkWidget *w _U_, gpointer data);
+static gint fetch_enum_value(gpointer control, const enum_val_t *enumvals);
+
+
+typedef struct {
+ layout_type_e type;
+ layout_pane_content_e content[3];
+} layout_t;
+
+
+#define LAYOUT_TYPE_BUTTONS_KEY "layout_type_buttons"
+
+#define LAYOUT_NONE_RB_KEY "layout_none_radio_button"
+#define LAYOUT_PLIST_RB_KEY "layout_plist_radio_button"
+#define LAYOUT_PDETAILS_RB_KEY "layout_pdetails_radio_button"
+#define LAYOUT_PBYTES_RB_KEY "layout_pbytes_radio_button"
+
+#define LAYOUT_CONTENT1_VB_KEY "layout_content1_vbox"
+#define LAYOUT_CONTENT2_VB_KEY "layout_content2_vbox"
+#define LAYOUT_CONTENT3_VB_KEY "layout_content3_vbox"
+
+
+static GtkWidget *layout_content_radio_vbox(GtkWidget *main_vb, int i, layout_pane_content_e content) {
+ GtkWidget *radio_vb, *radio_lb;
+ GtkWidget *radio_none_rb, *radio_plist_rb, *radio_pdetails_rb, *radio_pbytes_rb;
+ char buf[64];
+
+
+ /* radio vbox */
+ radio_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(radio_vb), 6);
+
+ g_snprintf (buf, sizeof(buf), "Pane %d:", i);
+ radio_lb = gtk_label_new(buf);
+ gtk_misc_set_alignment(GTK_MISC(radio_lb), 0.0f, 0.5f);
+ gtk_container_add(GTK_CONTAINER(radio_vb), radio_lb);
+
+ radio_none_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "None");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_none_rb), content == layout_pane_content_none);
+ gtk_widget_set_tooltip_text (radio_none_rb, "Put nothing in this pane.");
+ gtk_container_add(GTK_CONTAINER(radio_vb), radio_none_rb);
+
+ radio_plist_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(radio_none_rb), "Packet List");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_plist_rb), content == layout_pane_content_plist);
+ gtk_widget_set_tooltip_text (radio_plist_rb, "Put the packet list in this pane.");
+ gtk_container_add(GTK_CONTAINER(radio_vb), radio_plist_rb);
+
+ radio_pdetails_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(radio_none_rb), "Packet Details");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_pdetails_rb), content == layout_pane_content_pdetails);
+ gtk_widget_set_tooltip_text (radio_pdetails_rb, "Put the packet details tree in this pane.");
+ gtk_container_add(GTK_CONTAINER(radio_vb), radio_pdetails_rb);
+
+ radio_pbytes_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(radio_none_rb), "Packet Bytes");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_pbytes_rb), content == layout_pane_content_pbytes);
+ gtk_widget_set_tooltip_text (radio_pbytes_rb, "Put the packet bytes dump in this pane.");
+ gtk_container_add(GTK_CONTAINER(radio_vb), radio_pbytes_rb);
+
+ g_object_set_data(G_OBJECT(radio_vb), LAYOUT_NONE_RB_KEY, radio_none_rb);
+ g_object_set_data(G_OBJECT(radio_vb), LAYOUT_PLIST_RB_KEY, radio_plist_rb);
+ g_object_set_data(G_OBJECT(radio_vb), LAYOUT_PDETAILS_RB_KEY, radio_pdetails_rb);
+ g_object_set_data(G_OBJECT(radio_vb), LAYOUT_PBYTES_RB_KEY, radio_pbytes_rb);
+
+ g_signal_connect(radio_none_rb, "toggled", G_CALLBACK(layout_validate_cb), main_vb);
+ g_signal_connect(radio_plist_rb, "toggled", G_CALLBACK(layout_validate_cb), main_vb);
+ g_signal_connect(radio_pdetails_rb, "toggled", G_CALLBACK(layout_validate_cb), main_vb);
+ g_signal_connect(radio_pbytes_rb, "toggled", G_CALLBACK(layout_validate_cb), main_vb);
+
+ return radio_vb;
+}
+
+static void
+layout_type_changed_cb (GtkToggleButton * togglebutton, gpointer user_data)
+{
+ GtkWidget ** layout_type_buttons = (GtkWidget**) user_data;
+ static gboolean dampen_feedback_loop = FALSE;
+
+ if (!dampen_feedback_loop) {
+ int i;
+ dampen_feedback_loop = TRUE;
+ for (i=0; i<LAYOUT_QTY; ++i) {
+ GtkToggleButton * tb = GTK_TOGGLE_BUTTON(layout_type_buttons[i]);
+ gboolean active = togglebutton==tb;
+ if (gtk_toggle_button_get_active(tb) != active)
+ gtk_toggle_button_set_active (tb, active);
+ }
+ dampen_feedback_loop = FALSE;
+ }
+}
+
+
+static layout_pane_content_e layout_pane_get_content(GtkWidget * radio_vb) {
+
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g_object_get_data(G_OBJECT(radio_vb), LAYOUT_NONE_RB_KEY))))
+ return layout_pane_content_none;
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g_object_get_data(G_OBJECT(radio_vb), LAYOUT_PLIST_RB_KEY))))
+ return layout_pane_content_plist;
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g_object_get_data(G_OBJECT(radio_vb), LAYOUT_PDETAILS_RB_KEY))))
+ return layout_pane_content_pdetails;
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(g_object_get_data(G_OBJECT(radio_vb), LAYOUT_PBYTES_RB_KEY))))
+ return layout_pane_content_pbytes;
+
+ g_assert_not_reached();
+ return -1;
+}
+
+static void layout_pane_set_content(GtkWidget * radio_vb, layout_pane_content_e pane_content) {
+
+
+ switch(pane_content) {
+ case(layout_pane_content_none):
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g_object_get_data(G_OBJECT(radio_vb), LAYOUT_NONE_RB_KEY)), TRUE);
+ break;
+ case(layout_pane_content_plist):
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g_object_get_data(G_OBJECT(radio_vb), LAYOUT_PLIST_RB_KEY)), TRUE);
+ break;
+ case(layout_pane_content_pdetails):
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g_object_get_data(G_OBJECT(radio_vb), LAYOUT_PDETAILS_RB_KEY)), TRUE);
+ break;
+ case(layout_pane_content_pbytes):
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g_object_get_data(G_OBJECT(radio_vb), LAYOUT_PBYTES_RB_KEY)), TRUE);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+
+
+static void layout_set(GtkWidget * main_vb, layout_t *layout) {
+ GtkWidget *radio_vb;
+ GtkWidget ** layout_type_buttons = g_object_get_data(G_OBJECT(main_vb), LAYOUT_TYPE_BUTTONS_KEY);
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(layout_type_buttons[layout->type - 1]), TRUE);
+
+ radio_vb = g_object_get_data(G_OBJECT(main_vb), LAYOUT_CONTENT1_VB_KEY);
+ layout_pane_set_content(radio_vb, layout->content[0]);
+ radio_vb = g_object_get_data(G_OBJECT(main_vb), LAYOUT_CONTENT2_VB_KEY);
+ layout_pane_set_content(radio_vb, layout->content[1]);
+ radio_vb = g_object_get_data(G_OBJECT(main_vb), LAYOUT_CONTENT3_VB_KEY);
+ layout_pane_set_content(radio_vb, layout->content[2]);
+}
+
+static void layout_get(GtkWidget * main_vb, layout_t *layout_out) {
+ GtkWidget *radio_vb;
+ GtkWidget ** layout_type_buttons = g_object_get_data(G_OBJECT(main_vb), LAYOUT_TYPE_BUTTONS_KEY);
+ int i;
+
+ for (i=0; i<LAYOUT_QTY; ++i) {
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(layout_type_buttons[i]))) {
+ layout_out->type = i + 1;
+ break;
+ }
+ }
+
+ radio_vb = g_object_get_data(G_OBJECT(main_vb), LAYOUT_CONTENT1_VB_KEY);
+ layout_out->content[0] = layout_pane_get_content(radio_vb);
+ radio_vb = g_object_get_data(G_OBJECT(main_vb), LAYOUT_CONTENT2_VB_KEY);
+ layout_out->content[1] = layout_pane_get_content(radio_vb);
+ radio_vb = g_object_get_data(G_OBJECT(main_vb), LAYOUT_CONTENT3_VB_KEY);
+ layout_out->content[2] = layout_pane_get_content(radio_vb);
+}
+
+static void layout_validate(layout_t *layout) {
+
+ if(layout->content[1] == layout->content[0]) {
+ layout->content[1] = layout_pane_content_none;
+ }
+ if(layout->content[2] == layout->content[0] || layout->content[2] == layout->content[1]) {
+ layout->content[2] = layout_pane_content_none;
+ }
+}
+
+
+static void layout_validate_cb(GtkWidget *w _U_, gpointer data) {
+ layout_t layout;
+
+ layout_get(data, &layout);
+ layout_validate(&layout);
+ layout_set(data, &layout);
+}
+
+static void
+layout_defaults_cb (GtkWidget * w _U_, gpointer data)
+{
+ layout_t default_layout = {
+ layout_type_5,
+ {
+ layout_pane_content_plist,
+ layout_pane_content_pdetails,
+ layout_pane_content_pbytes
+ }
+ };
+
+ layout_set(data, &default_layout);
+}
+
+#define SCROLLBAR_PLACEMENT_KEY "scrollbar_placement"
+#define ALTERN_COLORS_KEY "altern_colors"
+#define HEX_DUMP_HIGHLIGHT_STYLE_KEY "hex_dump_highlight_style"
+#define FILTER_TOOLBAR_PLACEMENT_KEY "filter_toolbar_show_in_statusbar"
+#define GUI_TOOLBAR_STYLE_KEY "toolbar_style"
+#define GUI_FILTER_TOOLBAR_STYLE_KEY "filter_toolbar_style"
+#define GUI_WINDOW_TITLE_KEY "window_title"
+
+
+#define GUI_TABLE_ROWS 6
+
+static const enum_val_t scrollbar_placement_vals[] = {
+ { "FALSE", "Left", FALSE },
+ { "TRUE", "Right", TRUE },
+ { NULL, NULL, 0 }
+};
+static const enum_val_t altern_colors_vals[] = {
+ { "FALSE", "No", FALSE },
+ { "TRUE", "Yes", TRUE },
+ { NULL, NULL, 0 }
+};
+static const enum_val_t highlight_style_vals[] = {
+ { "FALSE", "Bold", FALSE },
+ { "TRUE", "Inverse", TRUE },
+ { NULL, NULL, 0 }
+};
+static const enum_val_t filter_toolbar_placement_vals[] = {
+ { "FALSE", "Below the main toolbar", FALSE },
+ { "TRUE", "Insert into statusbar", TRUE },
+ { NULL, NULL, 0 }
+};
+static const enum_val_t toolbar_style_vals[] = {
+ { "ICONS", "Icons only", TB_STYLE_ICONS },
+ { "TEXT", "Text only", TB_STYLE_TEXT },
+ { "BOTH", "Icons & Text", TB_STYLE_BOTH },
+ { NULL, NULL, 0 }
+};
+
+GtkWidget*
+layout_prefs_show(void)
+{
+ GtkWidget *main_vb, *button_hb, *type_tb;
+ GtkWidget *pane_fr, *pane_vb;
+ GtkWidget *radio_hb, *radio_vb;
+ GtkWidget *default_vb, *default_bt;
+ GtkWidget *main_tb, *hbox;
+ GtkWidget *scrollbar_om;
+ GtkWidget *altern_colors_om;
+ GtkWidget *highlight_style_om;
+ GtkWidget *toolbar_style_om;
+ GtkWidget *filter_toolbar_style_om;
+ GtkWidget *filter_toolbar_placement_om;
+ GtkWidget *window_title_te;
+
+ const char ** inline_txt [LAYOUT_QTY] = {
+ icon_layout_5_xpm, icon_layout_2_xpm, icon_layout_1_xpm,
+ icon_layout_4_xpm, icon_layout_3_xpm, icon_layout_6_xpm };
+ GtkWidget ** layout_type_buttons = g_malloc (sizeof(GtkWidget*) * LAYOUT_QTY);
+
+ int pos = 0;
+ int i;
+
+
+ /* main vertical box */
+ main_vb = gtk_vbox_new(FALSE, 7);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+
+ /* pane frame */
+ pane_fr = gtk_frame_new("Panes");
+ gtk_box_pack_start(GTK_BOX(main_vb), pane_fr, FALSE, FALSE, 0);
+ gtk_widget_show(pane_fr);
+
+ /* pane vertical box */
+ pane_vb = gtk_vbox_new(FALSE, 7);
+ gtk_container_set_border_width(GTK_CONTAINER(pane_vb), 5);
+ gtk_container_add(GTK_CONTAINER(pane_fr), pane_vb);
+ gtk_widget_show(pane_vb);
+
+ /* button hbox */
+ button_hb = gtk_hbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(button_hb), 6);
+ gtk_box_pack_start (GTK_BOX(pane_vb), button_hb, FALSE, FALSE, 0);
+
+ /* pane layout */
+ for (i=0; i<LAYOUT_QTY; ++i)
+ {
+ type_tb = gtk_toggle_button_new ();
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(type_tb),
+ (layout_type_e)(i + 1) == prefs.gui_layout_type);
+
+ gtk_container_add (GTK_CONTAINER(type_tb), xpm_to_widget(inline_txt[i]));
+
+ g_signal_connect(type_tb, "toggled", G_CALLBACK(layout_type_changed_cb), layout_type_buttons);
+ layout_type_buttons[i] = type_tb;
+ gtk_box_pack_start (GTK_BOX(button_hb), type_tb, TRUE, FALSE, 0);
+ }
+
+ g_object_set_data(G_OBJECT(main_vb), LAYOUT_TYPE_BUTTONS_KEY, layout_type_buttons);
+
+ /* radio hbox */
+ radio_hb = gtk_hbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(radio_hb), 6);
+ gtk_box_pack_start (GTK_BOX(pane_vb), radio_hb, FALSE, FALSE, 0);
+
+ radio_vb = layout_content_radio_vbox(main_vb, 1, prefs.gui_layout_content_1);
+ gtk_container_set_border_width(GTK_CONTAINER(radio_vb), 6);
+ gtk_box_pack_start (GTK_BOX(radio_hb), radio_vb, FALSE, FALSE, 0);
+ g_object_set_data(G_OBJECT(main_vb), LAYOUT_CONTENT1_VB_KEY, radio_vb);
+
+ radio_vb = layout_content_radio_vbox(main_vb, 2, prefs.gui_layout_content_2);
+ gtk_container_set_border_width(GTK_CONTAINER(radio_vb), 6);
+ gtk_box_pack_start (GTK_BOX(radio_hb), radio_vb, FALSE, FALSE, 0);
+ g_object_set_data(G_OBJECT(main_vb), LAYOUT_CONTENT2_VB_KEY, radio_vb);
+
+ radio_vb = layout_content_radio_vbox(main_vb, 3, prefs.gui_layout_content_3);
+ gtk_container_set_border_width(GTK_CONTAINER(radio_vb), 6);
+ gtk_box_pack_start (GTK_BOX(radio_hb), radio_vb, FALSE, FALSE, 0);
+ g_object_set_data(G_OBJECT(main_vb), LAYOUT_CONTENT3_VB_KEY, radio_vb);
+
+ default_vb = gtk_vbox_new(FALSE, 0);
+ default_bt = gtk_button_new_with_label("Default panes");
+ gtk_widget_set_tooltip_text (default_bt, "Reset the pane layout settings to default values.");
+ g_signal_connect(default_bt, "clicked", G_CALLBACK(layout_defaults_cb), main_vb);
+ gtk_box_pack_end(GTK_BOX(default_vb), default_bt, FALSE, FALSE, 0);
+ gtk_box_pack_end(GTK_BOX(radio_hb), default_vb, FALSE, FALSE, 0);
+
+ /* Main horizontal box */
+ /* XXX - Is there a better way to center the table? */
+ hbox = gtk_hbox_new(FALSE, 7);
+ gtk_box_pack_start (GTK_BOX(main_vb), hbox, TRUE, FALSE, 0);
+
+ /* Main table */
+ main_tb = gtk_table_new(GUI_TABLE_ROWS, 2, FALSE);
+ gtk_box_pack_start( GTK_BOX(hbox), main_tb, FALSE, FALSE, 0 );
+ gtk_table_set_row_spacings( GTK_TABLE(main_tb), 10 );
+ gtk_table_set_col_spacings( GTK_TABLE(main_tb), 15 );
+
+ /* Scrollbar placement */
+ scrollbar_om = create_preference_option_menu(main_tb, pos++,
+ "Vertical scrollbar placement:",
+ "Select where the vertical scrollbar will be displayed in the panes.",
+ scrollbar_placement_vals, prefs.gui_scrollbar_on_right);
+ g_object_set_data(G_OBJECT(main_vb), SCROLLBAR_PLACEMENT_KEY, scrollbar_om);
+
+ /* Alternating row colors in list and tree views */
+ altern_colors_om = create_preference_option_menu(main_tb, pos++,
+ "Alternating row colors in lists and trees:",
+ "Select whether or not the rows of lists and trees have alternating color.",
+ altern_colors_vals, prefs.gui_altern_colors);
+ g_object_set_data(G_OBJECT(main_vb), ALTERN_COLORS_KEY, altern_colors_om);
+
+ /* Packet Bytes Dump highlight style */
+ highlight_style_om = create_preference_option_menu(main_tb, pos++,
+ "Packet bytes highlight style:",
+ "Select the style in which the packet bytes dump will be displayed.",
+ highlight_style_vals, prefs.gui_hex_dump_highlight_style);
+ g_object_set_data(G_OBJECT(main_vb), HEX_DUMP_HIGHLIGHT_STYLE_KEY, highlight_style_om);
+
+ /* Toolbar prefs */
+ toolbar_style_om = create_preference_option_menu(main_tb, pos++,
+ "Toolbar style:",
+ "Select the style in which the toolbar will be displayed.",
+ toolbar_style_vals, prefs.gui_toolbar_main_style);
+ g_object_set_data(G_OBJECT(main_vb), GUI_TOOLBAR_STYLE_KEY, toolbar_style_om);
+
+ /* Filter toolbar prefs */
+ filter_toolbar_style_om = create_preference_option_menu(main_tb, pos++,
+ "Filter toolbar style:",
+ "Select the style in which the filter toolbar will be displayed.",
+ toolbar_style_vals, prefs.gui_toolbar_filter_style);
+ g_object_set_data(G_OBJECT(main_vb), GUI_FILTER_TOOLBAR_STYLE_KEY, filter_toolbar_style_om);
+
+ /* Placement of Filter toolbar */
+ filter_toolbar_placement_om = create_preference_option_menu(main_tb, pos++,
+ "Filter toolbar placement:",
+ "Select where the filter toolbar will be displayed.",
+ filter_toolbar_placement_vals, prefs.filter_toolbar_show_in_statusbar);
+ g_object_set_data(G_OBJECT(main_vb), FILTER_TOOLBAR_PLACEMENT_KEY, filter_toolbar_placement_om);
+
+ /* Window title */
+ window_title_te = create_preference_entry(main_tb, pos++,
+ "Custom window title (appended to existing titles):",
+ "Enter the text to be appended to the window title.",
+ prefs.gui_window_title);
+ gtk_entry_set_text(GTK_ENTRY(window_title_te), prefs.gui_window_title);
+ g_object_set_data(G_OBJECT(main_vb), GUI_WINDOW_TITLE_KEY, window_title_te);
+
+ /* Show 'em what we got */
+ gtk_widget_show_all(main_vb);
+
+ return(main_vb);
+}
+
+static gint
+fetch_enum_value(gpointer control, const enum_val_t *enumvals)
+{
+ return fetch_preference_option_menu_val(GTK_WIDGET(control), enumvals);
+}
+
+void
+layout_prefs_fetch(GtkWidget *w)
+{
+ layout_t layout_fetched;
+
+ layout_get(w, &layout_fetched);
+
+ prefs.gui_layout_type = layout_fetched.type;
+ prefs.gui_layout_content_1 = layout_fetched.content[0];
+ prefs.gui_layout_content_2 = layout_fetched.content[1];
+ prefs.gui_layout_content_3 = layout_fetched.content[2];
+
+ prefs.gui_scrollbar_on_right = fetch_enum_value(
+ g_object_get_data(G_OBJECT(w), SCROLLBAR_PLACEMENT_KEY),
+ scrollbar_placement_vals);
+
+ prefs.gui_altern_colors = fetch_enum_value(
+ g_object_get_data(G_OBJECT(w), ALTERN_COLORS_KEY), altern_colors_vals);
+ prefs.filter_toolbar_show_in_statusbar = fetch_enum_value(
+ g_object_get_data(G_OBJECT(w), FILTER_TOOLBAR_PLACEMENT_KEY), filter_toolbar_placement_vals);
+ prefs.gui_hex_dump_highlight_style = fetch_enum_value(
+ g_object_get_data(G_OBJECT(w), HEX_DUMP_HIGHLIGHT_STYLE_KEY), highlight_style_vals);
+ prefs.gui_toolbar_main_style = fetch_enum_value(
+ g_object_get_data(G_OBJECT(w), GUI_TOOLBAR_STYLE_KEY), toolbar_style_vals);
+ prefs.gui_toolbar_filter_style = fetch_enum_value(
+ g_object_get_data(G_OBJECT(w), GUI_FILTER_TOOLBAR_STYLE_KEY), toolbar_style_vals);
+
+ g_free(prefs.gui_window_title);
+ prefs.gui_window_title = g_strdup(gtk_entry_get_text(
+ GTK_ENTRY(g_object_get_data(G_OBJECT(w), GUI_WINDOW_TITLE_KEY))));
+}
+
+void
+layout_prefs_apply(GtkWidget *w _U_)
+{
+ update_main_window_title();
+ main_widgets_rearrange();
+}
+
+void
+layout_prefs_destroy(GtkWidget *main_vb)
+{
+ GtkWidget ** layout_type_buttons = g_object_get_data(G_OBJECT(main_vb), LAYOUT_TYPE_BUTTONS_KEY);
+
+ g_free(layout_type_buttons);
+}
+
diff --git a/ui/gtk/prefs_layout.h b/ui/gtk/prefs_layout.h
new file mode 100644
index 0000000000..427b1be591
--- /dev/null
+++ b/ui/gtk/prefs_layout.h
@@ -0,0 +1,57 @@
+/* prefs_layout.h
+ * Definitions for layout preferences window
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __LAYOUT_PREFS_H__
+#define __LAYOUT_PREFS_H__
+
+/** @file
+ * "User Interface: Layout" preferences page.
+ * @ingroup prefs_group
+ */
+
+/** Build a User interface layout preferences page.
+ *
+ * @return the new preferences page
+ */
+extern GtkWidget *layout_prefs_show(void);
+
+/** Fetch preference values from page.
+ *
+ * @param widget widget from layout_prefs_show()
+ */
+extern void layout_prefs_fetch(GtkWidget *widget);
+
+/** Apply preference values from page.
+ *
+ * @param widget widget from layout_prefs_show()
+ */
+extern void layout_prefs_apply(GtkWidget *widget);
+
+/** Destroy preference values from page.
+ *
+ * @param widget widget from layout_prefs_show()
+ */
+extern void layout_prefs_destroy(GtkWidget *widget);
+
+#endif
diff --git a/ui/gtk/prefs_nameres.c b/ui/gtk/prefs_nameres.c
new file mode 100644
index 0000000000..8869617948
--- /dev/null
+++ b/ui/gtk/prefs_nameres.c
@@ -0,0 +1,307 @@
+/* nameres_prefs.c
+ * Dialog box for name resolution preferences
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <epan/addr_resolv.h>
+#include <epan/prefs.h>
+#include <epan/uat.h>
+#include <epan/oids.h>
+
+#include "ui/gtk/prefs_nameres.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/prefs_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/menus.h"
+
+
+#define M_RESOLVE_KEY "m_resolve"
+#define N_RESOLVE_KEY "n_resolve"
+#define T_RESOLVE_KEY "t_resolve"
+
+#if defined(HAVE_C_ARES) || defined(HAVE_GNU_ADNS)
+# define C_RESOLVE_KEY "c_resolve"
+# define RESOLVE_CONCURRENCY_KEY "resolve_concurrency"
+#endif /* HAVE_C_ARES || HAVE_GNU_ADNS */
+
+#define BASE_TABLE_ROWS 3
+
+#if defined(HAVE_C_ARES) || defined(HAVE_GNU_ADNS)
+# define ASYNC_TABLE_ROWS 2
+#else
+# define ASYNC_TABLE_ROWS 0
+#endif
+
+#ifdef HAVE_LIBSMI
+# define SUPPRESS_SMI_ERRORS_KEY "suppress_smi_errors"
+# define LOAD_SMI_MODULES_KEY "load_smi_modules"
+# define SMI_TABLE_ROWS 2
+#else
+# define SMI_TABLE_ROWS 0
+#endif
+
+#ifdef HAVE_GEOIP
+# define GEOIP_TABLE_ROWS 1
+#else
+# define GEOIP_TABLE_ROWS 0
+#endif
+
+#define RESOLV_TABLE_ROWS (\
+ BASE_TABLE_ROWS + \
+ ASYNC_TABLE_ROWS + \
+ SMI_TABLE_ROWS + \
+ GEOIP_TABLE_ROWS \
+ )
+
+GtkWidget*
+nameres_prefs_show(void)
+{
+ guint table_row;
+ GtkWidget *main_tb, *main_vb;
+ GtkWidget *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
+#if defined(HAVE_C_ARES) || defined(HAVE_GNU_ADNS)
+ GtkWidget *c_resolv_cb;
+ GtkWidget *resolv_concurrency_te;
+ char concur_str[10+1];
+#endif /* HAVE_C_ARES || HAVE_GNU_ADNS */
+#ifdef HAVE_LIBSMI
+ GtkWidget *load_smi_modules_cb, *suppress_smi_errors_cb;
+ uat_t *smi_paths_uat;
+ uat_t *smi_modules_uat;
+#endif
+#ifdef HAVE_GEOIP
+ uat_t *geoip_db_paths_uat;
+#endif
+ /*
+ * XXX - it would be nice if the current setting of the resolver
+ * flags could be different from the preference flags, so that
+ * the preference flags would represent what the user *typically*
+ * wants, but they could override them for particular captures
+ * without a subsequent editing of the preferences recording the
+ * temporary settings as permanent preferences.
+ */
+ prefs.name_resolve = gbl_resolv_flags;
+
+ /* Main vertical box */
+ main_vb = gtk_vbox_new(FALSE, 7);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+
+ /* Main table */
+ main_tb = gtk_table_new(RESOLV_TABLE_ROWS, 3, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
+ gtk_widget_show(main_tb);
+
+ /* Resolve MAC addresses */
+ table_row = 0;
+ m_resolv_cb = create_preference_check_button(main_tb, table_row,
+ "Enable MAC name resolution:", "e.g. Ethernet address to manufacturer name",
+ prefs.name_resolve & RESOLV_MAC);
+ g_object_set_data(G_OBJECT(main_vb), M_RESOLVE_KEY, m_resolv_cb);
+
+ /* Resolve network addresses */
+ table_row++;
+ n_resolv_cb = create_preference_check_button(main_tb, table_row,
+ "Enable network name resolution:", "e.g. IP address to DNS name (hostname)",
+ prefs.name_resolve & RESOLV_NETWORK);
+ g_object_set_data(G_OBJECT(main_vb), N_RESOLVE_KEY, n_resolv_cb);
+
+ /* Resolve transport addresses */
+ table_row++;
+ t_resolv_cb = create_preference_check_button(main_tb, table_row,
+ "Enable transport name resolution:", "e.g. TCP/UDP port to service name",
+ prefs.name_resolve & RESOLV_TRANSPORT);
+ g_object_set_data(G_OBJECT(main_vb), T_RESOLVE_KEY, t_resolv_cb);
+
+#if defined(HAVE_C_ARES) || defined(HAVE_GNU_ADNS)
+ /* Enable concurrent (asynchronous) DNS lookups */
+ table_row++;
+ c_resolv_cb = create_preference_check_button(main_tb, table_row,
+ "Enable concurrent DNS name resolution:", "be sure to enable network name resolution",
+ prefs.name_resolve & RESOLV_CONCURRENT);
+ g_object_set_data(G_OBJECT(main_vb), C_RESOLVE_KEY, c_resolv_cb);
+
+ /* Max concurrent requests */
+ table_row++;
+ g_snprintf(concur_str, sizeof(concur_str), "%d", prefs.name_resolve_concurrency);
+ resolv_concurrency_te = create_preference_entry(main_tb, table_row,
+ "Maximum concurrent requests:", "maximum parallel running DNS requests", concur_str);
+ g_object_set_data(G_OBJECT(main_vb), RESOLVE_CONCURRENCY_KEY, resolv_concurrency_te);
+
+#else /* HAVE_C_ARES || HAVE_GNU_ADNS */
+ table_row++;
+ create_preference_static_text(main_tb, table_row,
+ "Enable concurrent DNS name resolution: N/A",
+ "Support for this feature was not compiled into this version of Wireshark");
+#endif /* HAVE_C_ARES || HAVE_GNU_ADNS */
+#ifdef HAVE_LIBSMI
+ /* Enable OID resolution */
+ table_row++;
+ load_smi_modules_cb = create_preference_check_button(main_tb, table_row,
+ "Enable OID resolution:", "You must restart Wireshark for this change to"
+ " take effect. [If True the 'SMI paths' and 'SMI modules' preferences will be shown].",
+ prefs.load_smi_modules);
+ g_object_set_data(G_OBJECT(main_vb), LOAD_SMI_MODULES_KEY, load_smi_modules_cb);
+
+ /* Suppress smi errors */
+ table_row++;
+ suppress_smi_errors_cb = create_preference_check_button(main_tb, table_row,
+ "Suppress SMI errors:", "Some errors can be ignored. If unsure, set to false.",
+ prefs.suppress_smi_errors);
+ g_object_set_data(G_OBJECT(main_vb), SUPPRESS_SMI_ERRORS_KEY, suppress_smi_errors_cb);
+
+ /* SMI paths UAT */
+ smi_paths_uat = uat_get_table_by_name("SMI Paths");
+ if (smi_paths_uat) {
+ table_row++;
+ create_preference_uat(main_tb, table_row,
+ "SMI (MIB and PIB) paths",
+ "Search paths for SMI (MIB and PIB) modules. You must\n"
+ "restart Wireshark for these changes to take effect.",
+ smi_paths_uat);
+ }
+
+ /* SMI modules UAT */
+ smi_modules_uat = uat_get_table_by_name("SMI Modules");
+ if (smi_modules_uat) {
+ table_row++;
+ create_preference_uat(main_tb, table_row,
+ "SMI (MIB and PIB) modules",
+ "List of enabled SMI (MIB and PIB) modules. You must\n"
+ "restart Wireshark for these changes to take effect.",
+ smi_modules_uat);
+ }
+#else /* HAVE_LIBSMI */
+ table_row++;
+ create_preference_static_text(main_tb, table_row,
+ "SMI (MIB and PIB) modules and paths: N/A",
+ "Support for this feature was not compiled into this version of Wireshark");
+ table_row++;
+ create_preference_static_text(main_tb, table_row,
+ "Enable OID resolution: N/A",
+ "Support for this feature was not compiled into this version of Wireshark");
+ table_row++;
+ create_preference_static_text(main_tb, table_row,
+ "Suppress SMI errors: N/A",
+ "Support for this feature was not compiled into this version of Wireshark");
+#endif /* HAVE_LIBSMI */
+
+#ifdef HAVE_GEOIP
+ /* GeoIP databases http://www.maxmind.com/app/api */
+ geoip_db_paths_uat = uat_get_table_by_name("GeoIP Database Paths");
+
+ if (geoip_db_paths_uat) {
+ table_row++;
+ create_preference_uat(main_tb, table_row,
+ "GeoIP database directories",
+ "Search paths for GeoIP address mapping databases.\n"
+ "Wireshark will look in each directory for files beginning\n"
+ "with \"Geo\" and ending with \".dat\".\n"
+ "You must restart Wireshark for these changes to take\n"
+ "effect.",
+ geoip_db_paths_uat);
+ }
+#else /* HAVE_GEOIP */
+ table_row++;
+ create_preference_static_text(main_tb, table_row,
+ "GeoIP database search paths: N/A",
+ "Support for this feature was not compiled into this version of Wireshark");
+#endif /* HAVE_GEOIP */
+
+ /* Show 'em what we got */
+ gtk_widget_show_all(main_vb);
+
+ return(main_vb);
+}
+
+void
+nameres_prefs_fetch(GtkWidget *w)
+{
+ GtkWidget *m_resolv_cb, *n_resolv_cb, *t_resolv_cb;
+#if defined(HAVE_C_ARES) || defined(HAVE_GNU_ADNS)
+ GtkWidget *c_resolv_cb, *resolv_concurrency_te;
+#endif /* HAVE_C_ARES || HAVE_GNU_ADNS */
+#ifdef HAVE_LIBSMI
+ GtkWidget *load_smi_modules_cb, *suppress_smi_errors_cb;
+ gboolean load_smi_modules_orig;
+#endif
+
+ m_resolv_cb = (GtkWidget *)g_object_get_data(G_OBJECT(w), M_RESOLVE_KEY);
+ n_resolv_cb = (GtkWidget *)g_object_get_data(G_OBJECT(w), N_RESOLVE_KEY);
+ t_resolv_cb = (GtkWidget *)g_object_get_data(G_OBJECT(w), T_RESOLVE_KEY);
+#if defined(HAVE_C_ARES) || defined(HAVE_GNU_ADNS)
+ c_resolv_cb = (GtkWidget *)g_object_get_data(G_OBJECT(w), C_RESOLVE_KEY);
+
+ resolv_concurrency_te = (GtkWidget *)g_object_get_data(G_OBJECT(w), RESOLVE_CONCURRENCY_KEY);
+#endif /* HAVE_C_ARES || HAVE_GNU_ADNS */
+
+ prefs.name_resolve = RESOLV_NONE;
+ prefs.name_resolve |= (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (m_resolv_cb)) ? RESOLV_MAC : RESOLV_NONE);
+ prefs.name_resolve |= (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (n_resolv_cb)) ? RESOLV_NETWORK : RESOLV_NONE);
+ prefs.name_resolve |= (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (t_resolv_cb)) ? RESOLV_TRANSPORT : RESOLV_NONE);
+#if defined(HAVE_C_ARES) || defined(HAVE_GNU_ADNS)
+ prefs.name_resolve |= (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (c_resolv_cb)) ? RESOLV_CONCURRENT : RESOLV_NONE);
+
+ prefs.name_resolve_concurrency = strtol (gtk_entry_get_text(
+ GTK_ENTRY(resolv_concurrency_te)), NULL, 10);
+#endif /* HAVE_C_ARES || HAVE_GNU_ADNS */
+#ifdef HAVE_LIBSMI
+ load_smi_modules_orig = prefs.load_smi_modules;
+ load_smi_modules_cb = (GtkWidget *)g_object_get_data(G_OBJECT(w), LOAD_SMI_MODULES_KEY);
+ prefs.load_smi_modules = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (load_smi_modules_cb));
+ suppress_smi_errors_cb = (GtkWidget *)g_object_get_data(G_OBJECT(w), SUPPRESS_SMI_ERRORS_KEY);
+ prefs.suppress_smi_errors = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (suppress_smi_errors_cb));
+
+ /* Perform actions needed when enabling/disabling OID resolution */
+ if (load_smi_modules_orig && !prefs.load_smi_modules) {
+ prefs.load_smi_modules = TRUE; /* hack to make oids_cleanup() actually do something */
+ oids_cleanup();
+ prefs.load_smi_modules = FALSE; /* end hack */
+ } else if (!load_smi_modules_orig && prefs.load_smi_modules) {
+ oids_init();
+ }
+#endif
+}
+
+void
+nameres_prefs_apply(GtkWidget *w _U_)
+{
+ /*
+ * XXX - force a regeneration of the protocol list if this has
+ * changed?
+ */
+ gbl_resolv_flags = prefs.name_resolve;
+ menu_name_resolution_changed();
+}
+
+void
+nameres_prefs_destroy(GtkWidget *w _U_)
+{
+}
diff --git a/ui/gtk/prefs_nameres.h b/ui/gtk/prefs_nameres.h
new file mode 100644
index 0000000000..b25995fc56
--- /dev/null
+++ b/ui/gtk/prefs_nameres.h
@@ -0,0 +1,57 @@
+/* nameres_prefs.h
+ * Definitions for name resolution preferences window
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __NAMERES_PREFS_H__
+#define __NAMERES_PREFS_H__
+
+/** @file
+ * "Name resolution" preferences page.
+ * @ingroup prefs_group
+ */
+
+/** Build a Name resolution preferences page.
+ *
+ * @return the new preferences page
+ */
+GtkWidget *nameres_prefs_show(void);
+
+/** Fetch preference values from page.
+ *
+ * @param widget widget from nameres_prefs_show()
+ */
+void nameres_prefs_fetch(GtkWidget *widget);
+
+/** Apply preference values from page.
+ *
+ * @param widget widget from nameres_prefs_show()
+ */
+void nameres_prefs_apply(GtkWidget *widget);
+
+/** Destroy preference values from page.
+ *
+ * @param widget widget from nameres_prefs_show()
+ */
+void nameres_prefs_destroy(GtkWidget *widget);
+
+#endif
diff --git a/ui/gtk/prefs_print.c b/ui/gtk/prefs_print.c
new file mode 100644
index 0000000000..b4e701897a
--- /dev/null
+++ b/ui/gtk/prefs_print.c
@@ -0,0 +1,183 @@
+/* print_prefs.c
+ * Dialog boxes for preferences for printing
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <errno.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/prefs.h>
+
+#include "../print.h"
+#include "../util.h"
+
+#include "ui/gtk/prefs_print.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/prefs_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/file_dlg.h"
+#include "ui/gtk/capture_file_dlg.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/gtkglobals.h"
+
+
+static void printer_browse_file_cb(GtkWidget *file_bt, GtkWidget *file_te);
+
+#define E_PRINT_FORMAT_KEY "print_format"
+#define E_PRINT_DESTINATION_KEY "print_destination"
+
+static const enum_val_t print_format_vals[] = {
+ { "text", "Plain Text", PR_FMT_TEXT },
+ { "postscript", "Postscript", PR_FMT_PS },
+ { NULL, NULL, 0 }
+};
+
+static const enum_val_t print_dest_vals[] = {
+#ifdef _WIN32
+ /* "PR_DEST_CMD" means "to printer" on Windows */
+ { "command", "Printer", PR_DEST_CMD },
+#else
+ { "command", "Command", PR_DEST_CMD },
+#endif
+ { "file", "File", PR_DEST_FILE },
+ { NULL, NULL, 0 }
+};
+
+GtkWidget * printer_prefs_show(void)
+{
+ GtkWidget *main_vb, *main_tb, *button;
+#ifndef _WIN32
+ GtkWidget *cmd_te;
+#endif
+ GtkWidget *file_lb_hb, *file_lb, *file_bt_hb, *file_bt, *file_te;
+
+ /* Enclosing containers for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+
+ main_tb = gtk_table_new(4, 2, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
+ gtk_widget_show(main_tb);
+
+ /* Output format */
+ button = create_preference_radio_buttons(main_tb, 0, "Format:",
+ NULL, print_format_vals, prefs.pr_format);
+ g_object_set_data(G_OBJECT(main_vb), E_PRINT_FORMAT_KEY, button);
+
+ /* Output destination */
+ button = create_preference_radio_buttons(main_tb, 1, "Print to:",
+ NULL, print_dest_vals, prefs.pr_dest);
+ g_object_set_data(G_OBJECT(main_vb), E_PRINT_DESTINATION_KEY,
+ button);
+
+#ifndef _WIN32
+ /* Command text entry */
+ cmd_te = create_preference_entry(main_tb, 2, "Command:", NULL,
+ prefs.pr_cmd);
+ g_object_set_data(G_OBJECT(main_vb), PRINT_CMD_TE_KEY, cmd_te);
+#endif
+
+
+ file_lb_hb = gtk_hbox_new(FALSE, 0);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), file_lb_hb, 0, 1, 3, 4);
+ gtk_widget_show(file_lb_hb);
+
+ file_lb = gtk_label_new("File:");
+ gtk_box_pack_end(GTK_BOX(file_lb_hb), file_lb, FALSE, FALSE, 0);
+ gtk_widget_show(file_lb);
+
+ /* File button and text entry */
+ file_bt_hb = gtk_hbox_new(FALSE, 0);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), file_bt_hb, 1, 2, 3, 4);
+ gtk_widget_show(file_bt_hb);
+
+ file_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_BROWSE);
+ gtk_box_pack_end(GTK_BOX(file_bt_hb), file_bt, FALSE, FALSE, 0);
+ gtk_widget_show(file_bt);
+
+ file_te = gtk_entry_new();
+ g_object_set_data(G_OBJECT(main_vb), PRINT_FILE_TE_KEY, file_te);
+ if (prefs.pr_file) gtk_entry_set_text(GTK_ENTRY(file_te), prefs.pr_file);
+ gtk_box_pack_start(GTK_BOX(file_bt_hb), file_te, TRUE, TRUE, 0);
+ gtk_widget_show(file_te);
+
+ g_signal_connect(file_bt, "clicked", G_CALLBACK(printer_browse_file_cb), file_te);
+
+ gtk_widget_show(main_vb);
+ return(main_vb);
+}
+
+
+static void
+printer_browse_file_cb(GtkWidget *file_bt, GtkWidget *file_te)
+{
+ file_selection_browse(file_bt, file_te, "Wireshark: Print to a File",
+ FILE_SELECTION_WRITE_BROWSE);
+}
+
+
+void
+printer_prefs_fetch(GtkWidget *w)
+{
+ prefs.pr_format = fetch_preference_radio_buttons_val(
+ g_object_get_data(G_OBJECT(w), E_PRINT_FORMAT_KEY), print_format_vals);
+
+ prefs.pr_dest = fetch_preference_radio_buttons_val(
+ g_object_get_data(G_OBJECT(w), E_PRINT_DESTINATION_KEY), print_dest_vals);
+
+#ifndef _WIN32
+ g_free(prefs.pr_cmd);
+ prefs.pr_cmd = g_strdup(gtk_entry_get_text(
+ GTK_ENTRY(g_object_get_data(G_OBJECT(w), PRINT_CMD_TE_KEY))));
+#endif
+
+ g_free(prefs.pr_file);
+ prefs.pr_file = g_strdup(gtk_entry_get_text(
+ GTK_ENTRY(g_object_get_data(G_OBJECT(w), PRINT_FILE_TE_KEY))));
+}
+
+void
+printer_prefs_apply(GtkWidget *w _U_)
+{
+}
+
+void
+printer_prefs_destroy(GtkWidget *w)
+{
+ GtkWidget *caller = gtk_widget_get_toplevel(w);
+ GtkWidget *fs;
+
+ /* Is there a file selection dialog associated with this
+ Preferences dialog? */
+ fs = g_object_get_data(G_OBJECT(caller), E_FILE_SEL_DIALOG_PTR_KEY);
+
+ if (fs != NULL) {
+ /* Yes. Destroy it. */
+ window_destroy(fs);
+ }
+}
diff --git a/ui/gtk/prefs_print.h b/ui/gtk/prefs_print.h
new file mode 100644
index 0000000000..d1af4ff953
--- /dev/null
+++ b/ui/gtk/prefs_print.h
@@ -0,0 +1,58 @@
+/* print_prefs.h
+ * Definitions for print preferences window
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PRINT_PREFS_H__
+#define __PRINT_PREFS_H__
+
+/** @file
+ * "Print" preferences page.
+ * @ingroup prefs_group
+ */
+
+/** Build a Print preferences page.
+ *
+ * @return the new preferences page
+ */
+GtkWidget *printer_prefs_show(void);
+
+/** Fetch preference values from page.
+ *
+ * @param widget widget from printer_prefs_show()
+ */
+void printer_prefs_fetch(GtkWidget *widget);
+
+/** Apply preference values from page.
+ *
+ * @param widget widget from printer_prefs_show()
+ */
+void printer_prefs_apply(GtkWidget *widget);
+
+/** Destroy preference values from page.
+ *
+ * @param widget widget from printer_prefs_show()
+ */
+void printer_prefs_destroy(GtkWidget *widget);
+
+#endif
diff --git a/ui/gtk/prefs_protocols.c b/ui/gtk/prefs_protocols.c
new file mode 100644
index 0000000000..eeb6fcd770
--- /dev/null
+++ b/ui/gtk/prefs_protocols.c
@@ -0,0 +1,88 @@
+/* prefs_protocols.c
+ * Dialog box for preferences common for all protocols
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <epan/prefs.h>
+
+#include "ui/gtk/prefs_protocols.h"
+#include "ui/gtk/prefs_dlg.h"
+
+#define PROTOCOLS_SHOW_HIDDEN_KEY "display_hidden_items"
+#define PROTOCOLS_TABLE_ROWS 1
+
+GtkWidget*
+protocols_prefs_show(void)
+{
+ GtkWidget *main_tb, *main_vb;
+ GtkWidget *display_hidden_cb;
+ int pos = 0;
+
+ /* Main vertical box */
+ main_vb = gtk_vbox_new(FALSE, 7);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+
+ /* Main table */
+ main_tb = gtk_table_new(PROTOCOLS_TABLE_ROWS, 1, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
+ gtk_widget_show(main_tb);
+
+ /* Show hidden protocol items in packet list */
+ display_hidden_cb = create_preference_check_button(main_tb, pos++,
+ "Display hidden protocol items:",
+ "Display all hidden protocol items in the packet list.",
+ prefs.display_hidden_proto_items);
+ g_object_set_data(G_OBJECT(main_vb), PROTOCOLS_SHOW_HIDDEN_KEY, display_hidden_cb);
+
+ /* Show 'em what we got */
+ gtk_widget_show_all(main_vb);
+
+ return main_vb;
+}
+
+void
+protocols_prefs_fetch(GtkWidget *w _U_)
+{
+ GtkWidget *display_hidden_cb;
+
+ display_hidden_cb = (GtkWidget *)g_object_get_data(G_OBJECT(w), PROTOCOLS_SHOW_HIDDEN_KEY);
+ prefs.display_hidden_proto_items = (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (display_hidden_cb)) ? TRUE : FALSE);
+}
+
+void
+protocols_prefs_apply(GtkWidget *w _U_)
+{
+}
+
+void
+protocols_prefs_destroy(GtkWidget *w _U_)
+{
+}
+
diff --git a/ui/gtk/prefs_protocols.h b/ui/gtk/prefs_protocols.h
new file mode 100644
index 0000000000..8856ba2b1a
--- /dev/null
+++ b/ui/gtk/prefs_protocols.h
@@ -0,0 +1,57 @@
+/* prefs_protocols.h
+ * Definitions for preferences common for all protocols
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PREFS_PROTOCOLS_H__
+#define __PREFS_PROTOCOLS_H__
+
+/** @file
+ * "Protocols" preferences page.
+ * @ingroup prefs_group
+ */
+
+/** Build a page for preferences common for all protocols
+ *
+ * @return the new preferences page
+ */
+GtkWidget *protocols_prefs_show(void);
+
+/** Fetch preference values from page.
+ *
+ * @param widget widget from protocols_prefs_show()
+ */
+void protocols_prefs_fetch(GtkWidget *widget);
+
+/** Apply preference values from page.
+ *
+ * @param widget widget from protocols_prefs_show()
+ */
+void protocols_prefs_apply(GtkWidget *widget);
+
+/** Destroy preference values from page.
+ *
+ * @param widget widget from protocols_prefs_show()
+ */
+void protocols_prefs_destroy(GtkWidget *widget);
+
+#endif
diff --git a/ui/gtk/prefs_stream.c b/ui/gtk/prefs_stream.c
new file mode 100644
index 0000000000..7dcd4d3466
--- /dev/null
+++ b/ui/gtk/prefs_stream.c
@@ -0,0 +1,228 @@
+/* stream_prefs.c
+ * Dialog boxes for preferences for the stream window
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include <epan/prefs.h>
+
+#include "../color.h"
+#include "../print.h"
+
+#include "ui/gtk/color_utils.h"
+#include "ui/gtk/prefs_stream.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/follow_tcp.h"
+#include "ui/gtk/old-gtk-compat.h"
+
+#define SAMPLE_MARKED_TEXT "Sample marked packet text\n"
+#define SAMPLE_IGNORED_TEXT "Sample ignored packet text\n"
+#define SAMPLE_CLIENT_TEXT "Sample TCP stream client text\n"
+#define SAMPLE_SERVER_TEXT "Sample TCP stream server text\n"
+#define MFG_IDX 0
+#define MBG_IDX 1
+#define IFG_IDX 2
+#define IBG_IDX 3
+#define CFG_IDX 4
+#define CBG_IDX 5
+#define SFG_IDX 6
+#define SBG_IDX 7
+#define MAX_IDX 8 /* set this to the number of IDX values */
+#define STREAM_SAMPLE_KEY "stream_entry"
+#define STREAM_CS_KEY "stream_colorselection"
+#define CS_RED 0
+#define CS_GREEN 1
+#define CS_BLUE 2
+#define CS_OPACITY 3
+
+static void update_text_color(GtkWidget *, gpointer);
+static void update_current_color(GtkWidget *, gpointer);
+
+static GdkColor tcolors[MAX_IDX], *curcolor = NULL;
+
+GtkWidget *
+stream_prefs_show(void)
+{
+ GtkWidget *main_vb, *main_tb, *label, *combo_box;
+ GtkWidget *sample, *colorsel;
+ int width, height, i;
+ const gchar *mt[] = {
+ "Marked packet foreground", /* MFG_IDX 0*/
+ "Marked packet background", /* MBG_IDX 1*/
+ "Ignored packet foreground", /* IFG_IDX 2*/
+ "Ignored packet background", /* IBG_IDX 3*/
+ "TCP stream client foreground", /* CFG_IDX 4*/
+ "TCP stream client background", /* CBG_IDX 5*/
+ "TCP stream server foreground", /* SFG_IDX 6*/
+ "TCP stream server background" /* SBG_IDX 7*/
+ };
+ int mcount = sizeof(mt) / sizeof (gchar *);
+ GtkTextBuffer *buf;
+ GtkTextIter iter;
+ PangoLayout *layout;
+
+ color_t_to_gdkcolor(&tcolors[MFG_IDX], &prefs.gui_marked_fg);
+ color_t_to_gdkcolor(&tcolors[MBG_IDX], &prefs.gui_marked_bg);
+ color_t_to_gdkcolor(&tcolors[IFG_IDX], &prefs.gui_ignored_fg);
+ color_t_to_gdkcolor(&tcolors[IBG_IDX], &prefs.gui_ignored_bg);
+ color_t_to_gdkcolor(&tcolors[CFG_IDX], &prefs.st_client_fg);
+ color_t_to_gdkcolor(&tcolors[CBG_IDX], &prefs.st_client_bg);
+ color_t_to_gdkcolor(&tcolors[SFG_IDX], &prefs.st_server_fg);
+ color_t_to_gdkcolor(&tcolors[SBG_IDX], &prefs.st_server_bg);
+
+ curcolor = &tcolors[CFG_IDX];
+
+ /* Enclosing containers for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+
+ main_tb = gtk_table_new(3, 3, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
+ gtk_widget_show(main_tb);
+
+ label = gtk_label_new("Set:");
+ gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, 0, 1);
+ gtk_widget_show(label);
+
+ /* We have to create this now, and configure it below. */
+ colorsel = gtk_color_selection_new();
+
+ combo_box = gtk_combo_box_text_new();
+ for (i = 0; i < mcount; i++){
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), mt[i]);
+ }
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), CFG_IDX);
+ g_signal_connect(combo_box, "changed", G_CALLBACK(update_current_color), colorsel);
+ gtk_table_attach(GTK_TABLE(main_tb), combo_box, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
+
+ gtk_widget_show(combo_box);
+
+ sample = gtk_text_view_new();
+ layout = gtk_widget_create_pango_layout(sample, SAMPLE_SERVER_TEXT);
+ pango_layout_get_pixel_size(layout, &width, &height);
+ g_object_unref(G_OBJECT(layout));
+ gtk_widget_set_size_request(sample, width, height * 2);
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(sample), FALSE);
+ buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(sample));
+ gtk_text_buffer_get_start_iter(buf, &iter);
+ gtk_text_buffer_create_tag(buf, "marked",
+ "foreground-gdk", &tcolors[MFG_IDX],
+ "background-gdk", &tcolors[MBG_IDX], NULL);
+ gtk_text_buffer_create_tag(buf, "ignored",
+ "foreground-gdk", &tcolors[IFG_IDX],
+ "background-gdk", &tcolors[IBG_IDX], NULL);
+ gtk_text_buffer_create_tag(buf, "client",
+ "foreground-gdk", &tcolors[CFG_IDX],
+ "background-gdk", &tcolors[CBG_IDX], NULL);
+ gtk_text_buffer_create_tag(buf, "server",
+ "foreground-gdk", &tcolors[SFG_IDX],
+ "background-gdk", &tcolors[SBG_IDX], NULL);
+ gtk_text_buffer_insert_with_tags_by_name(buf, &iter, SAMPLE_MARKED_TEXT, -1,
+ "marked", NULL);
+ gtk_text_buffer_insert_with_tags_by_name(buf, &iter, SAMPLE_IGNORED_TEXT, -1,
+ "ignored", NULL);
+ gtk_text_buffer_insert_with_tags_by_name(buf, &iter, SAMPLE_CLIENT_TEXT, -1,
+ "client", NULL);
+ gtk_text_buffer_insert_with_tags_by_name(buf, &iter, SAMPLE_SERVER_TEXT, -1,
+ "server", NULL);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), sample, 2, 3, 0, 2);
+ gtk_widget_show(sample);
+
+ gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(colorsel),
+ curcolor);
+ gtk_table_attach(GTK_TABLE(main_tb), colorsel, 0, 3, 2, 3,
+ GTK_SHRINK, GTK_SHRINK, 0, 0);
+
+ g_object_set_data(G_OBJECT(colorsel), STREAM_SAMPLE_KEY, sample);
+ g_signal_connect(colorsel, "color-changed", G_CALLBACK(update_text_color), NULL);
+ gtk_widget_show(colorsel);
+
+ gtk_widget_show(main_vb);
+ return(main_vb);
+}
+
+static void
+update_text_color(GtkWidget *w, gpointer data _U_) {
+ GtkTextView *sample = g_object_get_data(G_OBJECT(w), STREAM_SAMPLE_KEY);
+ GtkTextBuffer *buf;
+ GtkTextTag *tag;
+
+ gtk_color_selection_get_current_color(GTK_COLOR_SELECTION(w), curcolor);
+
+ buf = gtk_text_view_get_buffer(sample);
+ tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buf), "marked");
+ g_object_set(tag, "foreground-gdk", &tcolors[MFG_IDX], "background-gdk",
+ &tcolors[MBG_IDX], NULL);
+ tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buf), "ignored");
+ g_object_set(tag, "foreground-gdk", &tcolors[IFG_IDX], "background-gdk",
+ &tcolors[IBG_IDX], NULL);
+ tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buf), "client");
+ g_object_set(tag, "foreground-gdk", &tcolors[CFG_IDX], "background-gdk",
+ &tcolors[CBG_IDX], NULL);
+ tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buf), "server");
+ g_object_set(tag, "foreground-gdk", &tcolors[SFG_IDX], "background-gdk",
+ &tcolors[SBG_IDX], NULL);
+}
+
+static void
+update_current_color(GtkWidget *combo_box, gpointer data)
+{
+ GtkColorSelection *colorsel = data;
+ int i;
+
+ i = gtk_combo_box_get_active (GTK_COMBO_BOX(combo_box));
+ curcolor = &tcolors[i];
+
+ gtk_color_selection_set_current_color(colorsel, curcolor);
+}
+
+void
+stream_prefs_fetch(GtkWidget *w _U_)
+{
+ gdkcolor_to_color_t(&prefs.gui_marked_fg, &tcolors[MFG_IDX]);
+ gdkcolor_to_color_t(&prefs.gui_marked_bg, &tcolors[MBG_IDX]);
+ gdkcolor_to_color_t(&prefs.gui_ignored_fg, &tcolors[IFG_IDX]);
+ gdkcolor_to_color_t(&prefs.gui_ignored_bg, &tcolors[IBG_IDX]);
+ gdkcolor_to_color_t(&prefs.st_client_fg, &tcolors[CFG_IDX]);
+ gdkcolor_to_color_t(&prefs.st_client_bg, &tcolors[CBG_IDX]);
+ gdkcolor_to_color_t(&prefs.st_server_fg, &tcolors[SFG_IDX]);
+ gdkcolor_to_color_t(&prefs.st_server_bg, &tcolors[SBG_IDX]);
+}
+
+void
+stream_prefs_apply(GtkWidget *w _U_)
+{
+ follow_tcp_redraw_all();
+}
+
+void
+stream_prefs_destroy(GtkWidget *w _U_)
+{
+}
diff --git a/ui/gtk/prefs_stream.h b/ui/gtk/prefs_stream.h
new file mode 100644
index 0000000000..921d745b80
--- /dev/null
+++ b/ui/gtk/prefs_stream.h
@@ -0,0 +1,59 @@
+/* stream_prefs.h
+ * Definitions for stream preferences window
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1999 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __STREAM_PREFS_H__
+#define __STREAM_PREFS_H__
+
+/** @file
+ * "User Interface: Colors" preferences page.
+ * @todo rename functions and files from stream to colors
+ * @ingroup prefs_group
+ */
+
+/** Build a Colors preferences page.
+ *
+ * @return the new preferences page
+ */
+GtkWidget *stream_prefs_show(void);
+
+/** Fetch preference values from page.
+ *
+ * @param widget widget from stream_prefs_show()
+ */
+void stream_prefs_fetch(GtkWidget *widget);
+
+/** Apply preference values from page.
+ *
+ * @param widget widget from stream_prefs_show()
+ */
+void stream_prefs_apply(GtkWidget *widget);
+
+/** Destroy preference values from page.
+ *
+ * @param widget widget from stream_prefs_show()
+ */
+void stream_prefs_destroy(GtkWidget *widget);
+
+#endif
diff --git a/ui/gtk/prefs_taps.c b/ui/gtk/prefs_taps.c
new file mode 100644
index 0000000000..d3eba4718e
--- /dev/null
+++ b/ui/gtk/prefs_taps.c
@@ -0,0 +1,133 @@
+/* prefs_taps.c
+ * Dialog box for tap/statistics preferences
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdlib.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/prefs.h>
+
+#include "ui/gtk/prefs_taps.h"
+#include "ui/gtk/prefs_dlg.h"
+#include "ui/gtk/main.h"
+
+#define TAP_UPDATE_INTERVAL_KEY "update_interval"
+
+#define RTP_PLAYER_MAX_VISIBLE_KEY "rtp_player_max_visible"
+#define RTP_PLAYER_TABLE_ROWS 6
+
+static char update_interval_str[128] = "";
+static char max_visible_str[128] = "";
+
+
+GtkWidget*
+stats_prefs_show(void)
+{
+ GtkWidget *main_tb, *main_vb;
+ GtkWidget *tap_update_interval_te;
+#ifdef HAVE_LIBPORTAUDIO
+ GtkWidget *rtp_player_max_visible_te;
+#endif
+ int pos = 0;
+
+ /* Main vertical box */
+ main_vb = gtk_vbox_new(FALSE, 7);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+
+ /* Main table */
+ main_tb = gtk_table_new(RTP_PLAYER_TABLE_ROWS, 2, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
+ gtk_widget_show(main_tb);
+
+ /* Tap update gap in ms */
+ tap_update_interval_te = create_preference_entry(main_tb, pos++,
+ "Tap update interval in ms:",
+ "Determines time between tap updates.", max_visible_str);
+ g_snprintf(update_interval_str, sizeof(update_interval_str), "%d", prefs.tap_update_interval);
+ gtk_entry_set_text(GTK_ENTRY(tap_update_interval_te), update_interval_str);
+ gtk_widget_set_tooltip_text(tap_update_interval_te, "Gap in milliseconds between updates to taps is defined here");
+ g_object_set_data(G_OBJECT(main_vb), TAP_UPDATE_INTERVAL_KEY, tap_update_interval_te);
+
+#ifdef HAVE_LIBPORTAUDIO
+ /* Max visible channels in RTP Player */
+ rtp_player_max_visible_te = create_preference_entry(main_tb, pos++,
+ "Max visible channels in RTP Player:",
+ "Determines maximum height of RTP Player window.", max_visible_str);
+ g_snprintf(max_visible_str, sizeof(max_visible_str), "%d", prefs.rtp_player_max_visible);
+ gtk_entry_set_text(GTK_ENTRY(rtp_player_max_visible_te), max_visible_str);
+ gtk_widget_set_tooltip_text(rtp_player_max_visible_te, "Maximum height of RTP Player window is defined here.");
+ g_object_set_data(G_OBJECT(main_vb), RTP_PLAYER_MAX_VISIBLE_KEY, rtp_player_max_visible_te);
+#endif
+
+ /* Show 'em what we got */
+ gtk_widget_show_all(main_vb);
+
+ return main_vb;
+}
+
+void
+stats_prefs_fetch(GtkWidget *w _U_)
+{
+ GtkWidget *tap_update_interval_te;
+#ifdef HAVE_LIBPORTAUDIO
+ GtkWidget *rtp_player_max_visible_te;
+#endif
+
+ /* Tap update interval */
+ tap_update_interval_te = (GtkWidget *)g_object_get_data(G_OBJECT(w), TAP_UPDATE_INTERVAL_KEY);
+ prefs.tap_update_interval = strtol(gtk_entry_get_text(
+ GTK_ENTRY(tap_update_interval_te)), NULL, 10);
+
+ /* Test for a sane tap update interval */
+ if (prefs.tap_update_interval < 100 || prefs.tap_update_interval > 10000) {
+ prefs.tap_update_interval = 3000;
+ }
+
+#ifdef HAVE_LIBPORTAUDIO
+ /* Max RTP channels */
+ rtp_player_max_visible_te = (GtkWidget *)g_object_get_data(G_OBJECT(w), RTP_PLAYER_MAX_VISIBLE_KEY);
+ prefs.rtp_player_max_visible = strtol(gtk_entry_get_text(
+ GTK_ENTRY(rtp_player_max_visible_te)), NULL, 10);
+
+ /* Test for a sane max channels entry */
+ if (prefs.rtp_player_max_visible < 1 || prefs.rtp_player_max_visible > 10)
+ prefs.rtp_player_max_visible = RTP_PLAYER_DEFAULT_VISIBLE;
+#endif
+}
+
+void
+stats_prefs_apply(GtkWidget *w _U_)
+{
+ reset_tap_update_timer();
+}
+
+void
+stats_prefs_destroy(GtkWidget *w _U_)
+{
+}
diff --git a/ui/gtk/prefs_taps.h b/ui/gtk/prefs_taps.h
new file mode 100644
index 0000000000..1876962efb
--- /dev/null
+++ b/ui/gtk/prefs_taps.h
@@ -0,0 +1,57 @@
+/* prefs_taps.h
+ * Definitions for Taps/Statistics preferences window
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PREFS_STATS_H__
+#define __PREFS_STATS_H__
+
+/** @file
+ * "RTP player" preferences page.
+ * @ingroup prefs_group
+ */
+
+/** Build a RTP player preferences page.
+ *
+ * @return the new preferences page
+ */
+GtkWidget *stats_prefs_show(void);
+
+/** Fetch preference values from page.
+ *
+ * @param widget widget from rtp_player_prefs_show()
+ */
+void stats_prefs_fetch(GtkWidget *widget);
+
+/** Apply preference values from page.
+ *
+ * @param widget widget from rtp_player_prefs_show()
+ */
+void stats_prefs_apply(GtkWidget *widget);
+
+/** Destroy preference values from page.
+ *
+ * @param widget widget from rtp_player_prefs_show()
+ */
+void stats_prefs_destroy(GtkWidget *widget);
+
+#endif
diff --git a/ui/gtk/print_dlg.c b/ui/gtk/print_dlg.c
new file mode 100644
index 0000000000..63f7c30a39
--- /dev/null
+++ b/ui/gtk/print_dlg.c
@@ -0,0 +1,1139 @@
+/* print_dlg.c
+ * Dialog boxes for printing and exporting to text files
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/prefs.h>
+#include <epan/epan_dissect.h>
+#include <epan/filesystem.h>
+
+#include "../print.h"
+#include "../alert_box.h"
+#include "../simple_dialog.h"
+#include "../util.h"
+#include <wsutil/file_util.h>
+
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/capture_file_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/file_dlg.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/range_utils.h"
+#include "ui/gtk/help_dlg.h"
+
+#ifdef _WIN32
+#include <gdk/gdkwin32.h>
+#include <windows.h>
+#include "win32/file_dlg_win32.h"
+#include "win32/print_win32.h"
+#include "../tempfile.h"
+#endif
+
+/* dialog output action */
+typedef enum {
+ output_action_print, /* print text to printer */
+ output_action_export_text, /* export to plain text */
+ output_action_export_ps, /* export to postscript */
+ output_action_export_psml, /* export to packet summary markup language */
+ output_action_export_pdml, /* export to packet data markup language */
+ output_action_export_csv, /* export to csv file */
+ output_action_export_carrays /* export to C array file */
+} output_action_e;
+
+
+/* On Win32, a GUI application apparently can't use "popen()" (it
+ "returns an invalid file handle, if used in a Windows program,
+ that will cause the program to hang indefinitely"), so we can't
+ use a pipe to a print command to print to a printer.
+
+ Eventually, we should try to use the native Win32 printing API
+ for this (and also use various UNIX printing APIs, when present?).
+*/
+
+static GtkWidget *
+open_print_dialog(const char *title, output_action_e action, print_args_t *args);
+static void print_cmd_toggle_dest(GtkWidget *widget, gpointer data);
+static void print_cmd_toggle_detail(GtkWidget *widget, gpointer data);
+static void print_ok_cb(GtkWidget *ok_bt, gpointer parent_w);
+static void print_destroy_cb(GtkWidget *win, gpointer user_data);
+
+
+
+#define PRINT_ARGS_KEY "printer_args"
+
+#define PRINT_PS_RB_KEY "printer_ps_radio_button"
+#define PRINT_PDML_RB_KEY "printer_pdml_radio_button"
+#define PRINT_PSML_RB_KEY "printer_psml_radio_button"
+#define PRINT_CSV_RB_KEY "printer_csv_radio_button"
+#define PRINT_CARRAYS_RB_KEY "printer_carrays_radio_button"
+#define PRINT_DEST_CB_KEY "printer_destination_check_button"
+
+#define PRINT_SUMMARY_CB_KEY "printer_summary_check_button"
+#define PRINT_DETAILS_CB_KEY "printer_details_check_button"
+#define PRINT_COLLAPSE_ALL_RB_KEY "printer_collapse_all_radio_button"
+#define PRINT_AS_DISPLAYED_RB_KEY "printer_as_displayed_radio_button"
+#define PRINT_EXPAND_ALL_RB_KEY "printer_expand_all_radio_button"
+#define PRINT_HEX_CB_KEY "printer_hex_check_button"
+#define PRINT_FORMFEED_CB_KEY "printer_formfeed_check_button"
+
+#define PRINT_BT_KEY "printer_button"
+
+#define PRINT_TE_PTR_KEY "printer_file_te_ptr"
+
+
+/*
+ * Keep a static pointer to the current "Print" window, if any, so that if
+ * somebody tries to do "File:Print" while there's already a "Print" window
+ * up, we just pop up the existing one, rather than creating a new one.
+ */
+static GtkWidget *print_win = NULL;
+
+static print_args_t print_args;
+
+static gboolean print_prefs_init = FALSE;
+
+
+static void
+file_print_cmd(gboolean print_selected)
+{
+ print_args_t *args = &print_args;
+
+ if (print_win != NULL) {
+ /* There's already a "Print" dialog box; reactivate it. */
+ reactivate_window(print_win);
+ return;
+ }
+
+ /* get settings from preferences (and other initial values) only once */
+ if(print_prefs_init == FALSE) {
+ print_prefs_init = TRUE;
+ args->format = prefs.pr_format;
+ args->to_file = prefs.pr_dest;
+ args->file = g_strdup(prefs.pr_file);
+ args->cmd = g_strdup(prefs.pr_cmd);
+ args->print_summary = TRUE;
+ args->print_dissections = print_dissections_as_displayed;
+ args->print_hex = FALSE;
+ args->print_formfeed = FALSE;
+ }
+
+ /* init the printing range */
+ packet_range_init(&args->range);
+
+ if(print_selected) {
+ args->range.process = range_process_selected;
+ }
+
+ print_win = open_print_dialog("Wireshark: Print", output_action_print, args);
+ g_signal_connect(print_win, "destroy", G_CALLBACK(print_destroy_cb), &print_win);
+}
+
+void
+file_print_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ file_print_cmd(FALSE);
+}
+
+void
+file_print_selected_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ file_print_cmd(TRUE);
+}
+
+/*
+ * Keep a static pointer to the current "Export text" window, if any, so that if
+ * somebody tries to do "File:Export to text" while there's already a "Export text" window
+ * up, we just pop up the existing one, rather than creating a new one.
+ */
+static GtkWidget *export_text_win = NULL;
+
+static print_args_t export_text_args;
+
+static gboolean export_text_prefs_init = FALSE;
+
+
+#ifdef _WIN32
+void
+export_text_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ win32_export_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), export_type_text);
+ return;
+}
+#else
+void
+export_text_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ print_args_t *args = &export_text_args;
+
+ if (export_text_win != NULL) {
+ /* There's already a "Export text" dialog box; reactivate it. */
+ reactivate_window(export_text_win);
+ return;
+ }
+
+ /* get settings from preferences (and other initial values) only once */
+ if(export_text_prefs_init == FALSE) {
+ export_text_prefs_init = TRUE;
+ args->format = PR_FMT_TEXT;
+ args->to_file = TRUE;
+ args->file = g_strdup("");
+ args->cmd = g_strdup("");
+ args->print_summary = TRUE;
+ args->print_dissections = print_dissections_as_displayed;
+ args->print_hex = FALSE;
+ args->print_formfeed = FALSE;
+ }
+
+ /* init the printing range */
+ packet_range_init(&args->range);
+
+ export_text_win = open_print_dialog("Wireshark: Export as \"Plain Text\" File", output_action_export_text, args);
+ g_signal_connect(export_text_win, "destroy", G_CALLBACK(print_destroy_cb), &export_text_win);
+}
+#endif
+
+
+/*
+ * Keep a static pointer to the current "Export ps" window, if any, so that if
+ * somebody tries to do "File:Export to ps" while there's already a "Export ps" window
+ * up, we just pop up the existing one, rather than creating a new one.
+ */
+static GtkWidget *export_ps_win = NULL;
+
+static print_args_t export_ps_args;
+
+static gboolean export_ps_prefs_init = FALSE;
+
+
+#ifdef _WIN32
+void
+export_ps_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ win32_export_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), export_type_ps);
+ return;
+}
+#else
+void
+export_ps_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ print_args_t *args = &export_ps_args;
+
+ if (export_ps_win != NULL) {
+ /* There's already a "Export ps" dialog box; reactivate it. */
+ reactivate_window(export_ps_win);
+ return;
+ }
+
+ /* get settings from preferences (and other initial values) only once */
+ if(export_ps_prefs_init == FALSE) {
+ export_ps_prefs_init = TRUE;
+ args->format = PR_FMT_PS;
+ args->to_file = TRUE;
+ args->file = g_strdup("");
+ args->cmd = g_strdup("");
+ args->print_summary = TRUE;
+ args->print_dissections = print_dissections_as_displayed;
+ args->print_hex = FALSE;
+ args->print_formfeed = FALSE;
+ }
+
+ /* init the printing range */
+ packet_range_init(&args->range);
+
+ export_ps_win = open_print_dialog("Wireshark: Export as \"PostScript\" file", output_action_export_ps, args);
+ g_signal_connect(export_ps_win, "destroy", G_CALLBACK(print_destroy_cb), &export_ps_win);
+}
+#endif
+
+
+/*
+ * Keep a static pointer to the current "Export psml" window, if any, so that if
+ * somebody tries to do "File:Export to psml" while there's already a "Export psml" window
+ * up, we just pop up the existing one, rather than creating a new one.
+ */
+static GtkWidget *export_psml_win = NULL;
+
+static print_args_t export_psml_args;
+
+static gboolean export_psml_prefs_init = FALSE;
+
+
+#ifdef _WIN32
+void
+export_psml_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ win32_export_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), export_type_psml);
+ return;
+}
+#else
+void
+export_psml_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ print_args_t *args = &export_psml_args;
+
+ if (export_psml_win != NULL) {
+ /* There's already a "Export psml" dialog box; reactivate it. */
+ reactivate_window(export_psml_win);
+ return;
+ }
+
+ /* get settings from preferences (and other initial values) only once */
+ if(export_psml_prefs_init == FALSE) {
+ export_psml_prefs_init = TRUE;
+ args->format = PR_FMT_TEXT; /* XXX */
+ args->to_file = TRUE;
+ args->file = g_strdup("");
+ args->cmd = g_strdup("");
+ args->print_summary = TRUE;
+ args->print_dissections = print_dissections_as_displayed;
+ args->print_hex = FALSE;
+ args->print_formfeed = FALSE;
+ }
+
+ /* init the printing range */
+ packet_range_init(&args->range);
+
+ export_psml_win = open_print_dialog("Wireshark: Export as \"PSML\" file", output_action_export_psml, args);
+ g_signal_connect(export_psml_win, "destroy", G_CALLBACK(print_destroy_cb), &export_psml_win);
+}
+#endif
+
+/*
+ * Keep a static pointer to the current "Export pdml" window, if any, so that if
+ * somebody tries to do "File:Export to pdml" while there's already a "Export pdml" window
+ * up, we just pop up the existing one, rather than creating a new one.
+ */
+static GtkWidget *export_pdml_win = NULL;
+
+static print_args_t export_pdml_args;
+
+static gboolean export_pdml_prefs_init = FALSE;
+
+
+#ifdef _WIN32
+void
+export_pdml_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ win32_export_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), export_type_pdml);
+ return;
+}
+#else
+void
+export_pdml_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ print_args_t *args = &export_pdml_args;
+
+ if (export_pdml_win != NULL) {
+ /* There's already a "Export pdml" dialog box; reactivate it. */
+ reactivate_window(export_pdml_win);
+ return;
+ }
+
+ /* get settings from preferences (and other initial values) only once */
+ if(export_pdml_prefs_init == FALSE) {
+ export_pdml_prefs_init = TRUE;
+ args->format = PR_FMT_TEXT; /* XXX */
+ args->to_file = TRUE;
+ args->file = g_strdup("");
+ args->cmd = g_strdup("");
+ args->print_summary = TRUE;
+ args->print_dissections = print_dissections_as_displayed;
+ args->print_hex = FALSE;
+ args->print_formfeed = FALSE;
+ }
+
+ /* init the printing range */
+ packet_range_init(&args->range);
+
+ export_pdml_win = open_print_dialog("Wireshark: Export as \"PDML\" file", output_action_export_pdml, args);
+ g_signal_connect(export_pdml_win, "destroy", G_CALLBACK(print_destroy_cb), &export_pdml_win);
+}
+#endif
+
+/*
+ * Keep a static pointer to the current "Export csv" window, if any, so that if
+ * somebody tries to do "File:Export to CSV" while there's already a "Export csv" window
+ * up, we just pop up the existing one, rather than creating a new one.
+ */
+static GtkWidget *export_csv_win = NULL;
+
+static print_args_t export_csv_args;
+
+static gboolean export_csv_prefs_init = FALSE;
+
+#ifdef _WIN32
+void
+export_csv_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ win32_export_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), export_type_csv);
+ return;
+}
+#else
+void
+export_csv_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ print_args_t *args = &export_csv_args;
+
+ if (export_csv_win != NULL) {
+ /* There's already a "Export csv" dialog box; reactivate it. */
+ reactivate_window(export_csv_win);
+ return;
+ }
+
+ /* get settings from preferences (and other initial values) only once */
+ if(export_csv_prefs_init == FALSE) {
+ export_csv_prefs_init = TRUE;
+ args->format = PR_FMT_TEXT; /* XXX */
+ args->to_file = TRUE;
+ args->file = g_strdup("");
+ args->cmd = g_strdup("");
+ args->print_summary = FALSE;
+ args->print_dissections = print_dissections_none;
+ args->print_hex = FALSE;
+ args->print_formfeed = FALSE;
+ }
+
+ /* init the printing range */
+ packet_range_init(&args->range);
+
+ export_csv_win = open_print_dialog("Wireshark: Export as \"Comma Separated Values\" File", output_action_export_csv, args);
+ g_signal_connect(export_csv_win, "destroy", G_CALLBACK(print_destroy_cb), &export_csv_win);
+}
+#endif
+
+/*
+ * Keep a static pointer to the current "Export carrays" window, if any, so that if
+ * somebody tries to do "File:Export to carrays" while there's already a "Export carrays" window
+ * up, we just pop up the existing one, rather than creating a new one.
+ */
+static GtkWidget *export_carrays_win = NULL;
+
+static print_args_t export_carrays_args;
+
+static gboolean export_carrays_prefs_init = FALSE;
+
+#ifdef _WIN32
+void
+export_carrays_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ win32_export_file(GDK_WINDOW_HWND(gtk_widget_get_window(top_level)), export_type_carrays);
+ return;
+}
+#else
+void
+export_carrays_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
+{
+ print_args_t *args = &export_carrays_args;
+
+ if (export_carrays_win != NULL) {
+ /* There's already a "Export carrays" dialog box; reactivate it. */
+ reactivate_window(export_carrays_win);
+ return;
+ }
+
+ /* get settings from preferences (and other initial values) only once */
+ if(export_carrays_prefs_init == FALSE) {
+ export_carrays_prefs_init = TRUE;
+ args->format = PR_FMT_TEXT;
+ args->to_file = TRUE;
+ args->file = g_strdup("");
+ args->cmd = g_strdup("");
+ args->print_summary = FALSE;
+ args->print_dissections = print_dissections_none;
+ args->print_hex = FALSE;
+ args->print_formfeed = FALSE;
+ }
+
+ /* init the printing range */
+ packet_range_init(&args->range);
+
+ export_carrays_win = open_print_dialog("Wireshark: Export as \"C Arrays\" File",
+ output_action_export_carrays, args);
+ g_signal_connect(export_carrays_win, "destroy", G_CALLBACK(print_destroy_cb), &export_carrays_win);
+}
+#endif
+
+static void
+print_browse_file_cb(GtkWidget *file_bt, GtkWidget *file_te)
+{
+ file_selection_browse(file_bt, file_te, "Wireshark: Print to File",
+ FILE_SELECTION_WRITE_BROWSE);
+}
+
+
+
+/* Open the print dialog */
+static GtkWidget *
+open_print_dialog(const char *title, output_action_e action, print_args_t *args)
+{
+ GtkWidget *main_win;
+ GtkWidget *main_vb;
+
+ GtkWidget *printer_fr, *printer_vb, *export_format_lb;
+ GtkWidget *text_rb, *ps_rb, *pdml_rb, *psml_rb, *csv_rb, *carrays_rb;
+ GtkWidget *printer_tb, *dest_cb;
+#ifndef _WIN32
+ GtkWidget *cmd_lb, *cmd_te;
+#endif
+ GtkWidget *file_bt, *file_te;
+
+ GtkWidget *range_fr, *range_tb;
+
+ GtkWidget *packet_hb;
+
+ GtkWidget *format_fr, *format_vb;
+ GtkWidget *summary_cb;
+
+ GtkWidget *details_cb;
+ GtkWidget *details_hb, *details_vb;
+ GtkWidget *collapse_all_rb, *as_displayed_rb, *expand_all_rb;
+ GtkWidget *hex_cb;
+ GtkWidget *sep, *formfeed_cb;
+
+ GtkWidget *bbox, *ok_bt, *cancel_bt, *help_bt;
+
+ /* dialog window */
+ main_win = dlg_window_new(title);
+
+ /* Vertical enclosing container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(main_win), main_vb);
+ gtk_widget_show(main_vb);
+
+/*****************************************************/
+
+ /*** printer frame ***/
+ printer_fr = gtk_frame_new(action == output_action_print ? "Printer" : "Export to file:");
+ gtk_box_pack_start(GTK_BOX(main_vb), printer_fr, FALSE, FALSE, 0);
+ gtk_widget_show(printer_fr);
+ printer_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(printer_vb), 5);
+ gtk_container_add(GTK_CONTAINER(printer_fr), printer_vb);
+ gtk_widget_show(printer_vb);
+
+ /* "Plain text" / "Postscript" / "PDML", ... radio buttons */
+ text_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "Plain _text");
+ if (args->format == PR_FMT_TEXT)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(text_rb), TRUE);
+ gtk_widget_set_tooltip_text(text_rb, "Print output in ascii \"plain text\" format. If you're unsure, use this format.");
+ gtk_box_pack_start(GTK_BOX(printer_vb), text_rb, FALSE, FALSE, 0);
+ if(action == output_action_print)
+ gtk_widget_show(text_rb);
+
+ ps_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(text_rb), "_PostScript");
+ if (args->format == PR_FMT_PS)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ps_rb), TRUE);
+ gtk_widget_set_tooltip_text(ps_rb, "Print output in \"postscript\" format, for postscript capable printers or print servers.");
+ gtk_box_pack_start(GTK_BOX(printer_vb), ps_rb, FALSE, FALSE, 0);
+ if(action == output_action_print)
+ gtk_widget_show(ps_rb);
+
+ pdml_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(text_rb), "PDM_L (XML: Packet Details Markup Language)");
+ if (action == output_action_export_pdml)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pdml_rb), TRUE);
+ gtk_widget_set_tooltip_text(pdml_rb,
+ "Print output in \"PDML\" (Packet Details Markup Language), "
+ "an XML based packet data interchange format. "
+ "Usually used in combination with the \"Output to file\" option to export packet data into an XML file.");
+ gtk_box_pack_start(GTK_BOX(printer_vb), pdml_rb, FALSE, FALSE, 0);
+ /* gtk_widget_show(pdml_rb); */
+
+ psml_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(text_rb), "PSML (XML: Packet Summary Markup Language)");
+ if (action == output_action_export_psml)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(psml_rb), TRUE);
+ gtk_widget_set_tooltip_text(psml_rb,
+ "Print output in \"PSML\" (Packet Summary Markup Language), "
+ "an XML based packet summary interchange format. "
+ "Usually used in combination with the \"Output to file\" option to export packet data into an XML file.");
+ gtk_box_pack_start(GTK_BOX(printer_vb), psml_rb, FALSE, FALSE, 0);
+ /* gtk_widget_show(psml_rb); */
+
+ csv_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(text_rb), "_CSV");
+ if (action == output_action_export_csv)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(csv_rb), TRUE);
+ gtk_widget_set_tooltip_text(csv_rb,
+ "Print output in \"Comma Separated Values\" (CSV) format, "
+ "a text format compatible with OpenOffice and Excel. "
+ "One row for each packet, with its timestamp and size.");
+ gtk_box_pack_start(GTK_BOX(printer_vb), csv_rb, FALSE, FALSE, 0);
+ /* gtk_widget_show(csv_rb); */
+
+ carrays_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(text_rb), "C Arrays");
+ if (action == output_action_export_carrays)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(carrays_rb), TRUE);
+ gtk_widget_set_tooltip_text(carrays_rb,
+ "Print output in C Arrays format, "
+ "a text file suitable for use in C/C++ programs. "
+ "One char[] for each packet.");
+ gtk_box_pack_start(GTK_BOX(printer_vb), carrays_rb, FALSE, FALSE, 0);
+ /* gtk_widget_show(carrays_rb); */
+
+ /* printer table */
+#ifndef _WIN32
+ printer_tb = gtk_table_new(2, 3, FALSE);
+#else
+ printer_tb = gtk_table_new(2, 2, FALSE);
+#endif
+ gtk_box_pack_start(GTK_BOX(printer_vb), printer_tb, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(printer_tb), 5);
+ gtk_table_set_col_spacings(GTK_TABLE(printer_tb), 5);
+ gtk_widget_show(printer_tb);
+
+
+ /* Output to file button */
+ dest_cb = gtk_check_button_new_with_mnemonic("Output to _file:");
+ if (args->to_file)
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dest_cb), TRUE);
+ gtk_widget_set_tooltip_text(dest_cb, "Output to file instead of printer");
+ gtk_table_attach_defaults(GTK_TABLE(printer_tb), dest_cb, 0, 1, 0, 1);
+ if(action == output_action_print)
+ gtk_widget_show(dest_cb);
+
+ /* File text entry */
+ file_te = gtk_entry_new();
+ g_object_set_data(G_OBJECT(dest_cb), PRINT_FILE_TE_KEY, file_te);
+ gtk_widget_set_tooltip_text(file_te, "Enter Output filename");
+ gtk_entry_set_text(GTK_ENTRY(file_te), args->file);
+ gtk_table_attach_defaults(GTK_TABLE(printer_tb), file_te, 1, 2, 0, 1);
+ gtk_widget_set_sensitive(file_te, args->to_file);
+ gtk_widget_show(file_te);
+ if (args->to_file)
+ gtk_widget_grab_focus(file_te);
+
+ /* "Browse" button */
+ file_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_BROWSE);
+ g_object_set_data(G_OBJECT(dest_cb), PRINT_FILE_BT_KEY, file_bt);
+ g_object_set_data(G_OBJECT(file_bt), PRINT_TE_PTR_KEY, file_te);
+ gtk_widget_set_tooltip_text(file_bt, "Browse output filename in filesystem");
+ gtk_table_attach_defaults(GTK_TABLE(printer_tb), file_bt, 2, 3, 0, 1);
+ gtk_widget_set_sensitive(file_bt, args->to_file);
+ gtk_widget_show(file_bt);
+
+ /* Command label and text entry */
+#ifndef _WIN32
+ cmd_lb = gtk_label_new("Print command:");
+ g_object_set_data(G_OBJECT(dest_cb), PRINT_CMD_LB_KEY, cmd_lb);
+ gtk_misc_set_alignment(GTK_MISC(cmd_lb), 1.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(printer_tb), cmd_lb, 0, 1, 1, 2);
+ gtk_widget_set_sensitive(cmd_lb, !args->to_file);
+ if(action == output_action_print)
+ gtk_widget_show(cmd_lb);
+
+ cmd_te = gtk_entry_new();
+ g_object_set_data(G_OBJECT(dest_cb), PRINT_CMD_TE_KEY, cmd_te);
+ gtk_widget_set_tooltip_text(cmd_te, "Enter print command");
+ gtk_entry_set_text(GTK_ENTRY(cmd_te), args->cmd);
+ gtk_table_attach_defaults(GTK_TABLE(printer_tb), cmd_te, 1, 2, 1, 2);
+ gtk_widget_set_sensitive(cmd_te, !args->to_file);
+ if(action == output_action_print)
+ gtk_widget_show(cmd_te);
+#endif
+
+ g_signal_connect(dest_cb, "toggled", G_CALLBACK(print_cmd_toggle_dest), NULL);
+ g_signal_connect(file_bt, "clicked", G_CALLBACK(print_browse_file_cb), file_te);
+
+ if(action == output_action_export_ps) {
+ export_format_lb = gtk_label_new("(PostScript files can be easily converted to PDF files using ghostscript's ps2pdf)");
+ gtk_box_pack_start(GTK_BOX(printer_vb), export_format_lb, FALSE, FALSE, 0);
+ gtk_widget_show(export_format_lb);
+ }
+
+/*****************************************************/
+
+ /*** hor box for range and format frames ***/
+ packet_hb = gtk_hbox_new(FALSE, 5);
+ gtk_container_add(GTK_CONTAINER(main_vb), packet_hb);
+ gtk_widget_show(packet_hb);
+
+ /*** packet range frame ***/
+ range_fr = gtk_frame_new("Packet Range");
+ gtk_box_pack_start(GTK_BOX(packet_hb), range_fr, FALSE, FALSE, 0);
+ gtk_widget_show(range_fr);
+
+ range_tb = range_new(&args->range);
+ gtk_container_add(GTK_CONTAINER(range_fr), range_tb);
+ gtk_widget_show(range_tb);
+
+/*****************************************************/
+
+ /*** packet format frame ***/
+ format_fr = gtk_frame_new("Packet Format");
+ gtk_box_pack_start(GTK_BOX(packet_hb), format_fr, TRUE, TRUE, 0);
+ if( action == output_action_print ||
+ action == output_action_export_text ||
+ action == output_action_export_ps)
+ gtk_widget_show(format_fr);
+ format_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(format_vb), 5);
+ gtk_container_add(GTK_CONTAINER(format_fr), format_vb);
+ gtk_widget_show(format_vb);
+
+ /* "Print summary line" check button */
+ summary_cb = gtk_check_button_new_with_mnemonic("Packet summary line");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(summary_cb), args->print_summary);
+ g_signal_connect(summary_cb, "clicked", G_CALLBACK(print_cmd_toggle_detail), main_win);
+ gtk_widget_set_tooltip_text(summary_cb, "Output of a packet summary line, like in the packet list");
+ gtk_container_add(GTK_CONTAINER(format_vb), summary_cb);
+ gtk_widget_show(summary_cb);
+
+
+ /* "Details" check button */
+ details_cb = gtk_check_button_new_with_mnemonic("Packet details:");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(details_cb), args->print_dissections != print_dissections_none);
+ g_signal_connect(details_cb, "clicked", G_CALLBACK(print_cmd_toggle_detail), main_win);
+ gtk_widget_set_tooltip_text(details_cb, "Output format of the selected packet details (protocol tree).");
+ gtk_container_add(GTK_CONTAINER(format_vb), details_cb);
+ gtk_widget_show(details_cb);
+
+ /*** packet details ***/
+ details_hb = gtk_hbox_new(FALSE, 6);
+ gtk_container_set_border_width(GTK_CONTAINER(details_hb), 0);
+ gtk_container_add(GTK_CONTAINER(format_vb), details_hb);
+ gtk_widget_show(details_hb);
+
+ details_vb = gtk_vbox_new(FALSE, 6);
+ gtk_container_set_border_width(GTK_CONTAINER(details_vb), 0);
+ gtk_container_add(GTK_CONTAINER(details_hb), details_vb);
+ gtk_widget_show(details_vb);
+
+ details_vb = gtk_vbox_new(FALSE, 6);
+ gtk_container_set_border_width(GTK_CONTAINER(details_vb), 0);
+ gtk_container_add(GTK_CONTAINER(details_hb), details_vb);
+ gtk_widget_show(details_vb);
+
+ /* "All collapsed"/"As displayed"/"All Expanded" radio buttons */
+ collapse_all_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "All co_llapsed");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(collapse_all_rb), args->print_dissections == print_dissections_collapsed);
+ gtk_widget_set_tooltip_text(collapse_all_rb, "Output of the packet details tree \"collapsed\"");
+ gtk_container_add(GTK_CONTAINER(details_vb), collapse_all_rb);
+ gtk_widget_show(collapse_all_rb);
+
+ as_displayed_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(collapse_all_rb), "As displa_yed");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(as_displayed_rb), args->print_dissections == print_dissections_as_displayed);
+ gtk_widget_set_tooltip_text(as_displayed_rb, "Output of the packet details tree \"as displayed\"");
+ gtk_container_add(GTK_CONTAINER(details_vb), as_displayed_rb);
+ gtk_widget_show(as_displayed_rb);
+
+ expand_all_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(collapse_all_rb), "All e_xpanded");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(expand_all_rb), args->print_dissections == print_dissections_expanded);
+ gtk_widget_set_tooltip_text(expand_all_rb, "Output of the packet details tree \"expanded\"");
+ gtk_container_add(GTK_CONTAINER(details_vb), expand_all_rb);
+ gtk_widget_show(expand_all_rb);
+
+ /* "Print hex" check button. */
+ hex_cb = gtk_check_button_new_with_mnemonic("Packet bytes");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hex_cb), args->print_hex);
+ g_signal_connect(hex_cb, "clicked", G_CALLBACK(print_cmd_toggle_detail), main_win);
+ gtk_widget_set_tooltip_text(hex_cb, "Add a hexdump of the packet data");
+ gtk_container_add(GTK_CONTAINER(format_vb), hex_cb);
+ gtk_widget_show(hex_cb);
+
+ /* seperator */
+ sep = gtk_hseparator_new();
+ gtk_container_add(GTK_CONTAINER(format_vb), sep);
+ gtk_widget_show(sep);
+
+ /* "Each packet on a new page" check button. */
+ formfeed_cb = gtk_check_button_new_with_mnemonic("Each packet on a new page");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(formfeed_cb), args->print_formfeed);
+ gtk_widget_set_tooltip_text (formfeed_cb, "When checked, a new page will be used for each packet. "
+ "This is done by adding a formfeed (or similar) between the packet outputs.");
+ gtk_container_add(GTK_CONTAINER(format_vb), formfeed_cb);
+ gtk_widget_show(formfeed_cb);
+
+
+ g_object_set_data(G_OBJECT(main_win), PRINT_ARGS_KEY, args);
+ g_object_set_data(G_OBJECT(main_win), PRINT_SUMMARY_CB_KEY, summary_cb);
+ g_object_set_data(G_OBJECT(main_win), PRINT_DETAILS_CB_KEY, details_cb);
+ g_object_set_data(G_OBJECT(main_win), PRINT_COLLAPSE_ALL_RB_KEY, collapse_all_rb);
+ g_object_set_data(G_OBJECT(main_win), PRINT_AS_DISPLAYED_RB_KEY, as_displayed_rb);
+ g_object_set_data(G_OBJECT(main_win), PRINT_EXPAND_ALL_RB_KEY, expand_all_rb);
+ g_object_set_data(G_OBJECT(main_win), PRINT_HEX_CB_KEY, hex_cb);
+
+/*****************************************************/
+
+
+ /* Button row */
+ bbox = dlg_button_row_new(action == output_action_print ? GTK_STOCK_PRINT : GTK_STOCK_OK, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+
+ ok_bt = g_object_get_data(G_OBJECT(bbox), action == output_action_print ? GTK_STOCK_PRINT : GTK_STOCK_OK);
+
+ g_object_set_data(G_OBJECT(main_win), PRINT_BT_KEY, ok_bt);
+
+ g_object_set_data(G_OBJECT(ok_bt), PRINT_PS_RB_KEY, ps_rb);
+ g_object_set_data(G_OBJECT(ok_bt), PRINT_PDML_RB_KEY, pdml_rb);
+ g_object_set_data(G_OBJECT(ok_bt), PRINT_PSML_RB_KEY, psml_rb);
+ g_object_set_data(G_OBJECT(ok_bt), PRINT_CSV_RB_KEY, csv_rb);
+ g_object_set_data(G_OBJECT(ok_bt), PRINT_CARRAYS_RB_KEY, carrays_rb);
+ g_object_set_data(G_OBJECT(ok_bt), PRINT_DEST_CB_KEY, dest_cb);
+#ifndef _WIN32
+ g_object_set_data(G_OBJECT(ok_bt), PRINT_CMD_TE_KEY, cmd_te);
+#endif
+
+ g_object_set_data(G_OBJECT(ok_bt), PRINT_ARGS_KEY, args);
+ g_object_set_data(G_OBJECT(ok_bt), PRINT_FILE_TE_KEY, file_te);
+ g_object_set_data(G_OBJECT(ok_bt), PRINT_SUMMARY_CB_KEY, summary_cb);
+ g_object_set_data(G_OBJECT(ok_bt), PRINT_DETAILS_CB_KEY, details_cb);
+ g_object_set_data(G_OBJECT(ok_bt), PRINT_COLLAPSE_ALL_RB_KEY, collapse_all_rb);
+ g_object_set_data(G_OBJECT(ok_bt), PRINT_AS_DISPLAYED_RB_KEY, as_displayed_rb);
+ g_object_set_data(G_OBJECT(ok_bt), PRINT_EXPAND_ALL_RB_KEY, expand_all_rb);
+ g_object_set_data(G_OBJECT(ok_bt), PRINT_HEX_CB_KEY, hex_cb);
+ g_object_set_data(G_OBJECT(ok_bt), PRINT_FORMFEED_CB_KEY, formfeed_cb);
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(print_ok_cb), main_win);
+ gtk_widget_set_tooltip_text (ok_bt, "Start output");
+
+ cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ window_set_cancel_button(main_win, cancel_bt, window_cancel_button_cb);
+ gtk_widget_set_tooltip_text (cancel_bt, "Cancel and exit dialog");
+
+ if(action == output_action_print) {
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_PRINT_DIALOG);
+ } else {
+#ifdef _WIN32
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_EXPORT_FILE_WIN32_DIALOG);
+#else
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_EXPORT_FILE_DIALOG);
+#endif
+ }
+
+ gtk_widget_grab_default(ok_bt);
+
+ /* Catch the "activate" signal on the "Command" and "File" text entries,
+ so that if the user types Return there, we act as if the "OK" button
+ had been selected, as happens if Return is typed if some widget
+ that *doesn't* handle the Return key has the input focus. */
+#ifndef _WIN32
+ dlg_set_activate(cmd_te, ok_bt);
+#endif
+ if(action != output_action_print)
+ dlg_set_activate(file_te, ok_bt);
+
+ g_signal_connect(main_win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+
+ gtk_widget_show(main_win);
+ window_present(main_win);
+
+ return main_win;
+}
+
+
+/* user changed "print to" destination */
+static void
+print_cmd_toggle_dest(GtkWidget *widget, gpointer data _U_)
+{
+#ifndef _WIN32
+ GtkWidget *cmd_lb, *cmd_te;
+#endif
+ GtkWidget *file_bt, *file_te;
+ int to_file;
+
+#ifndef _WIN32
+ cmd_lb = GTK_WIDGET(g_object_get_data(G_OBJECT(widget), PRINT_CMD_LB_KEY));
+ cmd_te = GTK_WIDGET(g_object_get_data(G_OBJECT(widget), PRINT_CMD_TE_KEY));
+#endif
+ file_bt = GTK_WIDGET(g_object_get_data(G_OBJECT(widget), PRINT_FILE_BT_KEY));
+ file_te = GTK_WIDGET(g_object_get_data(G_OBJECT(widget), PRINT_FILE_TE_KEY));
+
+ to_file = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
+#ifndef _WIN32
+ gtk_widget_set_sensitive(cmd_lb, !to_file);
+ gtk_widget_set_sensitive(cmd_te, !to_file);
+#endif
+ gtk_widget_set_sensitive(file_bt, to_file);
+ gtk_widget_set_sensitive(file_te, to_file);
+}
+
+
+/* user changed "packet details" */
+static void
+print_cmd_toggle_detail(GtkWidget *widget _U_, gpointer data)
+{
+ GtkWidget *print_bt, *summary_cb, *details_cb, *collapse_all_rb, *expand_all_rb, *as_displayed_rb, *hex_cb;
+ gboolean print_detail;
+
+
+ print_bt = GTK_WIDGET(g_object_get_data(G_OBJECT(data), PRINT_BT_KEY));
+ summary_cb = GTK_WIDGET(g_object_get_data(G_OBJECT(data), PRINT_SUMMARY_CB_KEY));
+ details_cb = GTK_WIDGET(g_object_get_data(G_OBJECT(data), PRINT_DETAILS_CB_KEY));
+ collapse_all_rb = GTK_WIDGET(g_object_get_data(G_OBJECT(data), PRINT_COLLAPSE_ALL_RB_KEY));
+ as_displayed_rb = GTK_WIDGET(g_object_get_data(G_OBJECT(data), PRINT_AS_DISPLAYED_RB_KEY));
+ expand_all_rb = GTK_WIDGET(g_object_get_data(G_OBJECT(data), PRINT_EXPAND_ALL_RB_KEY));
+ hex_cb = GTK_WIDGET(g_object_get_data(G_OBJECT(data), PRINT_HEX_CB_KEY));
+
+ /* is user disabled details, disable the corresponding buttons */
+ print_detail = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (details_cb));
+ gtk_widget_set_sensitive(collapse_all_rb, print_detail);
+ gtk_widget_set_sensitive(as_displayed_rb, print_detail);
+ gtk_widget_set_sensitive(expand_all_rb, print_detail);
+
+ /* if user selected nothing to print at all, disable the "ok" button */
+ gtk_widget_set_sensitive(print_bt,
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (summary_cb)) ||
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (details_cb)) ||
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (hex_cb)));
+}
+
+
+static void
+print_ok_cb(GtkWidget *ok_bt, gpointer parent_w)
+{
+ GtkWidget *button;
+ print_args_t *args;
+ const gchar *g_dest;
+ gchar *f_name;
+ gchar *dirname;
+ gboolean export_as_pdml = FALSE, export_as_psml = FALSE;
+ gboolean export_as_csv = FALSE;
+ gboolean export_as_carrays = FALSE;
+#ifdef _WIN32
+ gboolean win_printer = FALSE;
+ int tmp_fd;
+ char *tmp_namebuf;
+ char *tmp_oldfile = NULL;
+#endif
+ cf_print_status_t status;
+
+ args = (print_args_t *)g_object_get_data(G_OBJECT(ok_bt), PRINT_ARGS_KEY);
+
+ /* Check whether the range is valid. */
+ if (!range_check_validity(&args->range)) {
+ /* The range isn't valid; don't dismiss the print/export dialog box,
+ just leave it around so that the user can, after they
+ dismiss the alert box popped up for the error, try again. */
+ return;
+ }
+
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), PRINT_DEST_CB_KEY);
+ args->to_file = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button));
+
+ if (args->to_file) {
+ g_dest = gtk_entry_get_text(GTK_ENTRY(g_object_get_data(G_OBJECT(ok_bt),
+ PRINT_FILE_TE_KEY)));
+ if (!g_dest[0]) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Output to file, but no file specified.");
+ return;
+ }
+ g_free(args->file);
+ args->file = g_strdup(g_dest);
+ /* Save the directory name for future file dialogs. */
+ f_name = g_strdup(g_dest);
+ dirname = get_dirname(f_name); /* Overwrites f_name */
+ set_last_open_dir(dirname);
+ g_free(f_name);
+ } else {
+#ifdef _WIN32
+ win_printer = TRUE;
+ /* We currently don't have a function in util.h to create just a tempfile */
+ /* name, so simply create a tempfile using the "official" function, */
+ /* then delete this file again. After this, the name MUST be available. */
+ /* */
+ /* Don't use tmpnam() or such, as this will fail under some ACL */
+ /* circumstances: http://bugs.wireshark.org/bugzilla/show_bug.cgi?id=358 */
+ /* Also: tmpnam is "insecure" and should not be used. */
+ tmp_fd = create_tempfile(&tmp_namebuf, "wshprint");
+ if(tmp_fd == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Couldn't create a temporary file for printing:\n%s", tmp_namebuf);
+ return;
+ }
+ /* remember to restore these values later! */
+ tmp_oldfile = args->file;
+ args->file = g_strdup(tmp_namebuf);
+ ws_unlink(args->file);
+ args->to_file = TRUE;
+#else
+ g_free(args->cmd);
+ args->cmd = g_strdup(gtk_entry_get_text(GTK_ENTRY(g_object_get_data(G_OBJECT(ok_bt),
+ PRINT_CMD_TE_KEY))));
+#endif
+ }
+
+ args->format = PR_FMT_TEXT;
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), PRINT_PS_RB_KEY);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button)))
+ args->format = PR_FMT_PS;
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), PRINT_PDML_RB_KEY);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button)))
+ export_as_pdml = TRUE;
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), PRINT_PSML_RB_KEY);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button)))
+ export_as_psml = TRUE;
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), PRINT_CSV_RB_KEY);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button)))
+ export_as_csv = TRUE;
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), PRINT_CARRAYS_RB_KEY);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button)))
+ export_as_carrays = TRUE;
+
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), PRINT_SUMMARY_CB_KEY);
+ args->print_summary = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button));
+
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), PRINT_COLLAPSE_ALL_RB_KEY);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button))) {
+ args->print_dissections = print_dissections_collapsed;
+ }
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), PRINT_AS_DISPLAYED_RB_KEY);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button))) {
+ args->print_dissections = print_dissections_as_displayed;
+ }
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), PRINT_EXPAND_ALL_RB_KEY);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button))) {
+ args->print_dissections = print_dissections_expanded;
+ }
+
+ /* the details setting has priority over the radio buttons */
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), PRINT_DETAILS_CB_KEY);
+ if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button))) {
+ args->print_dissections = print_dissections_none;
+ }
+
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), PRINT_HEX_CB_KEY);
+ args->print_hex = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button));
+
+ button = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), PRINT_FORMFEED_CB_KEY);
+ args->print_formfeed = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button));
+
+
+ window_destroy(GTK_WIDGET(parent_w));
+
+ /* Now print/export the packets */
+ if (export_as_pdml)
+ status = cf_write_pdml_packets(&cfile, args);
+ else if (export_as_psml)
+ status = cf_write_psml_packets(&cfile, args);
+ else if (export_as_csv)
+ status = cf_write_csv_packets(&cfile, args);
+ else if (export_as_carrays)
+ status = cf_write_carrays_packets(&cfile, args);
+ else {
+ switch (args->format) {
+
+ case PR_FMT_TEXT:
+ if (args->to_file) {
+ args->stream = print_stream_text_new(TRUE, args->file);
+ if (args->stream == NULL) {
+ open_failure_alert_box(args->file, errno, TRUE);
+ return;
+ }
+ } else {
+ args->stream = print_stream_text_new(FALSE, args->cmd);
+ if (args->stream == NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Couldn't run print command %s.", args->cmd);
+ }
+ }
+ break;
+
+ case PR_FMT_PS:
+ if (args->to_file) {
+ args->stream = print_stream_ps_new(TRUE, args->file);
+ if (args->stream == NULL) {
+ open_failure_alert_box(args->file, errno, TRUE);
+ return;
+ }
+ } else {
+ args->stream = print_stream_ps_new(FALSE, args->cmd);
+ if (args->stream == NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Couldn't run print command %s.", args->cmd);
+ }
+ }
+ break;
+
+ default:
+ g_assert_not_reached();
+ return;
+ }
+ status = cf_print_packets(&cfile, args);
+ }
+ switch (status) {
+
+ case CF_PRINT_OK:
+ break;
+
+ case CF_PRINT_OPEN_ERROR:
+ if (args->to_file)
+ open_failure_alert_box(args->file, errno, TRUE);
+ else
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Couldn't run print command %s.",
+ args->cmd);
+ break;
+
+ case CF_PRINT_WRITE_ERROR:
+ if (args->to_file)
+ write_failure_alert_box(args->file, errno);
+ else
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Error writing to print command: %s", g_strerror(errno));
+ break;
+ }
+
+#ifdef _WIN32
+ if (win_printer) {
+ print_mswin(args->file);
+
+ /* trash temp file */
+ ws_remove(args->file);
+ g_free(args->file);
+
+ /* restore old settings */
+ args->to_file = FALSE;
+ args->file = tmp_oldfile;
+ }
+#endif
+}
+
+static void
+print_destroy_cb(GtkWidget *win, gpointer user_data)
+{
+ GtkWidget *fs;
+
+ /* Is there a file selection dialog associated with this
+ Print File dialog? */
+ fs = g_object_get_data(G_OBJECT(win), E_FILE_SEL_DIALOG_PTR_KEY);
+
+ if (fs != NULL) {
+ /* Yes. Destroy it. */
+ window_destroy(fs);
+ }
+
+ /* Note that we no longer have a "Print" dialog box. */
+ *((gpointer *) user_data) = NULL;
+}
diff --git a/ui/gtk/profile_dlg.c b/ui/gtk/profile_dlg.c
new file mode 100644
index 0000000000..362caa94f0
--- /dev/null
+++ b/ui/gtk/profile_dlg.c
@@ -0,0 +1,1372 @@
+/* profile_dlg.c
+ * Dialog box for profiles editing
+ * Stig Bjorlykke <stig@bjorlykke.org>, 2008
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#if GTK_CHECK_VERSION(3,0,0)
+# include <gdk/gdkkeysyms-compat.h>
+#endif
+
+#include <epan/filesystem.h>
+#include <epan/prefs.h>
+
+#include "../simple_dialog.h"
+#include <wsutil/file_util.h>
+
+#include "ui/gtk/main.h"
+#include "ui/gtk/menus.h"
+#include "ui/gtk/profile_dlg.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/recent.h"
+#include "ui/gtk/old-gtk-compat.h"
+enum {
+ NAME_COLUMN,
+ GLOBAL_COLUMN,
+ DATA_COLUMN,
+ NUM_COLUMNS
+};
+
+#define E_PROF_PROFILE_L_KEY "profile_profile_l"
+#define E_PROF_DEL_BT_KEY "profile_del_bt"
+#define E_PROF_NAME_TE_KEY "profile_name_te"
+
+static GtkWidget *global_profile_w = NULL;
+static GList *current_profiles = NULL;
+static GList *edited_profiles = NULL;
+
+#define PROF_STAT_DEFAULT 1
+#define PROF_STAT_EXISTS 2
+#define PROF_STAT_NEW 3
+#define PROF_STAT_CHANGED 4
+#define PROF_STAT_COPY 5
+
+#define PROF_OPERATION_NEW 1
+#define PROF_OPERATION_EDIT 2
+
+typedef struct {
+ char *name; /* profile name */
+ char *reference; /* profile reference */
+ int status;
+ gboolean is_global;
+ gboolean from_global;
+} profile_def;
+
+static GList *
+add_profile_entry(GList *fl, const char *profilename, const char *reference, int status,
+ gboolean is_global, gboolean from_global)
+{
+ profile_def *profile;
+
+ profile = (profile_def *) g_malloc(sizeof(profile_def));
+ profile->name = g_strdup(profilename);
+ profile->reference = g_strdup(reference);
+ profile->status = status;
+ profile->is_global = is_global;
+ profile->from_global = from_global;
+ return g_list_append(fl, profile);
+}
+
+static GList *
+remove_profile_entry(GList *fl, GList *fl_entry)
+{
+ profile_def *profile;
+
+ profile = (profile_def *) fl_entry->data;
+ g_free(profile->name);
+ g_free(profile->reference);
+ g_free(profile);
+ return g_list_remove_link(fl, fl_entry);
+}
+
+static const gchar *
+get_profile_parent (const gchar *profilename)
+{
+ GList *fl_entry = g_list_first(edited_profiles);
+ guint no_edited = g_list_length(edited_profiles);
+ profile_def *profile;
+ guint i;
+
+ if (fl_entry) {
+ /* We have edited profiles, find parent */
+ for (i = 0; i < no_edited; i++) {
+ while (fl_entry) {
+ profile = (profile_def *) fl_entry->data;
+ if (strcmp (profile->name, profilename) == 0) {
+ if ((profile->status == PROF_STAT_NEW) ||
+ (profile->reference == NULL)) {
+ /* Copy from a new profile */
+ return NULL;
+ } else {
+ /* Found a parent, use this */
+ profilename = profile->reference;
+ }
+ }
+ fl_entry = g_list_next(fl_entry);
+ }
+ fl_entry = g_list_first(edited_profiles);
+ }
+ }
+
+ return profilename;
+}
+
+static GList *
+add_to_profile_list(const char *name, const char *expression, int status,
+ gboolean is_global, gboolean from_global)
+{
+ edited_profiles = add_profile_entry(edited_profiles, name, expression, status,
+ is_global, from_global);
+
+ return g_list_last(edited_profiles);
+}
+
+static void
+remove_from_profile_list(GList *fl_entry)
+{
+ edited_profiles = remove_profile_entry(edited_profiles, fl_entry);
+}
+
+static void
+empty_profile_list(gboolean edit_list)
+{
+ GList **flpp;
+
+ if (edit_list) {
+ flpp = &edited_profiles;
+
+ while(*flpp) {
+ *flpp = remove_profile_entry(*flpp, g_list_first(*flpp));
+ }
+
+ g_assert(g_list_length(*flpp) == 0);
+ }
+
+ flpp = &current_profiles;
+
+ while(*flpp) {
+ *flpp = remove_profile_entry(*flpp, g_list_first(*flpp));
+ }
+
+ g_assert(g_list_length(*flpp) == 0);
+}
+
+static void
+copy_profile_list(void)
+{
+ GList *flp_src;
+ profile_def *profile;
+
+ flp_src = edited_profiles;
+
+ /* throw away the "old" destination list - a NULL list is ok here */
+ empty_profile_list(FALSE);
+
+ /* copy the list entries */
+ while(flp_src) {
+ profile = (flp_src)->data;
+
+ current_profiles = add_profile_entry(current_profiles, profile->name,
+ profile->reference, profile->status,
+ profile->is_global, profile->from_global);
+ flp_src = g_list_next(flp_src);
+ }
+}
+
+
+static GtkTreeIter *
+fill_list(GtkWidget *main_w)
+{
+ WS_DIR *dir; /* scanned directory */
+ WS_DIRENT *file; /* current file */
+ GList *fl_entry;
+ profile_def *profile;
+ GtkTreeView *profile_l;
+ GtkListStore *store;
+ GtkTreeIter iter, *l_select = NULL;
+ const gchar *profile_name = get_profile_name ();
+ const gchar *profiles_dir, *name;
+ gchar *filename;
+
+ profile_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY));
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(profile_l));
+
+ fl_entry = add_to_profile_list(DEFAULT_PROFILE, DEFAULT_PROFILE, PROF_STAT_DEFAULT, FALSE, FALSE);
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, NAME_COLUMN, DEFAULT_PROFILE, GLOBAL_COLUMN, FALSE, DATA_COLUMN, fl_entry, -1);
+ if (strcmp (profile_name, DEFAULT_PROFILE)==0) {
+ l_select = g_memdup(&iter, sizeof(iter));
+ }
+
+ /* fill in data */
+ profiles_dir = get_profiles_dir();
+ if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
+ while ((file = ws_dir_read_name(dir)) != NULL) {
+ name = ws_dir_get_name(file);
+ filename = g_strdup_printf ("%s%s%s", profiles_dir, G_DIR_SEPARATOR_S, name);
+
+ if (test_for_directory(filename) == EISDIR) {
+ fl_entry = add_to_profile_list(name, name, PROF_STAT_EXISTS, FALSE, FALSE);
+ profile = (profile_def *) fl_entry->data;
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, NAME_COLUMN, profile->name, GLOBAL_COLUMN, FALSE, DATA_COLUMN, fl_entry, -1);
+
+ if (profile->name) {
+ if (strcmp(profile_name, profile->name) == 0) {
+ /*
+ * XXX - We're assuming that we can just copy a GtkTreeIter
+ * and use it later without any crashes. This may not be a
+ * valid assumption.
+ */
+ l_select = g_memdup(&iter, sizeof(iter));
+ }
+ }
+ }
+ g_free (filename);
+ }
+ ws_dir_close (dir);
+ }
+
+ profiles_dir = get_global_profiles_dir();
+ if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
+ while ((file = ws_dir_read_name(dir)) != NULL) {
+ name = ws_dir_get_name(file);
+ filename = g_strdup_printf ("%s%s%s", profiles_dir, G_DIR_SEPARATOR_S, name);
+
+ if (test_for_directory(filename) == EISDIR) {
+ fl_entry = add_to_profile_list(name, name, PROF_STAT_EXISTS, TRUE, TRUE);
+ profile = (profile_def *) fl_entry->data;
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, NAME_COLUMN, profile->name, GLOBAL_COLUMN, TRUE, DATA_COLUMN, fl_entry, -1);
+ }
+ g_free (filename);
+ }
+ ws_dir_close (dir);
+ }
+
+ /* Make the current list and the edited list equal */
+ copy_profile_list ();
+
+ return l_select;
+}
+
+static gboolean
+profile_is_invalid_name(const gchar *name)
+{
+ gchar *message = NULL;
+
+#ifdef _WIN32
+ char *invalid_dir_char = "\\/:*?\"<>|";
+ gboolean invalid = FALSE;
+ int i;
+
+ for (i = 0; i < 9; i++) {
+ if (strchr(name, invalid_dir_char[i])) {
+ /* Invalid character in directory */
+ invalid = TRUE;
+ }
+ }
+ if (name[0] == '.' || name[strlen(name)-1] == '.') {
+ /* Profile name cannot start or end with period */
+ invalid = TRUE;
+ }
+ if (invalid) {
+ message = g_strdup_printf("start or end with period (.), or contain any of the following characters:\n"
+ " \\ / : * ? \" &lt; &gt; |");
+ }
+#else
+ if (strchr(name, '/')) {
+ /* Invalid character in directory */
+ message = g_strdup_printf("contain the '/' character.");
+ }
+#endif
+
+ if (message) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "A profile name cannot %s\nProfiles unchanged.", message);
+ g_free(message);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+profile_select(GtkWidget *main_w, GtkTreeView *profile_l, gboolean destroy)
+{
+ GList *fl_entry;
+ profile_def *profile;
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(profile_l));
+
+ if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ gtk_tree_model_get(model, &iter, DATA_COLUMN, &fl_entry, -1);
+ if (fl_entry) {
+ profile = (profile_def *) fl_entry->data;
+ if (profile_exists (profile->name, FALSE) || profile_exists (profile->name, TRUE)) {
+ /* The new profile exists, change */
+ change_configuration_profile (profile->name);
+ } else if (!profile_exists (get_profile_name(), FALSE)) {
+ /* The new profile does not exist, and the previous profile has
+ been deleted. Change to the default profile */
+ change_configuration_profile (NULL);
+ }
+ }
+ }
+
+ if (destroy) {
+ /*
+ * Destroy the profile dialog box.
+ */
+ empty_profile_list (TRUE);
+ window_destroy(main_w);
+ }
+}
+
+static void
+profile_apply(GtkWidget *main_w, GtkTreeView *profile_l, gboolean destroy)
+{
+ char *pf_dir_path, *pf_dir_path2, *pf_filename;
+ GList *fl1, *fl2;
+ profile_def *profile1, *profile2;
+ gboolean found;
+
+ /* First validate all profile names */
+ fl1 = g_list_first(edited_profiles);
+ while (fl1) {
+ profile1 = (profile_def *) fl1->data;
+ g_strstrip(profile1->name);
+ if (profile_is_invalid_name(profile1->name)) {
+ return;
+ }
+ fl1 = g_list_next(fl1);
+ }
+
+ /* Then do all copy profiles */
+ fl1 = g_list_first(edited_profiles);
+ while (fl1) {
+ profile1 = (profile_def *) fl1->data;
+ g_strstrip(profile1->name);
+ if (profile1->status == PROF_STAT_COPY) {
+ if (create_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't create directory\n\"%s\":\n%s.",
+ pf_dir_path, g_strerror(errno));
+
+ g_free(pf_dir_path);
+ }
+ profile1->status = PROF_STAT_EXISTS;
+
+ if (profile1->reference) {
+ if (copy_persconffile_profile(profile1->name, profile1->reference, profile1->from_global,
+ &pf_filename, &pf_dir_path, &pf_dir_path2) == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't copy file \"%s\" in directory\n\"%s\" to\n\"%s\":\n%s.",
+ pf_filename, pf_dir_path2, pf_dir_path, g_strerror(errno));
+
+ g_free(pf_filename);
+ g_free(pf_dir_path);
+ g_free(pf_dir_path2);
+ }
+ }
+
+ g_free (profile1->reference);
+ profile1->reference = g_strdup(profile1->name);
+ }
+ fl1 = g_list_next(fl1);
+ }
+
+
+ /* Then create new and rename changed */
+ fl1 = g_list_first(edited_profiles);
+ while (fl1) {
+ profile1 = (profile_def *) fl1->data;
+ g_strstrip(profile1->name);
+ if (profile1->status == PROF_STAT_NEW) {
+ /* We do not create a directory for the default profile */
+ if (strcmp(profile1->name, DEFAULT_PROFILE)!=0) {
+ if (create_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't create directory\n\"%s\":\n%s.",
+ pf_dir_path, g_strerror(errno));
+
+ g_free(pf_dir_path);
+ }
+ profile1->status = PROF_STAT_EXISTS;
+
+ g_free (profile1->reference);
+ profile1->reference = g_strdup(profile1->name);
+ }
+ } else if (profile1->status == PROF_STAT_CHANGED) {
+ if (strcmp(profile1->reference, profile1->name)!=0) {
+ /* Rename old profile directory to new */
+ if (rename_persconffile_profile(profile1->reference, profile1->name,
+ &pf_dir_path, &pf_dir_path2) == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't rename directory\n\"%s\" to\n\"%s\":\n%s.",
+ pf_dir_path, pf_dir_path2, g_strerror(errno));
+
+ g_free(pf_dir_path);
+ g_free(pf_dir_path2);
+ }
+ profile1->status = PROF_STAT_EXISTS;
+ g_free (profile1->reference);
+ profile1->reference = g_strdup(profile1->name);
+ }
+ }
+ fl1 = g_list_next(fl1);
+ }
+
+ /* Last remove deleted */
+ fl1 = g_list_first(current_profiles);
+ while (fl1) {
+ found = FALSE;
+ profile1 = (profile_def *) fl1->data;
+ fl2 = g_list_first(edited_profiles);
+ while (fl2) {
+ profile2 = (profile_def *) fl2->data;
+ if (!profile2->is_global) {
+ if (strcmp(profile1->name, profile2->name)==0) {
+ /* Profile exists in both lists */
+ found = TRUE;
+ } else if (strcmp(profile1->name, profile2->reference)==0) {
+ /* Profile has been renamed */
+ found = TRUE;
+ }
+ }
+ fl2 = fl2->next;
+ }
+ if (!found) {
+ /* Exists in existing list and not in edited, this is a deleted profile */
+ if (delete_persconffile_profile(profile1->name, &pf_dir_path) == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't delete profile directory\n\"%s\":\n%s.",
+ pf_dir_path, g_strerror(errno));
+
+ g_free(pf_dir_path);
+ }
+ }
+ fl1 = g_list_next(fl1);
+ }
+
+ copy_profile_list();
+ profile_select(main_w, profile_l, destroy);
+}
+
+static void
+profile_dlg_ok_cb(GtkWidget *ok_bt, gpointer data _U_)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(ok_bt);
+ GtkTreeView *profile_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY));
+
+ /*
+ * Apply the profile and destroy the dialog box.
+ */
+ profile_apply(main_w, profile_l, TRUE);
+}
+
+static void
+profile_dlg_apply_cb(GtkWidget *apply_bt, gpointer data _U_)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(apply_bt);
+ GtkTreeView *profile_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY));
+
+ /*
+ * Apply the profile, but don't destroy the dialog box.
+ */
+ profile_apply(main_w, profile_l, FALSE);
+}
+
+/* cancel button pressed, revert changes and exit dialog */
+static void
+profile_dlg_cancel_cb(GtkWidget *cancel_bt, gpointer data _U_)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(cancel_bt);
+
+ empty_profile_list (TRUE);
+ window_destroy(GTK_WIDGET(main_w));
+}
+
+/* Treat this as a cancel, by calling "profile_dlg_cancel_cb()" */
+static gboolean
+profile_dlg_delete_event_cb(GtkWidget *main_w, GdkEvent *event _U_,
+ gpointer data)
+{
+ profile_dlg_cancel_cb(main_w, data);
+ return FALSE;
+}
+
+static void
+profile_dlg_destroy_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ global_profile_w = NULL;
+}
+
+
+static gboolean
+profile_button_press_cb(GtkWidget *list, GdkEventButton *event, gpointer data _U_)
+{
+ if (event->type == GDK_2BUTTON_PRESS) {
+ GtkWidget *main_w = gtk_widget_get_toplevel(list);
+
+ profile_apply (main_w, GTK_TREE_VIEW(list), TRUE);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+profile_key_release_cb(GtkWidget *list, GdkEventKey *event, gpointer data _U_)
+{
+ if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter) {
+ GtkWidget *main_w = gtk_widget_get_toplevel(list);
+
+ profile_apply (main_w, GTK_TREE_VIEW(list), TRUE);
+ }
+
+ return FALSE;
+}
+
+static void
+profile_sel_list_cb(GtkTreeSelection *sel, gpointer data _U_)
+{
+ GtkWidget *profile_l = GTK_WIDGET(gtk_tree_selection_get_tree_view(sel));
+ GtkWidget *main_w = gtk_widget_get_toplevel(profile_l);
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkWidget *name_te = g_object_get_data(G_OBJECT(main_w), E_PROF_NAME_TE_KEY);
+ GtkWidget *del_bt = g_object_get_data(G_OBJECT(main_w), E_PROF_DEL_BT_KEY);
+ profile_def *profile;
+ gchar *name = NULL;
+ GList *fl_entry;
+ gint sensitivity = FALSE;
+
+ if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ gtk_tree_model_get(model, &iter, DATA_COLUMN, &fl_entry, -1);
+ if (fl_entry) {
+ profile = (profile_def *) fl_entry->data;
+ name = g_strdup(profile->name);
+ if ((profile->status != PROF_STAT_DEFAULT) && !profile->is_global) {
+ sensitivity = TRUE;
+ }
+ }
+ }
+
+ /*
+ * Did you know that this function is called when the window is destroyed?
+ * Funny, that.
+ * This means that we have to:
+ *
+ * attach to the top-level window data items containing pointers to
+ * the widgets we affect here;
+ *
+ * give each of those widgets their own destroy callbacks;
+ *
+ * clear that pointer when the widget is destroyed;
+ *
+ * don't do anything to the widget if the pointer we get back is
+ * null;
+ *
+ * so that if we're called after any of the widgets we'd affect are
+ * destroyed, we know that we shouldn't do anything to those widgets.
+ */
+ if (name_te != NULL) {
+ gtk_entry_set_text(GTK_ENTRY(name_te), name ? name : "");
+ gtk_widget_set_sensitive(name_te, sensitivity);
+ }
+ if (del_bt != NULL)
+ gtk_widget_set_sensitive(del_bt, sensitivity);
+ g_free(name);
+}
+
+static void
+profile_new_bt_clicked_cb(GtkWidget *w, gpointer data _U_)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(w);
+ GtkWidget *name_te = g_object_get_data(G_OBJECT(main_w), E_PROF_NAME_TE_KEY);
+ GtkTreeView *profile_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY));
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GList *fl_entry;
+ const gchar *name = "New profile";
+
+ /* Add a new entry to the profile list. */
+ fl_entry = add_to_profile_list(name, "", PROF_STAT_NEW, FALSE, FALSE);
+
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(profile_l));
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, NAME_COLUMN, name, GLOBAL_COLUMN, FALSE, DATA_COLUMN, fl_entry, -1);
+ /* Select the item. */
+ gtk_tree_selection_select_iter(gtk_tree_view_get_selection(profile_l), &iter);
+
+ gtk_editable_select_region(GTK_EDITABLE(name_te), 0, -1);
+ gtk_widget_grab_focus(name_te);
+}
+
+static void
+profile_copy_bt_clicked_cb(GtkWidget *w, gpointer data _U_)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(w);
+ GtkWidget *name_te = g_object_get_data(G_OBJECT(main_w), E_PROF_NAME_TE_KEY);
+ GtkTreeView *profile_l = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY));
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GList *fl_entry;
+ const gchar *name = gtk_entry_get_text(GTK_ENTRY(name_te));
+ const gchar *parent = NULL;
+ gchar *new_name;
+
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ profile_def *profile = NULL;
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(profile_l));
+ if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ gtk_tree_model_get(model, &iter, DATA_COLUMN, &fl_entry, -1);
+ if (fl_entry) {
+ profile = (profile_def *) fl_entry->data;
+ }
+ }
+
+ if (profile && profile->is_global) {
+ parent = profile->name;
+ } else {
+ parent = get_profile_parent (name);
+ }
+
+ if (profile && profile->is_global && !profile_exists (parent, FALSE)) {
+ new_name = g_strdup (name);
+ } else {
+ new_name = g_strdup_printf ("%s (copy)", name);
+ }
+
+ /* Add a new entry to the profile list. */
+ fl_entry = add_to_profile_list(new_name, parent, PROF_STAT_COPY, FALSE, profile ? profile->from_global : FALSE);
+
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(profile_l));
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, NAME_COLUMN, new_name, GLOBAL_COLUMN, FALSE, DATA_COLUMN, fl_entry, -1);
+ /* Select the item. */
+ gtk_tree_selection_select_iter(gtk_tree_view_get_selection(profile_l), &iter);
+
+ gtk_editable_select_region(GTK_EDITABLE(name_te), 0, -1);
+ gtk_widget_grab_focus(name_te);
+
+ g_free (new_name);
+}
+
+static void
+profile_name_te_changed_cb(GtkWidget *w, gpointer data _U_)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(w);
+ GtkWidget *name_te = g_object_get_data(G_OBJECT(main_w), E_PROF_NAME_TE_KEY);
+ GtkWidget *profile_l = g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY);
+ profile_def *profile;
+ GList *fl_entry;
+ const gchar *name = "";
+
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(profile_l));
+ name = gtk_entry_get_text(GTK_ENTRY(name_te));
+
+ /* if something was selected */
+ if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ gtk_tree_model_get(model, &iter, DATA_COLUMN, &fl_entry, -1);
+ if (fl_entry != NULL) {
+ profile = (profile_def *) fl_entry->data;
+
+ if (strlen(name) > 0 && profile && !profile->is_global) {
+ if (profile->status != PROF_STAT_DEFAULT) {
+ g_free(profile->name);
+ profile->name = g_strdup(name);
+ if ((profile->status != PROF_STAT_NEW) &&
+ (profile->status != PROF_STAT_COPY)) {
+ profile->status = PROF_STAT_CHANGED;
+ }
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, NAME_COLUMN, name, -1);
+ }
+ }
+ }
+ }
+}
+
+static void
+profile_del_bt_clicked_cb(GtkWidget *w, gpointer data _U_)
+{
+ GtkWidget *main_w = gtk_widget_get_toplevel(w);
+ GtkWidget *profile_l = g_object_get_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY);
+ GList *fl_entry;
+
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(profile_l));
+ /* If something was selected */
+ if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ gtk_tree_model_get(model, &iter, DATA_COLUMN, &fl_entry, -1);
+
+ if (fl_entry != NULL) {
+ remove_from_profile_list (fl_entry);
+ gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
+ }
+ }
+
+ if (gtk_tree_model_get_iter_first (model, &iter)) {
+ gtk_tree_selection_select_iter(sel, &iter);
+ }
+}
+
+static GtkWidget *
+profile_dialog_new(void)
+{
+ GtkWidget *main_w, /* main window */
+ *main_vb, /* main container */
+ *bbox, /* button container */
+ *ok_bt, /* "OK" button */
+ *apply_bt, /* "Apply" button */
+ *cancel_bt, /* "Cancel" button */
+ *help_bt; /* "Help" button */
+ GtkWidget *profile_vb, /* profile settings box */
+ *props_vb;
+ GtkWidget *top_hb,
+ *list_bb,
+ *new_bt,
+ *copy_bt,
+ *del_bt,
+ *profile_sc,
+ *profile_l,
+ *middle_hb,
+ *name_lb,
+ *name_te,
+ *profile_fr,
+ *edit_fr,
+ *props_fr;
+ GtkListStore *store;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *sel;
+ GtkTreeIter *l_select;
+ gboolean has_global = has_global_profiles();
+
+ /* Get a pointer to a static variable holding the type of profile on
+ which we're working, so we can pass that pointer to callback
+ routines. */
+
+ main_w = dlg_conf_window_new("Wireshark: Configuration Profiles");
+ gtk_window_set_default_size(GTK_WINDOW(main_w), 400, 400);
+
+ main_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(main_w), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* Container for each row of widgets */
+ profile_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(profile_vb), 0);
+ gtk_container_add(GTK_CONTAINER(main_vb), profile_vb);
+ gtk_widget_show(profile_vb);
+
+ /* Top row: Buttons and profile list */
+ top_hb = gtk_hbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(profile_vb), top_hb);
+ gtk_widget_show(top_hb);
+
+ edit_fr = gtk_frame_new("Edit");
+ gtk_box_pack_start(GTK_BOX(top_hb), edit_fr, FALSE, FALSE, 0);
+ gtk_widget_show(edit_fr);
+
+ list_bb = gtk_vbox_new(TRUE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(list_bb), 5);
+ gtk_container_add(GTK_CONTAINER(edit_fr), list_bb);
+ gtk_widget_show(list_bb);
+
+ new_bt = gtk_button_new_from_stock(GTK_STOCK_NEW);
+ g_signal_connect(new_bt, "clicked", G_CALLBACK(profile_new_bt_clicked_cb), NULL);
+ gtk_widget_show(new_bt);
+ gtk_box_pack_start (GTK_BOX (list_bb), new_bt, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text (new_bt, "Create a new profile (with default properties)");
+
+ copy_bt = gtk_button_new_from_stock(GTK_STOCK_COPY);
+ g_signal_connect(copy_bt, "clicked", G_CALLBACK(profile_copy_bt_clicked_cb), NULL);
+ gtk_widget_show(copy_bt);
+ gtk_box_pack_start (GTK_BOX (list_bb), copy_bt, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text (copy_bt, "Copy the selected profile");
+
+ del_bt = gtk_button_new_from_stock(GTK_STOCK_DELETE);
+ gtk_widget_set_sensitive(del_bt, FALSE);
+ g_signal_connect(del_bt, "clicked", G_CALLBACK(profile_del_bt_clicked_cb), NULL);
+ g_object_set_data(G_OBJECT(main_w), E_PROF_DEL_BT_KEY, del_bt);
+ gtk_widget_show(del_bt);
+ gtk_box_pack_start (GTK_BOX (list_bb), del_bt, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text (del_bt, "Delete the selected profile");
+
+ profile_fr = gtk_frame_new("Configuration Profiles");
+ gtk_box_pack_start(GTK_BOX(top_hb), profile_fr, TRUE, TRUE, 0);
+ gtk_widget_show(profile_fr);
+
+ profile_sc = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(profile_sc),
+ GTK_SHADOW_IN);
+
+ gtk_container_set_border_width (GTK_CONTAINER (profile_sc), 5);
+ gtk_container_add(GTK_CONTAINER(profile_fr), profile_sc);
+ gtk_widget_show(profile_sc);
+
+ store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER);
+ profile_l = tree_view_new(GTK_TREE_MODEL(store));
+ /* Only show headers if having more than one column */
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(profile_l), has_global);
+
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Name", renderer, "text", NAME_COLUMN, NULL);
+ gtk_tree_view_column_set_expand(column, TRUE);
+ gtk_tree_view_column_set_sort_column_id(column, NAME_COLUMN);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(profile_l), column);
+
+ renderer = gtk_cell_renderer_toggle_new();
+ column = gtk_tree_view_column_new_with_attributes("Global", renderer, "active", GLOBAL_COLUMN, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(profile_l), column);
+ gtk_widget_set_tooltip_text(gtk_tree_view_column_get_button(column), "Global profiles will be copied to users profiles when used");
+ gtk_tree_view_column_set_visible(column, has_global);
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(profile_l));
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+ g_signal_connect(sel, "changed", G_CALLBACK(profile_sel_list_cb), profile_vb);
+ g_signal_connect(profile_l, "button_press_event", G_CALLBACK(profile_button_press_cb), NULL);
+ g_signal_connect(profile_l, "key_release_event", G_CALLBACK(profile_key_release_cb), NULL);
+ g_object_set_data(G_OBJECT(main_w), E_PROF_PROFILE_L_KEY, profile_l);
+ gtk_container_add(GTK_CONTAINER(profile_sc), profile_l);
+ gtk_widget_show(profile_l);
+
+ /* fill in data */
+ l_select = fill_list(main_w);
+
+ g_object_unref(G_OBJECT(store));
+
+ props_fr = gtk_frame_new("Properties");
+ gtk_box_pack_start(GTK_BOX(profile_vb), props_fr, FALSE, FALSE, 0);
+ gtk_widget_show(props_fr);
+
+ props_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(props_vb), 5);
+ gtk_container_add(GTK_CONTAINER(props_fr), props_vb);
+ gtk_widget_show(props_vb);
+
+ /* row: Profile name entry */
+ middle_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(props_vb), middle_hb);
+ gtk_widget_show(middle_hb);
+
+ name_lb = gtk_label_new("Profile name:");
+ gtk_box_pack_start(GTK_BOX(middle_hb), name_lb, FALSE, FALSE, 0);
+ gtk_widget_show(name_lb);
+
+ name_te = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(middle_hb), name_te, TRUE, TRUE, 0);
+ g_object_set_data(G_OBJECT(main_w), E_PROF_NAME_TE_KEY, name_te);
+ g_signal_connect(name_te, "changed", G_CALLBACK(profile_name_te_changed_cb), NULL);
+#ifdef _WIN32
+ gtk_widget_set_tooltip_text (name_te, "A profile name cannot start or end with a period (.), and cannot contain any of the following characters:\n \\ / : * ? \" < > |");
+#else
+ gtk_widget_set_tooltip_text (name_te, "A profile name cannot contain the '/' character");
+#endif
+ gtk_widget_show(name_te);
+
+ /* button row (create all possible buttons and hide the unrequired later - it's a lot easier) */
+ bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 5);
+ gtk_widget_show(bbox);
+
+ ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(profile_dlg_ok_cb), NULL);
+ gtk_widget_set_tooltip_text (ok_bt, "Apply the profiles and close this dialog");
+
+ /* Catch the "activate" signal on the profile name and profile
+ list entries, so that if the user types Return
+ there, we act as if the "OK" button had been selected, as
+ happens if Return is typed if some widget that *doesn't*
+ handle the Return key has the input focus. */
+ dlg_set_activate(name_te, ok_bt);
+
+ apply_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_APPLY);
+ g_signal_connect(apply_bt, "clicked", G_CALLBACK(profile_dlg_apply_cb), NULL);
+ gtk_widget_set_tooltip_text (apply_bt, "Apply the profiles and keep this dialog open");
+
+ cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ gtk_widget_set_tooltip_text (cancel_bt, "Cancel the changes");
+ g_signal_connect(cancel_bt, "clicked", G_CALLBACK(profile_dlg_cancel_cb), NULL);
+ window_set_cancel_button(main_w, cancel_bt, NULL);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_CONFIG_PROFILES_DIALOG);
+ gtk_widget_set_tooltip_text (help_bt, "Show topic specific help");
+
+ if(ok_bt) {
+ gtk_widget_grab_default(ok_bt);
+ }
+
+
+ /* DO SELECTION THINGS *AFTER* SHOWING THE DIALOG! */
+ /* otherwise the updatings can get confused */
+ if (l_select) {
+ gtk_tree_selection_select_iter(sel, l_select);
+ g_free(l_select);
+ }
+
+ if (profile_l) {
+ gtk_widget_grab_focus(profile_l);
+ }
+
+ g_signal_connect(main_w, "delete_event", G_CALLBACK(profile_dlg_delete_event_cb), NULL);
+ g_signal_connect(main_w, "destroy", G_CALLBACK(profile_dlg_destroy_cb), NULL);
+
+ gtk_widget_show(main_w);
+
+ window_present(main_w);
+
+ return main_w;
+}
+
+
+static void
+select_profile_cb (GtkWidget *w _U_, gpointer data)
+{
+ const gchar *current_profile = get_profile_name ();
+ gchar *selected_profile = (gchar *) data;
+
+ if (strcmp (selected_profile, current_profile) != 0) {
+ change_configuration_profile (selected_profile);
+ }
+}
+
+gboolean
+profile_show_popup_cb (GtkWidget *w _U_, GdkEvent *event, gpointer user_data _U_)
+{
+ GdkEventButton *bevent = (GdkEventButton *)event;
+ const gchar *profile_name = get_profile_name ();
+ const gchar *profiles_dir, *name;
+ WS_DIR *dir; /* scanned directory */
+ WS_DIRENT *file; /* current file */
+ GtkWidget *menu;
+ GtkWidget *menu_item;
+
+ menu = gtk_menu_new ();
+
+ if (bevent->button != 1) {
+ GtkWidget *change_menu = menus_get_profiles_change_menu ();
+
+#if GTK_CHECK_VERSION(2,16,0)
+ GtkWidget *edit_menu = menus_get_profiles_edit_menu ();
+ GtkWidget *delete_menu = menus_get_profiles_delete_menu ();
+ if (strcmp (profile_name, DEFAULT_PROFILE) != 0) {
+ gchar *label;
+ label = g_strdup_printf ("Edit \"%s\"...", profile_name);
+ gtk_menu_item_set_label (GTK_MENU_ITEM(edit_menu), label);
+ g_free (label);
+ label = g_strdup_printf ("Delete \"%s\"", profile_name);
+ gtk_menu_item_set_label (GTK_MENU_ITEM(delete_menu), label);
+ g_free (label);
+ } else {
+ gtk_menu_item_set_label (GTK_MENU_ITEM(edit_menu), "Edit...");
+ gtk_menu_item_set_label (GTK_MENU_ITEM(delete_menu), "Delete");
+ }
+#endif
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM(change_menu), menu);
+ }
+
+ /* Add a menu item for the Default profile */
+ menu_item = gtk_check_menu_item_new_with_label (DEFAULT_PROFILE);
+ if (strcmp (profile_name, DEFAULT_PROFILE) == 0) {
+ /* Check current profile */
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(menu_item), TRUE);
+ }
+ g_object_set (G_OBJECT(menu_item), "draw-as-radio", TRUE, NULL);
+ g_signal_connect (menu_item, "activate", G_CALLBACK(select_profile_cb), g_strdup (DEFAULT_PROFILE));
+ gtk_menu_shell_append (GTK_MENU_SHELL(menu), menu_item);
+ gtk_widget_show (menu_item);
+
+ profiles_dir = get_profiles_dir();
+ if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
+ while ((file = ws_dir_read_name(dir)) != NULL) {
+ name = ws_dir_get_name(file);
+
+ if (profile_exists(name, FALSE)) {
+ menu_item = gtk_check_menu_item_new_with_label (name);
+ if (strcmp (name, profile_name)==0) {
+ /* Check current profile */
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(menu_item), TRUE);
+ }
+ g_object_set (G_OBJECT(menu_item), "draw-as-radio", TRUE, NULL);
+ g_signal_connect (menu_item, "activate", G_CALLBACK(select_profile_cb), g_strdup (name));
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
+ gtk_widget_show (menu_item);
+ }
+ }
+ ws_dir_close (dir);
+ }
+
+ profiles_dir = get_global_profiles_dir();
+ if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
+ GtkWidget *sub_menu = NULL;
+ gboolean added_submenu = FALSE;
+
+ while ((file = ws_dir_read_name(dir)) != NULL) {
+ name = ws_dir_get_name(file);
+
+ if (profile_exists(name, TRUE)) {
+ if (!added_submenu) {
+ menu_item = gtk_separator_menu_item_new ();
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
+ gtk_widget_show (menu_item);
+
+ menu_item = gtk_menu_item_new_with_label ("New from Global");
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
+ gtk_widget_show (menu_item);
+
+ sub_menu = gtk_menu_new ();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM(menu_item), sub_menu);
+
+ added_submenu = TRUE;
+ }
+
+ menu_item = gtk_menu_item_new_with_label (name);
+ g_signal_connect (menu_item, "activate", G_CALLBACK(select_profile_cb), g_strdup (name));
+ if (profile_exists(name, FALSE)) {
+ gtk_widget_set_sensitive(menu_item, FALSE);
+ }
+ gtk_menu_shell_append (GTK_MENU_SHELL (sub_menu), menu_item);
+ gtk_widget_show (menu_item);
+ }
+ }
+ ws_dir_close (dir);
+ }
+
+ if (bevent->button != 1) {
+ /* Second-click is handled in popup_menu_handler() */
+ return FALSE;
+ }
+
+ gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL,
+ bevent->button, bevent->time);
+
+ return TRUE;
+}
+
+static void
+profile_name_edit_ok (GtkWidget *w _U_, gpointer parent_w)
+{
+ gint operation = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(w), "operation"));
+ GtkComboBox *combo_box = g_object_get_data (G_OBJECT(w), "create_from");
+ GtkWidget *entry = g_object_get_data (G_OBJECT(w), "entry");
+ GtkTreeStore *store;
+ GtkTreeIter iter;
+ const gchar *new_name = gtk_entry_get_text(GTK_ENTRY(entry));
+ const gchar *profile_name = "";
+ gboolean from_global = FALSE;
+ char *pf_dir_path, *pf_dir_path2, *pf_filename;
+
+ if (strlen(new_name) == 0 || profile_is_invalid_name(new_name)) {
+ return;
+ }
+
+ switch (operation) {
+ case PROF_OPERATION_NEW:
+ if (gtk_combo_box_get_active_iter(combo_box, &iter)) {
+ store = GTK_TREE_STORE(gtk_combo_box_get_model(combo_box));
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 0, &profile_name, 1, &from_global, -1);
+ }
+ break;
+ case PROF_OPERATION_EDIT:
+ profile_name = get_profile_name();
+ if (strcmp(new_name, profile_name) == 0) {
+ /* Rename without a change, do nothing */
+ window_destroy(GTK_WIDGET(parent_w));
+ return;
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (profile_exists (new_name, FALSE)) {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "The profile already exists:\n%s.", new_name);
+ return;
+ }
+
+ /* Write recent file for profile we are leaving */
+ write_profile_recent();
+
+ switch (operation) {
+ case PROF_OPERATION_NEW:
+ if (create_persconffile_profile(new_name, &pf_dir_path) == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't create directory\n\"%s\":\n%s.",
+ pf_dir_path, g_strerror(errno));
+
+ g_free(pf_dir_path);
+ } else if (strlen (profile_name) &&
+ copy_persconffile_profile(new_name, profile_name, from_global, &pf_filename,
+ &pf_dir_path, &pf_dir_path2) == -1)
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't copy file \"%s\" in directory\n\"%s\" to\n\"%s\":\n%s.",
+ pf_filename, pf_dir_path2, pf_dir_path, g_strerror(errno));
+
+ g_free(pf_filename);
+ g_free(pf_dir_path);
+ g_free(pf_dir_path2);
+ } else {
+ change_configuration_profile (new_name);
+ }
+ break;
+ case PROF_OPERATION_EDIT:
+ if (rename_persconffile_profile(profile_name, new_name,
+ &pf_dir_path, &pf_dir_path2) == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't rename directory\n\"%s\" to\n\"%s\":\n%s.",
+ pf_dir_path, pf_dir_path2, g_strerror(errno));
+
+ g_free(pf_dir_path);
+ g_free(pf_dir_path2);
+ } else {
+ change_configuration_profile (new_name);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ window_destroy(GTK_WIDGET(parent_w));
+}
+
+static void
+profile_name_edit_cancel (GtkWidget *w _U_, gpointer parent_w)
+{
+ window_destroy(GTK_WIDGET(parent_w));
+}
+
+static void
+profile_name_edit_dlg (gint operation)
+{
+ WS_DIR *dir; /* scanned directory */
+ WS_DIRENT *file; /* current file */
+ GtkWidget *win, *main_tb, *main_vb, *bbox, *cancel_bt, *ok_bt;
+ GtkWidget *entry, *label, *combo_box=NULL;
+ GtkCellRenderer *cell;
+ GtkTreeStore *store;
+ GtkTreeIter iter, parent;
+ gchar *window_title=NULL;
+ const gchar *profile_name, *profiles_dir, *name;
+ gboolean has_global = has_global_profiles();
+
+ profile_name = get_profile_name();
+
+ switch (operation) {
+ case PROF_OPERATION_NEW:
+ window_title = g_strdup ("Create New Profile");
+ break;
+ case PROF_OPERATION_EDIT:
+ window_title = g_strdup_printf ("Edit: %s", profile_name);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ win = dlg_window_new(window_title);
+ g_free (window_title);
+
+ gtk_window_set_resizable(GTK_WINDOW(win),FALSE);
+ gtk_window_resize(GTK_WINDOW(win), 400, 100);
+
+ main_vb = gtk_vbox_new(FALSE, 5);
+ gtk_container_add(GTK_CONTAINER(win), main_vb);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 6);
+
+ main_tb = gtk_table_new(2, 2, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 10);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 5);
+
+ if (operation == PROF_OPERATION_NEW) {
+ label = gtk_label_new("Create from:");
+ gtk_widget_set_tooltip_text (label, "All configuration files will be copied from this profile");
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, 0, 1);
+ gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
+
+ store = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
+ combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL (store));
+ gtk_widget_set_tooltip_text (combo_box, "All configuration files will be copied from this profile");
+
+ cell = gtk_cell_renderer_text_new();
+ gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), cell, TRUE);
+ gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell,
+ "text", 0, "sensitive", 2,
+ NULL);
+
+ gtk_tree_store_append(store, &iter, NULL);
+ gtk_tree_store_set(store, &iter, 0, "", 1, FALSE, 2, TRUE, -1);
+
+ if (has_global) {
+ gtk_tree_store_append(store, &parent, NULL);
+ gtk_tree_store_set(store, &parent, 0, "Personal", 1, FALSE, 2, FALSE, -1);
+ }
+
+ gtk_tree_store_append(store, &iter, has_global ? &parent : NULL);
+ gtk_tree_store_set(store, &iter, 0, DEFAULT_PROFILE, 1, FALSE, 2, TRUE, -1);
+ profiles_dir = get_profiles_dir();
+ if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
+ while ((file = ws_dir_read_name(dir)) != NULL) {
+ name = ws_dir_get_name(file);
+ if (profile_exists(name, FALSE)) {
+ gtk_tree_store_append(store, &iter, has_global ? &parent : NULL);
+ gtk_tree_store_set(store, &iter, 0, name, 1, FALSE, 2, TRUE, -1);
+ }
+ }
+ ws_dir_close (dir);
+ }
+
+ if (has_global) {
+ gtk_tree_store_append(store, &parent, NULL);
+ gtk_tree_store_set(store, &parent, 0, "Global", 1, FALSE, 2, FALSE, -1);
+ profiles_dir = get_global_profiles_dir();
+ if ((dir = ws_dir_open(profiles_dir, 0, NULL)) != NULL) {
+ while ((file = ws_dir_read_name(dir)) != NULL) {
+ name = ws_dir_get_name(file);
+ if (profile_exists(name, TRUE)) {
+ gtk_tree_store_append(store, &iter, &parent);
+ gtk_tree_store_set(store, &iter, 0, name, 1, TRUE, 2, TRUE, -1);
+ }
+ }
+ ws_dir_close (dir);
+ }
+ }
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), combo_box, 1, 2, 0, 1);
+ g_object_unref(store);
+ }
+
+ label = gtk_label_new("Profile name:");
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, 1, 2);
+ gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
+
+ entry = gtk_entry_new();
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), entry, 1, 2, 1, 2);
+ switch (operation) {
+ case PROF_OPERATION_NEW:
+ gtk_entry_set_text(GTK_ENTRY(entry), "New profile");
+ break;
+ case PROF_OPERATION_EDIT:
+ gtk_entry_set_text(GTK_ENTRY(entry), profile_name);
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+#ifdef _WIN32
+ gtk_widget_set_tooltip_text (entry, "A profile name cannot start or end with a period (.), and cannot contain any of the following characters:\n \\ / : * ? \" < > |");
+#else
+ gtk_widget_set_tooltip_text (entry, "A profile name cannot contain the '/' character");
+#endif
+
+ bbox = dlg_button_row_new(GTK_STOCK_CANCEL,GTK_STOCK_OK, NULL);
+ gtk_box_pack_end(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+
+ ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ g_object_set_data (G_OBJECT(ok_bt), "entry", entry);
+ g_object_set_data (G_OBJECT(ok_bt), "create_from", combo_box);
+ g_object_set_data (G_OBJECT(ok_bt), "operation", GINT_TO_POINTER(operation));
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(profile_name_edit_ok), win);
+
+ dlg_set_activate(entry, ok_bt);
+ gtk_widget_grab_focus(entry);
+
+ cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ g_signal_connect(cancel_bt, "clicked", G_CALLBACK(profile_name_edit_cancel), win);
+ window_set_cancel_button(win, cancel_bt, NULL);
+
+ gtk_widget_grab_default(ok_bt);
+ gtk_widget_show_all(win);
+}
+
+void
+profile_new_cb (GtkWidget *w _U_, gpointer data _U_)
+{
+ profile_name_edit_dlg (PROF_OPERATION_NEW);
+}
+
+void
+profile_delete_cb (GtkWidget *w _U_, gpointer data _U_)
+{
+ const gchar *name = get_profile_name();
+ char *pf_dir_path;
+
+ if (profile_exists(name, FALSE) && strcmp (name, DEFAULT_PROFILE) != 0) {
+ if (delete_persconffile_profile(name, &pf_dir_path) == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't delete profile directory\n\"%s\":\n%s.",
+ pf_dir_path, g_strerror(errno));
+
+ g_free(pf_dir_path);
+ }
+
+ /* Change to the default profile */
+ change_configuration_profile (NULL);
+ }
+}
+
+void
+profile_edit_cb (GtkWidget *w _U_, gpointer data _U_)
+{
+ profile_name_edit_dlg (PROF_OPERATION_EDIT);
+}
+
+/* Create a profile dialog for editing display profiles; this is to be used
+ as a callback for menu items, toolbars, etc.. */
+void
+profile_dialog_cb(GtkWidget *w _U_)
+{
+ /* Has a profiles dialog box already been opened */
+ if (global_profile_w != NULL) {
+ /* Yes. Just reactivate it. */
+ reactivate_window(global_profile_w);
+ } else {
+ global_profile_w = profile_dialog_new ();
+ }
+}
+
diff --git a/ui/gtk/profile_dlg.h b/ui/gtk/profile_dlg.h
new file mode 100644
index 0000000000..934e2c4ce7
--- /dev/null
+++ b/ui/gtk/profile_dlg.h
@@ -0,0 +1,69 @@
+/* profile_dlg.h
+ * Definitions for dialog box for profiles editing.
+ * Stig Bjorlykke <stig@bjorlykke.org>, 2008
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PROFILE_DLG_H__
+#define __PROFILE_DLG_H__
+
+/** @file
+ * "Configuration Profiles" dialog box
+ * @ingroup dialog_group
+ */
+
+/** User requested the "Configuration Profiles" popup menu.
+ *
+ * @param w parent widget
+ * @param event button event
+ * @param user_data pointer to user_data (unused)
+ */
+gboolean profile_show_popup_cb(GtkWidget *w _U_, GdkEvent *event, gpointer user_data _U_);
+
+/** User requested to create a new profile.
+ *
+ * @param w parent widget (unused)
+ * @param data pointer to user_data (unused)
+ */
+void profile_new_cb (GtkWidget *w _U_, gpointer data _U_);
+
+/** User requested to delete the current profile.
+ *
+ * @param w parent widget (unused)
+ * @param data pointer to user_data (unused)
+ */
+void profile_delete_cb (GtkWidget *w _U_, gpointer data _U_);
+
+/** User requested to edit the current profile.
+ *
+ * @param w parent widget (unused)
+ * @param data pointer to user_data (unused)
+ */
+void profile_edit_cb (GtkWidget *w _U_, gpointer data _U_);
+
+/** User requested the "Configuration Profiles" dialog box by menu or toolbar.
+ *
+ * @param widget parent widget
+ */
+void profile_dialog_cb(GtkWidget *widget);
+
+#endif /* profile_dlg.h */
diff --git a/ui/gtk/progress_dlg.c b/ui/gtk/progress_dlg.c
new file mode 100644
index 0000000000..1d007eaf24
--- /dev/null
+++ b/ui/gtk/progress_dlg.c
@@ -0,0 +1,414 @@
+/* progress_dlg.c
+ * Routines for progress-bar (modal) dialog
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "../progress_dlg.h"
+
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+
+
+#define PROG_BAR_KEY "progress_bar"
+
+static gboolean delete_event_cb(GtkWidget *w, GdkEvent *event, gpointer data);
+static void stop_cb(GtkWidget *w, gpointer data);
+
+/*
+ * Define the structure describing a progress dialog.
+ */
+struct progdlg {
+ GtkWidget *dlg_w; /* top-level window widget */
+ GTimeVal start_time;
+ GTimeVal last_time; /* last time it was updated */
+
+ GtkLabel *status_lb;
+ GtkLabel *elapsed_lb;
+ GtkLabel *time_left_lb;
+ GtkLabel *percentage_lb;
+ gchar *title;
+};
+
+/*
+ * Create and pop up the progress dialog; allocate a "progdlg_t"
+ * and initialize it to contain all information the implementation
+ * needs in order to manipulate the dialog, and return a pointer to
+ * it.
+ *
+ * The first argument is the task to do, e.g. "Loading".
+ * The second argument is the item to do, e.g. "capture.cap".
+ * The third argument is TRUE if the "terminate this operation" button should
+ * be a "Stop" button (meaning that the operation is stopped, but not undone),
+ * and FALSE if it should be a "Cancel" button (meaning that it's stopped
+ * and anything it's done would be undone)
+ * The fourth argument is a pointer to a Boolean variable that will be
+ * set to TRUE if the user hits that button.
+ *
+ * XXX - provide a way to specify the progress in units, with the total
+ * number of units specified as an argument when the progress dialog
+ * is created; updates would be given in units, with the progress dialog
+ * code computing the percentage, and the progress bar would have a
+ * label "0" on the left and <total number of units> on the right, with
+ * a label in the middle giving the number of units we've processed
+ * so far. This could be used when filtering packets, for example; we
+ * wouldn't always use it, as we have no idea how many packets are to
+ * be read.
+ */
+progdlg_t *
+create_progress_dlg(const gchar *task_title, const gchar *item_title,
+ gboolean terminate_is_stop, gboolean *stop_flag)
+{
+ progdlg_t *dlg;
+ GtkWidget *dlg_w, *main_vb, *title_lb, *status_lb, *elapsed_lb, *time_left_lb, *percentage_lb;
+ GtkWidget *prog_bar, *bbox, *cancel_bt;
+ GtkWidget *static_vb, *tmp_lb, *main_hb, *dynamic_vb, *percentage_hb;
+ gchar *task_title_dup;
+ gchar *item_title_dup;
+
+ dlg = g_malloc(sizeof (progdlg_t));
+
+ /* limit the item_title to some reasonable length */
+ item_title_dup = g_strdup(item_title);
+ if (strlen(item_title_dup) > 110) {
+ g_strlcpy(&item_title_dup[100], "...", 4);
+ }
+
+ dlg->title = g_strdup_printf("%s: %s", task_title, item_title_dup);
+
+ dlg_w = dlg_window_new(dlg->title);
+ gtk_window_set_modal(GTK_WINDOW(dlg_w), TRUE);
+
+ /*
+ * Container for dialog widgets.
+ */
+ main_vb = gtk_vbox_new(FALSE, 1);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(dlg_w), main_vb);
+
+ /*
+ * Static labels (left dialog side, labels aligned to the right)
+ */
+ static_vb = gtk_vbox_new(FALSE, 1);
+ task_title_dup = g_strdup_printf ("%s:", task_title);
+ tmp_lb = gtk_label_new(task_title_dup);
+ gtk_misc_set_alignment(GTK_MISC(tmp_lb), 1.0f, 0.0f);
+ gtk_box_pack_start(GTK_BOX(static_vb), tmp_lb, FALSE, TRUE, 3);
+ tmp_lb = gtk_label_new("Status:");
+ gtk_misc_set_alignment(GTK_MISC(tmp_lb), 1.0f, 0.0f);
+ gtk_box_pack_start(GTK_BOX(static_vb), tmp_lb, FALSE, TRUE, 3);
+ tmp_lb = gtk_label_new("Elapsed Time:");
+ gtk_misc_set_alignment(GTK_MISC(tmp_lb), 1.0f, 0.0f);
+ gtk_box_pack_start(GTK_BOX(static_vb), tmp_lb, FALSE, TRUE, 3);
+ tmp_lb = gtk_label_new("Time Left:");
+ gtk_misc_set_alignment(GTK_MISC(tmp_lb), 1.0f, 0.0f);
+ gtk_box_pack_start(GTK_BOX(static_vb), tmp_lb, FALSE, TRUE, 3);
+ tmp_lb = gtk_label_new("Progress:");
+ gtk_misc_set_alignment(GTK_MISC(tmp_lb), 1.0f, 0.0f);
+ gtk_box_pack_start(GTK_BOX(static_vb), tmp_lb, FALSE, TRUE, 3);
+
+
+ /*
+ * Dynamic labels (right dialog side, labels aligned to the left)
+ */
+ dynamic_vb = gtk_vbox_new(FALSE, 1);
+
+ /*
+ * Put the item_title here as a label indicating what we're
+ * doing; set its alignment and padding so it's aligned on the
+ * left.
+ */
+ title_lb = gtk_label_new(item_title_dup);
+ gtk_box_pack_start(GTK_BOX(dynamic_vb), title_lb, FALSE, TRUE, 3);
+ gtk_misc_set_alignment(GTK_MISC(title_lb), 0.0f, 0.0f);
+ gtk_misc_set_padding(GTK_MISC(title_lb), 0, 0);
+
+ /* same for "Status" */
+ status_lb = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(dynamic_vb), status_lb, FALSE, TRUE, 3);
+ gtk_misc_set_alignment(GTK_MISC(status_lb), 0.0f, 0.0f);
+ gtk_misc_set_padding(GTK_MISC(status_lb), 0, 0);
+ dlg->status_lb = (GtkLabel *) status_lb;
+
+ /* same for "Elapsed Time" */
+ elapsed_lb = gtk_label_new("00:00");
+ gtk_box_pack_start(GTK_BOX(dynamic_vb), elapsed_lb, FALSE, TRUE, 3);
+ gtk_misc_set_alignment(GTK_MISC(elapsed_lb), 0.0f, 0.0f);
+ gtk_misc_set_padding(GTK_MISC(elapsed_lb), 0, 0);
+ dlg->elapsed_lb = (GtkLabel *) elapsed_lb;
+
+ /* same for "Time Left" */
+ time_left_lb = gtk_label_new("--:--");
+ gtk_box_pack_start(GTK_BOX(dynamic_vb), time_left_lb, FALSE, TRUE, 3);
+ gtk_misc_set_alignment(GTK_MISC(time_left_lb), 0.0f, 0.0f);
+ gtk_misc_set_padding(GTK_MISC(time_left_lb), 0, 0);
+ dlg->time_left_lb = (GtkLabel *) time_left_lb;
+
+ /*
+ * The progress bar (in its own horizontal box, including
+ * percentage value)
+ */
+ percentage_hb = gtk_hbox_new(FALSE, 1);
+ gtk_box_pack_start(GTK_BOX(dynamic_vb), percentage_hb, FALSE, TRUE, 3);
+
+ prog_bar = gtk_progress_bar_new();
+ gtk_box_pack_start(GTK_BOX(percentage_hb), prog_bar, FALSE, TRUE, 3);
+
+ percentage_lb = gtk_label_new(" 0%");
+ gtk_misc_set_alignment(GTK_MISC(percentage_lb), 0.0f, 0.0f);
+ gtk_box_pack_start(GTK_BOX(percentage_hb), percentage_lb, FALSE, TRUE, 3);
+ dlg->percentage_lb = (GtkLabel *) percentage_lb;
+
+ /*
+ * Attach a pointer to the progress bar widget to the top-level widget.
+ */
+ g_object_set_data(G_OBJECT(dlg_w), PROG_BAR_KEY, prog_bar);
+
+ /*
+ * Static and dynamic boxes are now complete
+ */
+ main_hb = gtk_hbox_new(FALSE, 1);
+ gtk_box_pack_start(GTK_BOX(main_hb), static_vb, FALSE, TRUE, 3);
+ gtk_box_pack_start(GTK_BOX(main_hb), dynamic_vb, FALSE, TRUE, 3);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_hb, FALSE, TRUE, 3);
+
+ /* Button row */
+ bbox = dlg_button_row_new(terminate_is_stop ? GTK_STOCK_STOP :
+ GTK_STOCK_CANCEL, NULL);
+ gtk_container_add(GTK_CONTAINER(main_vb), bbox);
+ gtk_widget_show(bbox);
+
+ cancel_bt = g_object_get_data(G_OBJECT(bbox), terminate_is_stop ? GTK_STOCK_STOP :
+ GTK_STOCK_CANCEL);
+ gtk_widget_grab_default(cancel_bt);
+
+ /*
+ * Allow user to either click the "Cancel"/"Stop" button, or
+ * the close button on the window, to stop an operation in
+ * progress.
+ */
+ g_signal_connect(cancel_bt, "clicked", G_CALLBACK(stop_cb), stop_flag);
+ g_signal_connect(dlg_w, "delete_event", G_CALLBACK(delete_event_cb), stop_flag);
+
+ gtk_widget_show_all(dlg_w);
+
+ dlg->dlg_w = dlg_w;
+
+ g_get_current_time(&dlg->start_time);
+ memset(&dlg->last_time, 0, sizeof(dlg->last_time));
+
+ g_free(task_title_dup);
+ g_free(item_title_dup);
+
+ return dlg;
+}
+
+progdlg_t *
+delayed_create_progress_dlg(const gchar *task_title, const gchar *item_title,
+ gboolean terminate_is_stop, gboolean *stop_flag,
+ const GTimeVal *start_time, gfloat progress)
+{
+ GTimeVal time_now;
+ gdouble delta_time;
+ gdouble min_display;
+ progdlg_t *dlg;
+
+#define INIT_DELAY 0.1 * 1e6
+#define MIN_DISPLAY_DEFAULT 2.0 * 1e6
+
+ /* Create a progress dialog, but only if it's not likely to disappear
+ * immediately, which can be disconcerting for the user.
+ *
+ * Arguments are as for create_progress_dlg(), plus:
+ *
+ * (a) A pointer to a GTimeVal structure which holds the time at which
+ * the caller started to process the data.
+ * (b) The current progress as a real number between 0 and 1.
+ */
+
+ g_get_current_time(&time_now);
+
+ /* Get the time elapsed since the caller started processing the data */
+
+ delta_time = (time_now.tv_sec - start_time->tv_sec) * 1e6 +
+ time_now.tv_usec - start_time->tv_usec;
+
+ /* Do nothing for the first INIT_DELAY microseconds */
+
+ if (delta_time < INIT_DELAY)
+ return NULL;
+
+ /* If we create the progress dialog we want it to be displayed for a
+ * minimum of MIN_DISPLAY_DEFAULT microseconds. However, if we
+ * previously estimated that the progress dialog didn't need to be
+ * created and the caller's processing is slowing down (perhaps due
+ * to the action of the operating system's scheduler on a compute-
+ * intensive task), we tail off the minimum display time such that
+ * the progress dialog will always be created after
+ * 2*MIN_DISPLAY_DEFAULT microseconds.
+ */
+
+ if (delta_time <= INIT_DELAY + MIN_DISPLAY_DEFAULT)
+ min_display = MIN_DISPLAY_DEFAULT;
+ else
+ min_display = 2 * MIN_DISPLAY_DEFAULT - delta_time;
+ /* = MIN_DISPLAY_DEFAULT - (delta_time - MIN_DISPLAY_DEFAULT) */
+
+ /* Assuming the progress increases linearly, see if the progress
+ * dialog would be displayed for at least min_display microseconds if
+ * we created it now.
+ */
+
+ if (progress >= (delta_time / (delta_time + min_display)))
+ return NULL;
+
+ dlg = create_progress_dlg(task_title, item_title, terminate_is_stop,
+ stop_flag);
+
+ /*
+ * Flush out the dialog so we don't see an "empty" one until first update.
+ */
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ /* set dialog start_time to the start of processing, not box creation */
+ dlg->start_time = *start_time;
+
+ return dlg;
+}
+
+/*
+ * Called when the dialog box is to be deleted.
+ * Set the "stop" flag to TRUE, and return TRUE - we don't want the dialog
+ * box deleted now, our caller will do so when they see that the
+ * "stop" flag is TRUE and abort the operation.
+ */
+static gboolean
+delete_event_cb(GtkWidget *w _U_, GdkEvent *event _U_, gpointer data)
+{
+ gboolean *stop_flag = (gboolean *) data;
+
+ *stop_flag = TRUE;
+ return TRUE;
+}
+
+/*
+ * Called when the "stop this operation" button is clicked.
+ * Set the "stop" flag to TRUE; we don't have to destroy the dialog
+ * box, as our caller will do so when they see that the "stop" flag is
+ * true and abort the operation.
+ */
+static void
+stop_cb(GtkWidget *w _U_, gpointer data)
+{
+ gboolean *stop_flag = (gboolean *) data;
+
+ *stop_flag = TRUE;
+}
+
+/*
+ * Update the progress information of the progress dialog box.
+ */
+void
+update_progress_dlg(progdlg_t *dlg, gfloat percentage, const gchar *status)
+{
+ GtkWidget *dlg_w = dlg->dlg_w;
+ GtkWidget *prog_bar;
+ GTimeVal time_now;
+ gdouble delta_time;
+ gulong ul_left;
+ gulong ul_elapsed;
+ gulong ul_percentage;
+ gchar tmp[100];
+
+
+ /* calculate some timing values */
+ g_get_current_time(&time_now);
+
+ delta_time = (time_now.tv_sec - dlg->last_time.tv_sec) * 1e6 +
+ time_now.tv_usec - dlg->last_time.tv_usec;
+
+ /* after the first time don't update more than every 100ms */
+ if (dlg->last_time.tv_sec && delta_time < 100*1000)
+ return;
+
+ dlg->last_time = time_now;
+ delta_time = (time_now.tv_sec - dlg->start_time.tv_sec) * 1e6 +
+ time_now.tv_usec - dlg->start_time.tv_usec;
+
+ ul_percentage = (gulong) (percentage * 100);
+ ul_elapsed = (gulong) (delta_time / 1000 / 1000);
+
+ /* update labels */
+ g_snprintf(tmp, sizeof(tmp), "%lu%% of %s", ul_percentage, dlg->title);
+ gtk_window_set_title(GTK_WINDOW(dlg_w), tmp);
+
+ gtk_label_set_text(dlg->status_lb, status);
+
+ g_snprintf(tmp, sizeof(tmp), "%lu%%", ul_percentage);
+ gtk_label_set_text(dlg->percentage_lb, tmp);
+
+ g_snprintf(tmp, sizeof(tmp), "%02lu:%02lu", ul_elapsed / 60,
+ ul_elapsed % 60);
+ gtk_label_set_text(dlg->elapsed_lb, tmp);
+
+ /* show "Time Left" only,
+ * if at least 5% and 3 seconds running (to get a useful estimation) */
+ if (ul_percentage >= 5 && delta_time >= 3 * 1e6) {
+ ul_left = (gulong) ((delta_time / percentage - delta_time) / 1000 / 1000);
+
+ g_snprintf(tmp, sizeof(tmp), "%02lu:%02lu", ul_left / 60,
+ ul_left % 60);
+ gtk_label_set_text(dlg->time_left_lb, tmp);
+ }
+
+ /* update progress bar */
+ prog_bar = g_object_get_data(G_OBJECT(dlg_w), PROG_BAR_KEY);
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(prog_bar), percentage);
+
+ /*
+ * Flush out the update and process any input events.
+ */
+ while (gtk_events_pending())
+ gtk_main_iteration();
+}
+
+/*
+ * Destroy the progress dialog.
+ */
+void
+destroy_progress_dlg(progdlg_t *dlg)
+{
+ GtkWidget *dlg_w = dlg->dlg_w;
+
+ window_destroy(GTK_WIDGET(dlg_w));
+ g_free(dlg->title);
+ g_free(dlg);
+}
diff --git a/ui/gtk/proto_dlg.c b/ui/gtk/proto_dlg.c
new file mode 100644
index 0000000000..4e67e4bdef
--- /dev/null
+++ b/ui/gtk/proto_dlg.c
@@ -0,0 +1,593 @@
+/* proto_dlg.c
+ *
+ * $Id$
+ *
+ * Laurent Deniel <laurent.deniel@free.fr>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2000 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#if GTK_CHECK_VERSION(3,0,0)
+# include <gdk/gdkkeysyms-compat.h>
+#endif
+
+#include <epan/prefs.h>
+#include <epan/filesystem.h>
+
+#include "../util.h"
+#include "../simple_dialog.h"
+#include "../disabled_protos.h"
+
+#include "ui/gtk/main.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/proto_dlg.h"
+#include "ui/gtk/help_dlg.h"
+
+
+static gboolean proto_delete_event_cb(GtkWidget *, GdkEvent *, gpointer);
+static void proto_ok_cb(GtkWidget *, gpointer);
+static void proto_apply_cb(GtkWidget *, gpointer);
+static void proto_save_cb(GtkWidget *, gpointer);
+static void proto_cancel_cb(GtkWidget *, gpointer);
+static void proto_destroy_cb(GtkWidget *, gpointer);
+
+static void show_proto_selection(GtkListStore *proto_store);
+static gboolean set_proto_selection(GtkWidget *);
+static gboolean revert_proto_selection(void);
+
+static void proto_col_clicked_cb(GtkWidget *col _U_, GtkWidget *proto_list);
+
+static void toggle_all_cb(GtkWidget *button, gpointer parent_w);
+static void enable_all_cb(GtkWidget *button, gpointer parent_w);
+static void disable_all_cb(GtkWidget *button, gpointer parent_w);
+static void status_toggled(GtkCellRendererToggle *, gchar *, gpointer);
+
+static GtkWidget *proto_w = NULL;
+
+/* list of protocols */
+static GSList *protocol_list = NULL;
+
+typedef struct protocol_data {
+ const char *name;
+ const char *abbrev;
+ int hfinfo_index;
+ gboolean enabled;
+ gboolean was_enabled;
+ GtkTreeIter iter;
+} protocol_data_t;
+
+#define DISABLED "Disabled"
+#define STATUS_TXT(x) ((x) ? "" : DISABLED)
+
+void
+proto_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+
+ GtkWidget *main_vb, *bbox, *proto_list, *label, *proto_sw, *proto_frame,
+ *proto_vb, *button, *ok_bt, *apply_bt, *save_bt, *cancel_bt, *help_bt;
+ const gchar *titles[] = { "Status", "Protocol", "Description" };
+ GtkListStore *proto_store;
+ GtkCellRenderer *proto_rend;
+ GtkTreeViewColumn *proto_col;
+
+
+ if (proto_w != NULL) {
+ reactivate_window(proto_w);
+ return;
+ }
+
+ proto_w = dlg_conf_window_new("Wireshark: Enabled Protocols");
+ gtk_window_set_default_size(GTK_WINDOW(proto_w), DEF_WIDTH , DEF_HEIGHT);
+
+ /* Container for each row of widgets */
+
+ main_vb = gtk_vbox_new(FALSE, 6);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 6);
+ gtk_container_add(GTK_CONTAINER(proto_w), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* Protocol selection list ("enable/disable" protocols) */
+
+ proto_frame = gtk_frame_new("Enabled Protocols");
+ gtk_box_pack_start(GTK_BOX(main_vb), proto_frame, TRUE, TRUE, 0);
+ gtk_widget_show(proto_frame);
+
+ /* Protocol list */
+
+ proto_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(proto_frame), proto_vb);
+ gtk_container_set_border_width(GTK_CONTAINER(proto_vb), 5);
+ gtk_widget_show(proto_vb);
+
+ proto_sw = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(proto_sw),
+ GTK_SHADOW_IN);
+ gtk_box_pack_start(GTK_BOX(proto_vb), proto_sw, TRUE, TRUE, 0);
+ gtk_widget_show(proto_sw);
+
+ proto_store = gtk_list_store_new(4, G_TYPE_BOOLEAN, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_POINTER);
+ show_proto_selection(proto_store);
+ /* default sort on "abbrev" column */
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(proto_store), 1,
+ GTK_SORT_ASCENDING);
+
+ proto_list = tree_view_new(GTK_TREE_MODEL(proto_store));
+ gtk_container_add(GTK_CONTAINER(proto_sw), proto_list);
+
+ proto_rend = gtk_cell_renderer_toggle_new();
+ g_signal_connect(proto_rend, "toggled", G_CALLBACK(status_toggled), proto_store);
+ proto_col = gtk_tree_view_column_new_with_attributes(titles[0], proto_rend, "active", 0, NULL);
+ gtk_tree_view_column_set_sort_column_id(proto_col, 0);
+ g_signal_connect(proto_col, "clicked", G_CALLBACK(proto_col_clicked_cb), proto_list);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(proto_list), proto_col);
+
+ proto_rend = gtk_cell_renderer_text_new();
+ proto_col = gtk_tree_view_column_new_with_attributes(titles[1], proto_rend, "text", 1, NULL);
+ gtk_tree_view_column_set_sort_column_id(proto_col, 1);
+ g_signal_connect(proto_col, "clicked", G_CALLBACK(proto_col_clicked_cb), proto_list);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(proto_list), proto_col);
+
+ proto_rend = gtk_cell_renderer_text_new();
+ proto_col = gtk_tree_view_column_new_with_attributes(titles[2], proto_rend, "text", 2, NULL);
+ gtk_tree_view_column_set_sort_column_id(proto_col, 2);
+ g_signal_connect(proto_col, "clicked", G_CALLBACK(proto_col_clicked_cb), proto_list);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(proto_list), proto_col);
+
+ gtk_tree_view_set_search_column(GTK_TREE_VIEW(proto_list), 1); /* col 1 in the *model* */
+ g_object_unref(G_OBJECT(proto_store));
+ gtk_widget_show(proto_list);
+
+ label = gtk_label_new("Disabling a protocol prevents higher "
+ "layer protocols from being displayed");
+ gtk_misc_set_alignment(GTK_MISC(label), 0.5f, 0.5f);
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(proto_vb), label, FALSE, FALSE, 5);
+
+ bbox = gtk_hbutton_box_new();
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
+ gtk_box_set_spacing(GTK_BOX(bbox), 5);
+ gtk_box_pack_start(GTK_BOX(proto_vb), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+
+ /* Enable All */
+ button = gtk_button_new_with_label("Enable All");
+ g_signal_connect(button, "clicked", G_CALLBACK(enable_all_cb), proto_list);
+ gtk_box_pack_start(GTK_BOX(bbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ /* Disable All */
+ button = gtk_button_new_with_label("Disable All");
+ g_signal_connect(button, "clicked", G_CALLBACK(disable_all_cb), proto_list);
+ gtk_box_pack_start(GTK_BOX(bbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+ /* Invert */
+ button = gtk_button_new_with_label("Invert");
+ g_signal_connect(button, "clicked", G_CALLBACK(toggle_all_cb), proto_list);
+ gtk_box_pack_start(GTK_BOX(bbox), button, TRUE, TRUE, 0);
+ gtk_widget_show(button);
+
+
+ /* Button row */
+ bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_SAVE, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+
+ ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(proto_ok_cb), proto_w);
+ gtk_widget_grab_default(ok_bt);
+
+ apply_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_APPLY);
+ g_signal_connect(apply_bt, "clicked", G_CALLBACK(proto_apply_cb), proto_w);
+
+ save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
+ g_signal_connect(save_bt, "clicked", G_CALLBACK(proto_save_cb), proto_w);
+
+ cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ window_set_cancel_button(proto_w, cancel_bt, proto_cancel_cb);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_ENABLED_PROTOCOLS_DIALOG);
+
+ g_signal_connect(proto_w, "delete_event", G_CALLBACK(proto_delete_event_cb), NULL);
+ g_signal_connect(proto_w, "destroy", G_CALLBACK(proto_destroy_cb), NULL);
+
+ gtk_widget_show(proto_w);
+
+ gtk_widget_grab_focus(proto_list); /* XXX: force focus to the tree_view. This hack req'd so "type-ahead find"
+ * will be effective after the window is displayed. The issue is
+ * that any call to gtk_tree_view_column_set_sort_column_id above
+ * apparently sets the focus to the column header button and thus
+ * type-ahead find is, in effect, disabled on the column.
+ * Also required: a grab_focus whenever the column header is
+ * clicked to change the column sort order since the click
+ * also changes the focus to the column header button.
+ * Is there a better way to do this ?
+ */
+
+ /* hide the Save button if the user uses implicit save */
+ if(!prefs.gui_use_pref_save) {
+ gtk_widget_hide(save_bt);
+ }
+
+ window_present(proto_w);
+} /* proto_cb */
+
+/* protocol list column header clicked (to change sort) */
+/* grab_focus(treeview) req'd so that type-ahead find works. */
+/* (See comment above). */
+static void
+proto_col_clicked_cb(GtkWidget *col _U_, GtkWidget *proto_list) {
+ gtk_widget_grab_focus(proto_list);
+}
+
+/* Status toggled */
+static void
+status_toggled(GtkCellRendererToggle *cell _U_, gchar *path_str, gpointer data)
+{
+ GtkTreeModel *model = (GtkTreeModel *)data;
+ GtkTreeIter iter;
+ GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
+ protocol_data_t *p;
+
+ gtk_tree_model_get_iter(model, &iter, path);
+ gtk_tree_model_get(model, &iter, 3, &p, -1);
+
+ if (p->enabled)
+ p->enabled = FALSE;
+ else
+ p->enabled = TRUE;
+
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, p->enabled, -1);
+
+ gtk_tree_path_free(path);
+} /* status toggled */
+
+/* XXX - We need callbacks for Gtk2 */
+
+/* Toggle All */
+static void
+toggle_all_cb(GtkWidget *button _U_, gpointer pl)
+{
+ GSList *entry;
+ GtkListStore *s = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(pl)));
+
+ for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
+ protocol_data_t *p = entry->data;
+
+ if (p->enabled)
+ p->enabled = FALSE;
+ else
+ p->enabled = TRUE;
+
+ gtk_list_store_set(s, &p->iter, 0, p->enabled, -1);
+ }
+}
+
+/* Enable/Disable All Helper */
+static void
+set_active_all(GtkWidget *w, gboolean new_state)
+{
+ GtkListStore *s = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(w)));
+ GSList *entry;
+
+ for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
+ protocol_data_t *p = entry->data;
+
+ p->enabled = new_state;
+ gtk_list_store_set(s, &p->iter, 0, new_state, -1);
+ }
+}
+
+/* Enable All */
+static void
+enable_all_cb(GtkWidget *button _U_, gpointer pl)
+{
+ set_active_all(pl, TRUE);
+}
+
+/* Disable All */
+static void
+disable_all_cb(GtkWidget *button _U_, gpointer pl)
+{
+ set_active_all(pl, FALSE);
+}
+
+static void
+proto_destroy_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ GSList *entry;
+
+ proto_w = NULL;
+ /* remove protocol list */
+ if (protocol_list) {
+ for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
+ g_free(entry->data);
+ }
+ g_slist_free(protocol_list);
+ protocol_list = NULL;
+ }
+}
+
+/* Treat this as a cancel, by calling "proto_cancel_cb()".
+ XXX - that'll destroy the Protocols dialog; will that upset
+ a higher-level handler that says "OK, we've been asked to delete
+ this, so destroy it"? */
+static gboolean
+proto_delete_event_cb(GtkWidget *proto_w_lcl, GdkEvent *event _U_,
+ gpointer dummy _U_)
+{
+ proto_cancel_cb(NULL, proto_w_lcl);
+ return FALSE;
+}
+
+/* Update protocol_list 'was_enabled' to current value of 'enabled' */
+static void
+update_was_enabled(void)
+{
+ GSList *entry;
+
+ for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
+ protocol_data_t *p = entry->data;
+ p->was_enabled = p->enabled;
+ }
+}
+
+static void
+proto_write(gpointer parent_w _U_)
+{
+ char *pf_dir_path;
+ char *pf_path;
+ int pf_save_errno;
+
+ /* Create the directory that holds personal configuration files, if
+ necessary. */
+ if (create_persconffile_dir(&pf_dir_path) == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't create directory\n\"%s\"\nfor disabled protocols file: %s.", pf_dir_path,
+ g_strerror(errno));
+ g_free(pf_dir_path);
+ } else {
+ save_disabled_protos_list(&pf_path, &pf_save_errno);
+ if (pf_path != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Could not save to your disabled protocols file\n\"%s\": %s.",
+ pf_path, g_strerror(pf_save_errno));
+ g_free(pf_path);
+ }
+ }
+}
+
+static void
+proto_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w)
+{
+ gboolean redissect;
+
+ /* update the selection now, so we'll save the right things */
+ redissect = set_proto_selection(GTK_WIDGET(parent_w));
+
+ /* if we don't have a Save button, just save the settings now */
+ if (!prefs.gui_use_pref_save) {
+ proto_write(parent_w);
+ }
+
+ window_destroy(GTK_WIDGET(parent_w));
+ if (redissect)
+ redissect_packets();
+}
+
+static void
+proto_apply_cb(GtkWidget *apply_bt _U_, gpointer parent_w)
+{
+ gboolean redissect;
+
+ /* update the selection now, so we'll save the right things */
+ redissect = set_proto_selection(GTK_WIDGET(parent_w));
+
+ /* if we don't have a Save button, just save the settings now */
+ if (!prefs.gui_use_pref_save) {
+ proto_write(parent_w);
+ update_was_enabled();
+ }
+
+ if (redissect)
+ redissect_packets();
+}
+
+static void
+proto_save_cb(GtkWidget *save_bt _U_, gpointer parent_w)
+{
+
+ proto_write(parent_w);
+
+ if (set_proto_selection(GTK_WIDGET(parent_w))) {
+ /* Redissect all the packets, and re-evaluate the display filter. */
+ redissect_packets();
+ }
+}
+
+static void
+proto_cancel_cb(GtkWidget *cancel_bt _U_, gpointer parent_w)
+{
+ gboolean redissect;
+
+ redissect = revert_proto_selection();
+ window_destroy(GTK_WIDGET(parent_w));
+ if (redissect)
+ redissect_packets();
+}
+
+static gboolean
+set_proto_selection(GtkWidget *parent_w _U_)
+{
+ GSList *entry;
+ gboolean need_redissect = FALSE;
+
+ for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
+ protocol_data_t *p = entry->data;
+ protocol_t *protocol;
+
+ protocol = find_protocol_by_id(p->hfinfo_index);
+ if (proto_is_protocol_enabled(protocol) != p->enabled) {
+ proto_set_decoding(p->hfinfo_index, p->enabled);
+ need_redissect = TRUE;
+ }
+ }
+
+ return need_redissect;
+
+} /* set_proto_selection */
+
+static gboolean
+revert_proto_selection(void)
+{
+ GSList *entry;
+ gboolean need_redissect = FALSE;
+
+ /*
+ * Undo all the changes we've made to protocol enable flags.
+ */
+ for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
+ protocol_data_t *p = entry->data;
+ protocol_t *protocol;
+
+ protocol = find_protocol_by_id(p->hfinfo_index);
+ if (proto_is_protocol_enabled(protocol) != p->was_enabled) {
+ proto_set_decoding(p->hfinfo_index, p->was_enabled);
+ need_redissect = TRUE;
+ }
+ }
+
+ return need_redissect;
+
+} /* revert_proto_selection */
+
+static gint
+protocol_data_compare(gconstpointer a, gconstpointer b)
+{
+ const protocol_data_t *ap = (const protocol_data_t *)a;
+ const protocol_data_t *bp = (const protocol_data_t *)b;
+
+ return strcmp(ap->abbrev, bp->abbrev);
+}
+
+static void
+create_protocol_list(void)
+{
+ gint i;
+ void *cookie;
+ protocol_t *protocol;
+ protocol_data_t *p;
+
+ /* Iterate over all the protocols */
+
+ for (i = proto_get_first_protocol(&cookie); i != -1;
+ i = proto_get_next_protocol(&cookie)) {
+ if (proto_can_toggle_protocol(i)) {
+ p = g_malloc(sizeof(protocol_data_t));
+ protocol = find_protocol_by_id(i);
+ p->name = proto_get_protocol_name(i);
+ p->abbrev = proto_get_protocol_short_name(protocol);
+ p->hfinfo_index = i;
+ p->enabled = proto_is_protocol_enabled(protocol);
+ p->was_enabled = p->enabled;
+ protocol_list = g_slist_insert_sorted(protocol_list,
+ p, protocol_data_compare);
+ }
+ }
+}
+
+static void
+show_proto_selection(GtkListStore *proto_store)
+{
+ GSList *entry;
+ protocol_data_t *p;
+
+ if (protocol_list == NULL)
+ create_protocol_list();
+
+ for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
+ p = entry->data;
+
+ gtk_list_store_append(proto_store, &p->iter);
+ gtk_list_store_set(proto_store, &p->iter,
+ 0, p->enabled,
+ 1, p->abbrev,
+ 2, p->name,
+ 3, p,
+ -1);
+ }
+
+} /* show_proto_selection */
+
+static void
+proto_disable_dialog_cb(gpointer dialog _U_, gint btn, gpointer data)
+{
+ protocol_t *protocol;
+ gint id = GPOINTER_TO_INT(data);
+
+ if (btn == ESD_BTN_OK) {
+ /* Allow proto_dlg to work with the original settings */
+ if (protocol_list == NULL)
+ create_protocol_list();
+ /* Toggle the protocol if it's enabled and allowed */
+ protocol = find_protocol_by_id(id);
+ if (proto_is_protocol_enabled(protocol) == TRUE) {
+ if (proto_can_toggle_protocol(id) == TRUE) {
+ proto_set_decoding(id, FALSE);
+ redissect_packets();
+ }
+ }
+ }
+}
+
+void
+proto_disable_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ header_field_info *hfinfo;
+ gint id;
+ gpointer dialog;
+
+ if (cfile.finfo_selected == NULL) {
+ /* There is no field selected */
+ return;
+ }
+
+ /* Find the id for the protocol for the selected field. */
+ hfinfo = cfile.finfo_selected->hfinfo;
+ if (hfinfo->parent == -1)
+ id = proto_get_id((protocol_t *)hfinfo->strings);
+ else
+ id = hfinfo->parent;
+
+ dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_OK_CANCEL,
+ "Do you want to temporarily disable protocol: %s ?",
+ proto_registrar_get_abbrev(id));
+
+ simple_dialog_set_cb(dialog, proto_disable_dialog_cb, GINT_TO_POINTER(id));
+}
diff --git a/ui/gtk/proto_dlg.h b/ui/gtk/proto_dlg.h
new file mode 100644
index 0000000000..add72fa623
--- /dev/null
+++ b/ui/gtk/proto_dlg.h
@@ -0,0 +1,49 @@
+/* proto_dlg.h
+ *
+ * $Id$
+ *
+ * Laurent Deniel <laurent.deniel@free.fr>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2000 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __PROTO_DLG_H__
+#define __PROTO_DLG_H__
+
+/** @file
+ * "Enabled Protocols" dialog box.
+ * @ingroup dialog_group
+ */
+
+/** Show the enabled protocols dialog.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+void proto_cb(GtkWidget *widget, gpointer data);
+
+/** Disable (temporarily) the selected protocol.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+void proto_disable_cb(GtkWidget *widget, gpointer data);
+
+#endif
diff --git a/ui/gtk/proto_help.c b/ui/gtk/proto_help.c
new file mode 100644
index 0000000000..80921da670
--- /dev/null
+++ b/ui/gtk/proto_help.c
@@ -0,0 +1,696 @@
+/* proto_help.c
+ * Routines for dynamic protocol help menus
+ *
+ * $Id$
+ *
+ * Edgar Gladkich <edgar.gladkich@incacon.de>
+ * Gerald Combs <gerald@wireshark.org>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* This file basically does nothing except remind us how this worked before
+ * the menu code was rewritten to no longer use the deprecated ItemFactory
+ * stuff.
+ */
+#define MAIN_MENU_USE_UIMANAGER 1
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include "ui/gtk/webbrowser.h"
+
+#include "../file.h"
+
+#include <epan/prefs.h>
+#include <epan/filesystem.h>
+#include <epan/strutil.h>
+#include <epan/proto.h>
+
+#include "ui/gtk/proto_help.h"
+
+/*
+ * The protocol help files cannot be downloaded downloaded directly. You
+ * will need to go to http://www.inacon.com/download/, then download one
+ * of the packages, e.g. the source package and extract the protocol_help
+ * folder. To actually use the help you will also need to be able to
+ * access the URL beginning at the contents of the loclation line in the
+ * ph.ini file (e.g. location=http://www.inacon.de/ph/data/).
+ * To provide offline service download the contents of the given location
+ * to your computer, then change the location parameter in the ph.ini file
+ * to point to that new URL. This may be a file:/// URL.
+ */
+
+/* Right now proto_help will not build with -DGTK_DISABLE_DEPRECATED due to
+ its use of GtkItemFactory.
+ See http://developer.gnome.org/gtk/2.24/GtkItemFactory.html: "As of GTK+ 2.4,
+ GtkItemFactory has been deprecated in favour of GtkUIManager."
+ This needs to be rewritten by the people who added this code or some other
+ volunteers. Otherwise this functionality will be lost once the non UI_MANAGER
+ stuff gets removed it isn't built by default any more.
+ */
+#ifdef MAIN_MENU_USE_UIMANAGER
+void proto_help_menu_modify(GtkTreeSelection *selection _U_, capture_file *cf _U_) {}
+void proto_help_menu_init(GtkWidget *widget _U_) {}
+void proto_help_init(void) {}
+#else
+
+#define PH_MENU_TOP "/Protocol Help"
+
+#define PH_FILE_LOG "ph.log"
+#define PH_INI_SUFFIX ".ini"
+#define PH_PATH_SEARCH_STR "${PATH}"
+
+/* .ini Section names */
+#define PH_INI_GROUP_DATABASE "database"
+#define PH_INI_GROUP_LOCATION_DATA "location data"
+#define PH_INI_GROUP_MAP "map"
+
+/* .ini [database] section keys */
+#define PH_INI_DB_KEY_SOURCE "source"
+#define PH_INI_DB_KEY_LOCATION_TEMPLATE "location"
+
+/* .ini Path sections */
+#define PH_INI_KEY_OVERVIEW "_OVERVIEW"
+
+/* Where to look for .ini files */
+#define PH_CONFFILE_SUBDIR "protocol_help"
+
+#ifdef PH_DEBUG_LOG
+const gchar *ph_log_path;
+#endif
+
+typedef struct proto_help_key_file_t {
+ GKeyFile *keyfile;
+ const gchar *source;
+ const gchar *loc_template;
+} proto_help_key_file;
+
+GPtrArray *g_ph_key_files = NULL;
+
+GtkItemFactory *g_ph_menu_factory;
+
+static void ph_menu_reset(void);
+static void ph_menu_onclick(GtkWidget*, gpointer, guint);
+
+static int ph_capture_get_protocol_id(GtkTreeSelection*, capture_file*);
+static const gchar* ph_capture_get_protocol_name(GtkTreeSelection*, capture_file*);
+static const gchar* ph_capture_get_protocol_abbrev(GtkTreeSelection*, capture_file*);
+static gchar* ph_capture_get_description(capture_file*);
+
+static proto_help_key_file *ph_ini_load_file(const gchar*);
+static gchar* ph_ini_get_value(GKeyFile *, const gchar*, const gchar*, gchar *);
+static gchar* ph_ini_get_path(GKeyFile *, const gchar*, const gchar*);
+static gchar** ph_ini_get_keywords(GKeyFile *, const gchar*);
+
+static guint ph_parse_string(const gchar*, const gchar*);
+
+/* #define PH_DEBUG_LOG 1 */
+#ifdef PH_DEBUG_LOG
+static void ph_logging_handler(const gchar*, GLogLevelFlags, const gchar*, gpointer);
+#endif
+
+/** @file
+ * Protocol help routines. Adds web browser links to protocol menu items
+ * via configuration files.
+ */
+
+/** Initialization
+ *
+ * @param void
+ * @return void
+ */
+#define PH_CONF_DIRS 2
+void proto_help_init(void)
+{
+ gchar *search_dir[PH_CONF_DIRS];
+ const gchar *ini_name;
+ gchar *ini_path;
+ GDir *conf_dir;
+ int i;
+
+ search_dir[0] = g_strdup_printf("%s" G_DIR_SEPARATOR_S PH_CONFFILE_SUBDIR, get_datafile_dir());
+ /* XXX - Use profiles? */
+ search_dir[1] = get_persconffile_path(PH_CONFFILE_SUBDIR, FALSE, FALSE);
+
+#ifdef PH_DEBUG_LOG
+ g_log_set_handler(NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, ph_logging_handler, NULL);
+#endif
+
+ if (g_ph_key_files)
+ return;
+
+ g_ph_key_files = g_ptr_array_new();
+
+ /* Start loop */
+
+#ifdef PH_DEBUG_LOG
+ ph_log_path = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", g_get_tmp_dir(), PH_FILE_LOG);
+#endif
+
+ for (i = 0; i < PH_CONF_DIRS; i++) {
+ g_log(NULL, G_LOG_LEVEL_INFO, "Looking for protocol help files in '%s'", search_dir[i]);
+ conf_dir = g_dir_open(search_dir[i], 0, NULL);
+ if (!conf_dir) {
+ continue;
+ }
+
+ while ((ini_name = g_dir_read_name(conf_dir)) != NULL) {
+ if (! g_str_has_suffix(ini_name, PH_INI_SUFFIX)) {
+ continue;
+ }
+ g_log(NULL, G_LOG_LEVEL_INFO, "-- Found '%s'", ini_name);
+ ini_path = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", search_dir[i], ini_name);
+ ph_ini_load_file(ini_path);
+ g_free(ini_path);
+ }
+ g_dir_close(conf_dir);
+ }
+}
+
+/** Initialize the menu
+ *
+ * @param widget Context menu root
+ * @return void
+ */
+void proto_help_menu_init(GtkWidget *widget)
+{
+ g_ph_menu_factory = gtk_item_factory_from_widget(widget);
+ ph_menu_reset();
+}
+
+/** Clear the menu
+ *
+ * @param void
+ * @return void
+ */
+static void ph_menu_reset(void)
+{
+ GtkWidget *menu_item = NULL;
+ GList *menu_entries = NULL;
+ GList *menu_entry = NULL;
+
+ if(!g_ph_menu_factory) return;
+
+ menu_item = gtk_item_factory_get_widget(g_ph_menu_factory, PH_MENU_TOP);
+ menu_entries = gtk_container_get_children(GTK_CONTAINER(menu_item));
+
+ for(menu_entry = g_list_first(menu_entries); menu_entry != NULL; menu_entry = g_list_next(menu_entry))
+ {
+ gtk_container_remove(GTK_CONTAINER(menu_item), menu_entry->data);
+ }
+
+ menu_item = gtk_item_factory_get_item(g_ph_menu_factory, PH_MENU_TOP);
+ gtk_widget_set_sensitive(menu_item, FALSE);
+}
+
+/* Callback to free URLs */
+static void
+url_destroy_cb(GtkWidget *w _U_, gpointer url) {
+ g_free(url);
+}
+
+/** Fill in the protocol help menu
+ *
+ * @param selection Currently-selected packet
+ * @param cf Capture file
+ * @return void
+ */
+void proto_help_menu_modify(GtkTreeSelection *selection, capture_file *cf)
+{
+ gchar *description;
+ const gchar *proto_abbrev, *proto_name;
+ gchar *value;
+ gchar **keys;
+ GHashTable *table;
+ guint i = 0, cur_kf;
+ GtkWidget *menu_item = NULL;
+ GtkItemFactoryEntry *menu_entry = NULL;
+ proto_help_key_file* phkf;
+ gchar *loc;
+ gboolean add_separator = FALSE;
+ gboolean found = FALSE;
+
+ if(!g_ph_menu_factory) return;
+ ph_menu_reset();
+
+ proto_abbrev = ph_capture_get_protocol_abbrev(selection, cf);
+ if(!proto_abbrev) return;
+
+ proto_name = ph_capture_get_protocol_name(selection, cf);
+ if(!proto_name) return;
+
+ description = ph_capture_get_description(cf);
+
+ for (cur_kf = 0; cur_kf < g_ph_key_files->len; cur_kf++) {
+ phkf = (proto_help_key_file *) g_ptr_array_index(g_ph_key_files, cur_kf);
+ g_assert(phkf);
+
+ value = ph_ini_get_path(phkf->keyfile, proto_abbrev, PH_INI_KEY_OVERVIEW);
+
+ if(!value)
+ {
+ g_log(NULL, G_LOG_LEVEL_DEBUG, "Overview page of the protocol '%s' is not defined", proto_abbrev);
+ continue;
+ }
+
+ /*
+ * XXX - We could save some memory here if we stored the location template
+ * and search value as separate items. However, that makes freeing them
+ * a bit messy.
+ */
+ loc = string_replace(phkf->loc_template, PH_PATH_SEARCH_STR, value);
+ g_free(value);
+ if (!loc || !strlen(loc)) continue;
+
+ if (add_separator) {
+ menu_entry = g_malloc0(sizeof(GtkItemFactoryEntry));
+ menu_entry->path = g_strdup_printf("%s/<separator>", PH_MENU_TOP);
+ menu_entry->item_type = "<Separator>";
+ gtk_item_factory_create_item(g_ph_menu_factory, menu_entry, NULL, 2);
+ }
+ add_separator = TRUE;
+
+ menu_entry = g_malloc0(sizeof(GtkItemFactoryEntry));
+ menu_entry->path = g_strdup_printf("%s/%s %s Overview", PH_MENU_TOP, phkf->source, proto_name);
+ menu_entry->callback = ph_menu_onclick;
+ gtk_item_factory_create_item(g_ph_menu_factory, menu_entry, loc, 2);
+ menu_item = gtk_item_factory_get_widget(g_ph_menu_factory, menu_entry->path);
+ g_assert(menu_item);
+ g_signal_connect(menu_item, "destroy", G_CALLBACK(url_destroy_cb), loc);
+
+ found = TRUE;
+
+ if(description)
+ {
+ keys = ph_ini_get_keywords(phkf->keyfile, proto_abbrev);
+ table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+
+ for(i = 0; keys[i] != NULL; i++)
+ {
+ if(!strcmp(keys[i], PH_INI_KEY_OVERVIEW)) continue; /* We already added the overview */
+ if(!ph_parse_string(description, keys[i])) continue; /* Bad format */
+ if(g_hash_table_lookup(table, g_ascii_strup(keys[i], -1)) != NULL) continue; /* Duplicate */
+
+ value = ph_ini_get_path(phkf->keyfile, proto_abbrev, keys[i]);
+ if(!value || !strlen(value)) continue;
+
+ loc = string_replace(phkf->loc_template, PH_PATH_SEARCH_STR, value);
+ g_free(value);
+ if (!loc || !strlen(loc)) continue;
+
+ g_hash_table_insert(table, g_ascii_strup(keys[i], -1), GINT_TO_POINTER(1));
+
+ menu_entry = g_malloc0(sizeof(GtkItemFactoryEntry));
+ menu_entry->path = g_strdup_printf("%s/%s", PH_MENU_TOP, keys[i]);
+ menu_entry->callback = ph_menu_onclick;
+ gtk_item_factory_create_item(g_ph_menu_factory, menu_entry, loc, 2);
+ menu_item = gtk_item_factory_get_widget(g_ph_menu_factory, menu_entry->path);
+ g_assert(menu_item);
+ g_signal_connect(menu_item, "destroy", G_CALLBACK(url_destroy_cb), loc);
+ }
+
+ g_hash_table_destroy(table);
+ }
+ }
+
+ g_free(description);
+ menu_item = gtk_item_factory_get_item(g_ph_menu_factory, PH_MENU_TOP);
+ gtk_widget_set_sensitive(menu_item, found);
+}
+
+/**
+* Function ph_menu_onclick
+*
+* @param GtkWidget *widget Description
+* @param gpointer data Description
+* @param guint action Description
+* @return void
+*/
+static void ph_menu_onclick(GtkWidget *widget _U_, gpointer data, guint action _U_)
+{
+ gchar *loc = (gchar *) data;
+
+ g_log(NULL, G_LOG_LEVEL_DEBUG, "Sending '%s' to the browser.", loc);
+
+ if (! loc) {
+ g_log(NULL, G_LOG_LEVEL_DEBUG, "Protocol help ended up with a NULL URL.");
+ return;
+ }
+
+ /* XXX - Should we do any RFC 3986 escaping first? */
+ if (! browser_open_url(loc)) {
+ g_log(NULL, G_LOG_LEVEL_DEBUG, "Couldn't get protocol help for %s", loc);
+ }
+}
+
+/** Get the field ID for a selected tree item.
+ *
+ * @param selection Tree selection
+ * @param cfile Capture file
+ * @return Field ID or 0
+ */
+static int
+ph_capture_get_protocol_id(GtkTreeSelection *selection, capture_file *cf)
+{
+ GtkTreeModel *model = NULL;
+ GtkTreeIter iter;
+ GtkTreeIter parent;
+ field_info *finfo = NULL;
+ int proto_id = 0;
+
+ if(!cf->finfo_selected) return 0;
+ proto_id = cf->finfo_selected->hfinfo->id;
+
+ if(!proto_id)
+ {
+ if(!gtk_tree_selection_get_selected(GTK_TREE_SELECTION(selection), &model, &iter)) return 0;
+
+ while(gtk_tree_model_iter_parent(model, &parent, &iter))
+ {
+ gtk_tree_model_get(model, &parent, 1, &finfo, -1);
+ iter = parent;
+
+ if(finfo->hfinfo->id > 0)
+ {
+ proto_id = finfo->hfinfo->id;
+ break;
+ }
+ }
+ }
+
+ while(proto_id && !proto_registrar_is_protocol(proto_id))
+ {
+ proto_id = proto_registrar_get_parent(proto_id);
+ }
+
+ return proto_id;
+}
+
+/** Get the protocol name for a selected tree item.
+ *
+ * @param selection Tree selection
+ * @param cfile Capture file
+ * @return Name for a protocol or NULL
+ */
+static const gchar*
+ph_capture_get_protocol_name(GtkTreeSelection *selection, capture_file *cf)
+{
+ int proto_id = ph_capture_get_protocol_id(selection, cf);
+
+ return (!proto_id) ? NULL : proto_get_protocol_short_name(find_protocol_by_id(proto_id));
+}
+
+/** Get the abbreviated protocol name for a selected tree item.
+ *
+ * @param selection Tree selection
+ * @param cfile Capture file
+ * @return Abbreviated (lower-case) name for a protocol or NULL
+ */
+static const gchar*
+ph_capture_get_protocol_abbrev(GtkTreeSelection *selection, capture_file *cf)
+{
+ int proto_id = ph_capture_get_protocol_id(selection, cf);
+
+ return (!proto_id) ? NULL : proto_registrar_get_abbrev(proto_id);
+}
+
+/** Return the selected item description
+ * @param cf capture file pointer
+ * @return Description of the selected item. MUST be freed with g_free().
+ */
+static gchar* ph_capture_get_description(capture_file *cf)
+{
+ gchar *buffer = NULL;
+
+ if(cf->finfo_selected->rep &&
+ strlen(cf->finfo_selected->rep->representation) > 0)
+ {
+ buffer = g_strdup(cf->finfo_selected->rep->representation);
+ }
+ else
+ {
+ buffer = g_malloc(ITEM_LABEL_LENGTH);
+ proto_item_fill_label(cf->finfo_selected, buffer);
+ }
+
+ return buffer;
+}
+
+/** Load a protocol help key file and add it to the global array.
+ *
+ * @param filename Full path to the key file.
+ * @return Newly-created key file entry or NULL.
+ */
+static proto_help_key_file *
+ph_ini_load_file(const gchar *filename)
+{
+ GKeyFile *kf;
+ GError *error = NULL;
+ proto_help_key_file *phkf = NULL;
+ gchar *old_template, *loc_template;
+ gchar **loc_data, *loc_repl, *loc_search;
+ gsize i, len;
+
+ if(!g_file_test(filename, G_FILE_TEST_EXISTS))
+ {
+ g_log(NULL, G_LOG_LEVEL_DEBUG, "Configuration file %s does not exists", filename);
+ return NULL;
+ }
+
+ kf = g_key_file_new();
+
+ if(!g_key_file_load_from_file(kf, filename, G_KEY_FILE_NONE, &error))
+ {
+ g_log(NULL, G_LOG_LEVEL_DEBUG, "Configuration file '%s' could not be loaded (%s)", filename, error->message);
+ g_error_free(error);
+ g_key_file_free(kf);
+
+ return NULL;
+ }
+
+ loc_template = ph_ini_get_value(kf, PH_INI_GROUP_DATABASE, PH_INI_DB_KEY_LOCATION_TEMPLATE, NULL);
+ if (!loc_template) {
+ g_key_file_free(kf);
+ return NULL;
+ }
+
+ loc_data = g_key_file_get_keys(kf, PH_INI_GROUP_LOCATION_DATA, &len, NULL);
+ if (loc_data) {
+ for (i = 0; i < len; i++) {
+ loc_repl = ph_ini_get_value(kf, PH_INI_GROUP_LOCATION_DATA, loc_data[i], NULL);
+ old_template = loc_template;
+ loc_search = g_strdup_printf("${%s}", loc_data[i]);
+ loc_template = string_replace(loc_template, loc_search, loc_repl);
+ g_free(loc_repl);
+ g_free(loc_search);
+ g_free(old_template);
+ }
+ }
+
+ /* Add ${PATH} to the end if it's not present */
+ if (!strstr(loc_template, PH_PATH_SEARCH_STR)) {
+ old_template = loc_template;
+ loc_template = g_strdup_printf("%s" PH_PATH_SEARCH_STR, old_template);
+ g_free(old_template);
+ }
+
+ phkf = g_malloc(sizeof(proto_help_key_file));
+ phkf->keyfile = kf;
+ phkf->source = ph_ini_get_value(kf, PH_INI_GROUP_DATABASE, PH_INI_DB_KEY_SOURCE, "(Unknown source)");
+ phkf->loc_template = loc_template;
+
+ g_ptr_array_add(g_ph_key_files, phkf);
+
+ return phkf;
+}
+
+/** Fetch a value for a key from a key file with an optional default value.
+ *
+ * @param keyfile The key file to search
+ * @param group Key file group
+ * @param key Key file value to fetch
+ * @param alt Alternate string to return. May be NULL.
+ * @return const gchar* A newly-allocated key value, or a copy of alt if not found.
+ */
+static gchar*
+ph_ini_get_value(GKeyFile *keyfile, const gchar *group, const gchar *key, gchar *alt)
+{
+ gchar *value = NULL;
+
+ if (keyfile) {
+ value = g_key_file_get_string(keyfile, group, key, NULL);
+ }
+
+ if (!value) {
+ value = g_strdup(alt);
+ }
+
+ return value;
+}
+
+/** Given a protocol name and a key, map the protocol name to a section in the
+ * keyfile, then look up the value for that key.
+ *
+ * @param keyfile The key file to search
+ * @param protocol Wireshark protocol name to map, e.g. "tcp".
+ * @param keyword The key to fetch from the mapped section.
+ * @return
+ */
+static gchar*
+ph_ini_get_path(GKeyFile *keyfile, const gchar *protocol, const gchar *keyword)
+{
+ GError *error = NULL;
+ gchar *map;
+ gchar *value;
+
+ if(!keyfile || !protocol || !keyword) return NULL;
+
+ map = g_key_file_get_string(keyfile, PH_INI_GROUP_MAP, protocol, &error);
+
+ if(!map)
+ {
+ g_log(NULL, G_LOG_LEVEL_DEBUG, "Protocol '%s' is not defined (%s)", protocol, error->message);
+ g_error_free(error);
+ return NULL;
+ }
+
+ value = g_key_file_get_string(keyfile, map, keyword, NULL);
+ g_free(map);
+ return value;
+}
+
+/** Given a protocol name, map it to a section in the keyfile, then
+ * return the keys in that section.
+ *
+ * @param keyfile The key file to search
+ * @param protocol Wireshark protocol name to map, e.g. "tcp".
+ * @return An array of keys in the mapped section. Must be freed with g_strfreev().
+ */
+static gchar**
+ph_ini_get_keywords(GKeyFile *keyfile, const gchar *protocol)
+{
+ GError *error = NULL;
+ gchar *map;
+ gchar **keys;
+ gsize length = 0;
+
+ if(!keyfile) return NULL;
+
+ map = g_key_file_get_string(keyfile, PH_INI_GROUP_MAP, protocol, &error);
+
+ if(!map)
+ {
+ g_log(NULL, G_LOG_LEVEL_DEBUG, "Protocol '%s' is not defined (%s)", protocol, error->message);
+ g_error_free(error);
+ return NULL;
+ }
+
+ error = NULL;
+ keys = g_key_file_get_keys(keyfile, map, &length, &error);
+ g_free(map);
+
+ if(!keys)
+ {
+ g_log(NULL, G_LOG_LEVEL_DEBUG, "Display titles are not defined (%s)", protocol);
+ g_error_free(error);
+ }
+
+ return keys;
+}
+
+/**
+* Function ph_parse_string
+*
+* @param const gchar *description Description
+* @param const gchar *value Description
+* @return guint Description
+*/
+static guint ph_parse_string(const gchar *description, const gchar *value)
+{
+ GRegex *regex = NULL;
+ GMatchInfo *match_info = NULL;
+ gchar *pattern = NULL;
+ guint result = 0;
+
+ pattern = g_strdup_printf("(?<![0-9a-zA-Z_])%s(?![0-9a-zA-Z_])", value);
+ regex = g_regex_new(pattern, 0, 0, NULL);
+
+ g_regex_match(regex, description, 0, &match_info);
+ if(g_match_info_matches(match_info)) result = 1;
+
+ g_match_info_free(match_info);
+ g_regex_unref(regex);
+
+ return result;
+}
+
+#ifdef PH_DEBUG_LOG
+/**
+ * Function ph_logging_handler
+ *
+ * @param const gchar *domain Description
+ * @param GLogLevelFlags level Description
+ * @param const gchar *message Description
+ * @param gpointer data Description
+ * @return void
+ */
+static void ph_logging_handler(const gchar *domain _U_, GLogLevelFlags level, const gchar *message, gpointer data _U_)
+{
+ gchar *log = NULL;
+ gchar *type = NULL;
+ FILE *file = NULL;
+ struct tm *timestamp = NULL;
+ time_t now;
+
+ time(&now);
+ timestamp = localtime(&now);
+
+ switch(level & G_LOG_LEVEL_MASK)
+ {
+ case G_LOG_LEVEL_ERROR:
+ type = "ERR";
+ break;
+
+ case G_LOG_LEVEL_DEBUG:
+ type = "WARNING";
+ break;
+
+ case G_LOG_LEVEL_INFO:
+ type = "INFO";
+ break;
+
+ default:
+ type = "OTHER";
+ }
+
+ file = ws_fopen(ph_log_path, "a+");
+
+ if(file)
+ {
+ log = g_strdup_printf("[%04u-%02u-%02u %02u:%02u:%02u %s] %s\n", timestamp->tm_year + 1900, timestamp->tm_mon + 1, timestamp->tm_mday, timestamp->tm_hour, timestamp->tm_min, timestamp->tm_sec, type, message);
+ fputs(log, file);
+ fclose(file);
+ }
+}
+#endif /* PH_DEBUG_LOG */
+#endif /* MAIN_MENU_USE_UIMANAGER */
diff --git a/ui/gtk/proto_help.h b/ui/gtk/proto_help.h
new file mode 100644
index 0000000000..b2d6b18602
--- /dev/null
+++ b/ui/gtk/proto_help.h
@@ -0,0 +1,51 @@
+/* proto_help.h
+ * Routines for dynamic protocol help menus
+ *
+ * $Id$
+ *
+ * Edgar Gladkich <edgar.gladkich@incacon.de>
+ * Gerald Combs <gerald@wireshark.org>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PROTO_HELP_H__
+#define __PROTO_HELP_H__
+
+/** Search for and read configuration files
+ *
+ */
+extern void proto_help_init(void);
+
+/** Initialize the menu
+ *
+ * @param widget Context menu root
+ * @return void
+ */
+extern void proto_help_menu_init(GtkWidget *widget);
+
+/** Fill in the protocol help menu
+ *
+ * @param selection Currently-selected packet
+ * @param cfile Capture file
+ * @return void
+ */
+extern void proto_help_menu_modify(GtkTreeSelection* selection, capture_file *cfile);
+
+#endif /* __PROTO_HELP_H__ */
diff --git a/ui/gtk/proto_hier_stats_dlg.c b/ui/gtk/proto_hier_stats_dlg.c
new file mode 100644
index 0000000000..21e95790e7
--- /dev/null
+++ b/ui/gtk/proto_hier_stats_dlg.c
@@ -0,0 +1,588 @@
+/* proto_hier_stats_dlg.c
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "../proto_hier_stats.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/proto_hier_stats_dlg.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/filter_utils.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/utf8_entities.h"
+
+
+enum {
+ PROTOCOL_COLUMN,
+ PRCT_PKTS_COLUMN,
+ PKTS_COLUMN,
+ PRCT_BYTES_COLUMN,
+ BYTES_COLUMN,
+ BANDWIDTH_COLUMN,
+ END_PKTS_COLUMN,
+ END_BYTES_COLUMN,
+ END_BANDWIDTH_COLUMN,
+ FILTER_NAME,
+ PRCT_PKTS_VALUE_COLUMN,
+ PRCT_BYTES_VALUE_COLUMN,
+ NUM_STAT_COLUMNS /* must be the last */
+};
+
+typedef struct {
+ GtkTreeView *tree_view;
+ GtkTreeIter *iter;
+ ph_stats_t *ps;
+} draw_info_t;
+
+static GtkWidget *tree;
+
+#define PCT(x,y) (100.0 * (float)(x) / (float)(y))
+#define BANDWIDTH(bytes,secs) ((bytes) * 8.0 / ((secs) * 1000.0 * 1000.0))
+
+static void
+proto_hier_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data _U_, guint callback_action)
+{
+ char *str = NULL;
+ const char *filter = NULL;
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(tree));
+ gtk_tree_selection_get_selected (sel, &model, &iter);
+ gtk_tree_model_get (model, &iter, FILTER_NAME, &filter, -1);
+ if (filter && 0 != strlen(filter)) {
+ str = g_strdup_printf("%s", filter);
+ } else {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not acquire information to build a filter!\nTry expanding or choosing another item.");
+ return;
+ }
+
+ apply_selected_filter (callback_action, str);
+
+ g_free (str);
+}
+
+
+/* Action callbacks */
+static void
+apply_as_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ proto_hier_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_SELECTED, 0));
+}
+static void
+apply_as_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ proto_hier_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_NOT_SELECTED, 0));
+}
+static void
+apply_as_and_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ proto_hier_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_SELECTED, 0));
+}
+static void
+apply_as_or_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ proto_hier_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_SELECTED, 0));
+}
+static void
+apply_as_and_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ proto_hier_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, 0));
+}
+static void
+apply_as_or_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ proto_hier_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, 0));
+}
+
+static void
+prep_as_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ proto_hier_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_SELECTED, 0));
+}
+static void
+prep_as_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ proto_hier_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, 0));
+}
+static void
+prep_as_and_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ proto_hier_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_SELECTED, 0));
+}
+static void
+prep_as_or_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ proto_hier_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_SELECTED, 0));
+}
+static void
+prep_as_and_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ proto_hier_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, 0));
+}
+static void
+prep_as_or_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ proto_hier_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, 0));
+}
+
+static void
+find_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ proto_hier_select_filter_cb( widget , user_data, CALLBACK_FIND_FRAME(ACTYPE_SELECTED, 0));
+}
+static void
+find_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ proto_hier_select_filter_cb( widget , user_data, CALLBACK_FIND_FRAME(ACTYPE_NOT_SELECTED, 0));
+}
+static void
+find_prev_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ proto_hier_select_filter_cb( widget , user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, 0));
+}
+static void
+find_prev_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ proto_hier_select_filter_cb( widget , user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_NOT_SELECTED, 0));
+}
+static void
+find_next_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ proto_hier_select_filter_cb( widget , user_data, CALLBACK_FIND_NEXT(ACTYPE_SELECTED, 0));
+}
+static void
+find_next_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ proto_hier_select_filter_cb( widget , user_data, CALLBACK_FIND_NEXT(ACTYPE_NOT_SELECTED, 0));
+}
+static void
+color_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ proto_hier_select_filter_cb( widget , user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, 0));
+}
+
+
+static const char *ui_desc_proto_hier_stats_filter_popup =
+"<ui>\n"
+" <popup name='ProtoHierStatsFilterPopup'>\n"
+" <menu action='/Apply as Filter'>\n"
+" <menuitem action='/Apply as Filter/Selected'/>\n"
+" <menuitem action='/Apply as Filter/Not Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected'/>\n"
+" </menu>\n"
+" <menu action='/Prepare a Filter'>\n"
+" <menuitem action='/Prepare a Filter/Selected'/>\n"
+" <menuitem action='/Prepare a Filter/Not Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected'/>\n"
+" </menu>\n"
+" <menu action='/Find Frame'>\n"
+" <menu action='/Find Frame/Find Frame'>\n"
+" <menuitem action='/Find Frame/Selected'/>\n"
+" <menuitem action='/Find Frame/Not Selected'/>\n"
+" </menu>\n"
+" <menu action='/Find Frame/Find Next'>\n"
+" <menuitem action='/Find Next/Selected'/>\n"
+" <menuitem action='/Find Next/Not Selected'/>\n"
+" </menu>\n"
+" <menu action='/Find Frame/Find Previous'>\n"
+" <menuitem action='/Find Previous/Selected'/>\n"
+" <menuitem action='/Find Previous/Not Selected'/>\n"
+" </menu>\n"
+" </menu>\n"
+" <menu action='/Colorize Procedure'>\n"
+" <menuitem action='/Colorize Procedure/Colorize Protocol'/>\n"
+" </menu>\n"
+" </popup>\n"
+"</ui>\n";
+
+/*
+ * GtkActionEntry
+ * typedef struct {
+ * const gchar *name;
+ * const gchar *stock_id;
+ * const gchar *label;
+ * const gchar *accelerator;
+ * const gchar *tooltip;
+ * GCallback callback;
+ * } GtkActionEntry;
+ * const gchar *name; The name of the action.
+ * const gchar *stock_id; The stock id for the action, or the name of an icon from the icon theme.
+ * const gchar *label; The label for the action. This field should typically be marked for translation,
+ * see gtk_action_group_set_translation_domain().
+ * If label is NULL, the label of the stock item with id stock_id is used.
+ * const gchar *accelerator; The accelerator for the action, in the format understood by gtk_accelerator_parse().
+ * const gchar *tooltip; The tooltip for the action. This field should typically be marked for translation,
+ * see gtk_action_group_set_translation_domain().
+ * GCallback callback; The function to call when the action is activated.
+ *
+ */
+static const GtkActionEntry proto_hier_stats_popup_entries[] = {
+ { "/Apply as Filter", NULL, "Apply as Filter", NULL, NULL, NULL },
+ { "/Prepare a Filter", NULL, "Prepare a Filter", NULL, NULL, NULL },
+ { "/Find Frame", NULL, "Find Frame", NULL, NULL, NULL },
+ { "/Find Frame/Find Frame", NULL, "Find Frame", NULL, NULL, NULL },
+ { "/Find Frame/Find Next", NULL, "Find Next" , NULL, NULL, NULL },
+ { "/Find Frame/Find Previous", NULL, "Find Previous", NULL, NULL, NULL },
+ { "/Colorize Procedure", NULL, "Colorize Procedure", NULL, NULL, NULL },
+ { "/Apply as Filter/Selected", NULL, "Selected", NULL, "Selected", G_CALLBACK(apply_as_selected_cb) },
+ { "/Apply as Filter/Not Selected", NULL, "Not Selected", NULL, "Not Selected", G_CALLBACK(apply_as_not_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", G_CALLBACK(apply_as_and_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", G_CALLBACK(apply_as_or_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", G_CALLBACK(apply_as_and_not_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", G_CALLBACK(apply_as_or_not_selected_cb) },
+ { "/Prepare a Filter/Selected", NULL, "Selected", NULL, "selcted", G_CALLBACK(prep_as_selected_cb) },
+ { "/Prepare a Filter/Not Selected", NULL, "Not Selected", NULL, "Not Selected", G_CALLBACK(prep_as_not_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", G_CALLBACK(prep_as_and_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", G_CALLBACK(prep_as_or_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", G_CALLBACK(prep_as_and_not_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", G_CALLBACK(prep_as_or_not_selected_cb) },
+ { "/Find Frame/Selected", NULL, "Selected", NULL, "Selected", G_CALLBACK(find_selected_cb) },
+ { "/Find Frame/Not Selected", NULL, "Not Selected", NULL, "Not Selected", G_CALLBACK(find_not_selected_cb) },
+ { "/Find Previous/Selected", NULL, "Selected", NULL, "Selected", G_CALLBACK(find_prev_selected_cb) },
+ { "/Find Previous/Not Selected", NULL, "Not Selected", NULL, "Not Selected", G_CALLBACK(find_prev_not_selected_cb) },
+ { "/Find Next/Selected", NULL, "Selected", NULL, "Selected", G_CALLBACK(find_next_selected_cb) },
+ { "/Find Next/Not Selected", NULL, "Not Selected", NULL, "Not Selected", G_CALLBACK(find_next_not_selected_cb) },
+ { "/Colorize Procedure/Colorize Protocol", NULL, "Colorize Protocol", NULL, "Colorize Protocol", G_CALLBACK(color_selected_cb) },
+};
+
+static void
+fill_in_tree_node(GNode *node, gpointer data)
+{
+ ph_stats_node_t *stats = node->data;
+ draw_info_t *di = data;
+ ph_stats_t *ps = di->ps;
+ draw_info_t child_di;
+ double seconds;
+ gchar *text[NUM_STAT_COLUMNS];
+ float percent_packets, percent_bytes;
+ GtkTreeView *tree_view = di->tree_view;
+ GtkTreeIter *iter = di->iter;
+ GtkTreeStore *store;
+ GtkTreeIter new_iter;
+
+ seconds = ps->last_time - ps->first_time;
+
+ percent_packets = (float) PCT(stats->num_pkts_total, ps->tot_packets);
+ percent_bytes = (float) PCT(stats->num_bytes_total, ps->tot_bytes);
+ text[PROTOCOL_COLUMN] = (gchar *) (stats->hfinfo->name);
+ text[PRCT_PKTS_COLUMN] = g_strdup_printf("%.2f %%", percent_packets);
+ text[PKTS_COLUMN] = g_strdup_printf("%u", stats->num_pkts_total);
+ text[PRCT_BYTES_COLUMN] = g_strdup_printf("%.2f %%", percent_bytes);
+ text[BYTES_COLUMN] = g_strdup_printf("%u", stats->num_bytes_total);
+ if (seconds > 0.0) {
+ text[BANDWIDTH_COLUMN] = g_strdup_printf("%.3f",
+ BANDWIDTH(stats->num_bytes_total, seconds));
+ } else {
+ text[BANDWIDTH_COLUMN] = "n.c.";
+ }
+ text[END_PKTS_COLUMN] = g_strdup_printf("%u", stats->num_pkts_last);
+ text[END_BYTES_COLUMN] = g_strdup_printf("%u", stats->num_bytes_last);
+ if (seconds > 0.0) {
+ text[END_BANDWIDTH_COLUMN] = g_strdup_printf("%.3f",
+ BANDWIDTH(stats->num_bytes_last, seconds));
+ } else {
+ text[END_BANDWIDTH_COLUMN] = "n.c.";
+ }
+
+ store = GTK_TREE_STORE(gtk_tree_view_get_model(tree_view));
+ gtk_tree_store_append(store, &new_iter, iter);
+ gtk_tree_store_set(store, &new_iter,
+ PROTOCOL_COLUMN, text[PROTOCOL_COLUMN],
+ PRCT_PKTS_COLUMN, text[PRCT_PKTS_COLUMN],
+ PKTS_COLUMN, text[PKTS_COLUMN],
+ PRCT_BYTES_COLUMN, text[PRCT_BYTES_COLUMN],
+ BYTES_COLUMN, text[BYTES_COLUMN],
+ BANDWIDTH_COLUMN, text[BANDWIDTH_COLUMN],
+ END_PKTS_COLUMN, text[END_PKTS_COLUMN],
+ END_BYTES_COLUMN, text[END_BYTES_COLUMN],
+ END_BANDWIDTH_COLUMN, text[END_BANDWIDTH_COLUMN],
+ FILTER_NAME, stats->hfinfo->abbrev,
+ PRCT_PKTS_VALUE_COLUMN, percent_packets,
+ PRCT_BYTES_VALUE_COLUMN, percent_bytes,
+ -1);
+
+ g_free(text[PRCT_PKTS_COLUMN]);
+ g_free(text[PKTS_COLUMN]);
+ g_free(text[PRCT_BYTES_COLUMN]);
+ g_free(text[BYTES_COLUMN]);
+ if (seconds > 0.0) g_free(text[BANDWIDTH_COLUMN]);
+ g_free(text[END_PKTS_COLUMN]);
+ g_free(text[END_BYTES_COLUMN]);
+ if (seconds > 0.0) g_free(text[END_BANDWIDTH_COLUMN]);
+
+ child_di.tree_view = tree_view;
+ child_di.iter = &new_iter;
+ child_di.ps = ps;
+
+ g_node_children_foreach(node, G_TRAVERSE_ALL,
+ fill_in_tree_node, &child_di);
+}
+
+static void
+fill_in_tree(GtkWidget *tree_lcl, ph_stats_t *ps)
+{
+ draw_info_t di;
+
+ di.tree_view = GTK_TREE_VIEW(tree_lcl);
+ di.iter = NULL;
+ di.ps = ps;
+
+ g_node_children_foreach(ps->stats_tree, G_TRAVERSE_ALL,
+ fill_in_tree_node, &di);
+}
+
+
+static gboolean
+proto_hier_show_popup_menu_cb(GtkWidget *widget _U_, GdkEvent *event, GtkWidget *popup_menu_object)
+{
+ GdkEventButton *bevent = (GdkEventButton *)event;
+
+ if (event->type==GDK_BUTTON_PRESS && bevent->button==3) {
+ /* If this is a right click on one of our columns, popup the context menu */
+ gtk_menu_popup(GTK_MENU(popup_menu_object), NULL, NULL, NULL, NULL, bevent->button, bevent->time);
+ }
+
+ return FALSE;
+}
+
+static void
+proto_hier_create_popup_menu(void)
+{
+
+ GtkUIManager *ui_manager;
+ GtkActionGroup *action_group;
+ GError *error = NULL;
+ GtkWidget *popup_menu_object;
+
+ action_group = gtk_action_group_new ("ProtoHierStatsTFilterPopupActionGroup");
+ gtk_action_group_add_actions (action_group, /* the action group */
+ (gpointer)proto_hier_stats_popup_entries, /* an array of action descriptions */
+ G_N_ELEMENTS(proto_hier_stats_popup_entries), /* the number of entries */
+ NULL); /* data to pass to the action callbacks */
+
+ ui_manager = gtk_ui_manager_new ();
+ gtk_ui_manager_insert_action_group (ui_manager,
+ action_group,
+ 0); /* the position at which the group will be inserted */
+ gtk_ui_manager_add_ui_from_string (ui_manager,ui_desc_proto_hier_stats_filter_popup, -1, &error);
+ if (error != NULL)
+ {
+ fprintf (stderr, "Warning: building proto hier ststs filter popup failed: %s\n",
+ error->message);
+ g_error_free (error);
+ error = NULL;
+ }
+ popup_menu_object = gtk_ui_manager_get_widget(ui_manager, "/ProtoHierStatsFilterPopup");
+ g_signal_connect(tree, "button_press_event", G_CALLBACK(proto_hier_show_popup_menu_cb), popup_menu_object);
+
+}
+
+#define MAX_DLG_HEIGHT 450
+#define DEF_DLG_WIDTH 700
+static void
+create_tree(GtkWidget *container, ph_stats_t *ps)
+{
+ GtkWidget *sw;
+ GtkTreeView *tree_view;
+ GtkTreeStore *store;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ /* Scrolled Window */
+ sw = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
+ GTK_SHADOW_IN);
+ gtk_container_add(GTK_CONTAINER(container), sw);
+
+ store = gtk_tree_store_new(NUM_STAT_COLUMNS, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_FLOAT,
+ G_TYPE_FLOAT);
+ tree = tree_view_new(GTK_TREE_MODEL(store));
+ g_object_unref(G_OBJECT(store));
+ tree_view = GTK_TREE_VIEW(tree);
+ gtk_tree_view_set_headers_visible(tree_view, TRUE);
+ gtk_tree_view_set_headers_clickable(tree_view, FALSE);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Protocol", renderer,
+ "text", PROTOCOL_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(tree_view, column);
+ renderer = gtk_cell_renderer_progress_new();
+ column = gtk_tree_view_column_new_with_attributes("% Packets", renderer,
+ "text", PRCT_PKTS_COLUMN,
+ "value", PRCT_PKTS_VALUE_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_expand(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(tree_view, column);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Packets", renderer,
+ "text", PKTS_COLUMN,
+ NULL);
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(tree_view, column);
+ renderer = gtk_cell_renderer_progress_new();
+ column = gtk_tree_view_column_new_with_attributes("% Bytes", renderer,
+ "text", PRCT_BYTES_COLUMN,
+ "value", PRCT_BYTES_VALUE_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_expand(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(tree_view, column);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Bytes", renderer,
+ "text", BYTES_COLUMN,
+ NULL);
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(tree_view, column);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Mbit/s", renderer,
+ "text", BANDWIDTH_COLUMN,
+ NULL);
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(tree_view, column);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("End Packets", renderer,
+ "text", END_PKTS_COLUMN,
+ NULL);
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(tree_view, column);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("End Bytes", renderer,
+ "text", END_BYTES_COLUMN,
+ NULL);
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(tree_view, column);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("End Mbit/s", renderer,
+ "text", END_BANDWIDTH_COLUMN,
+ NULL);
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column(tree_view, column);
+
+ /* Fill in the data. */
+ fill_in_tree(tree, ps);
+
+ gtk_widget_set_size_request(tree, DEF_DLG_WIDTH, MAX_DLG_HEIGHT);
+ gtk_tree_view_expand_all(tree_view);
+
+ proto_hier_create_popup_menu ();
+
+ gtk_container_add(GTK_CONTAINER(sw), tree);
+}
+
+void
+proto_hier_stats_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ ph_stats_t *ps;
+ GtkWidget *dlg, *close_bt, *help_bt, *vbox, *bbox;
+ GtkWidget *label;
+ char title[256];
+ const char *current_filter;
+
+ /* Get the statistics. */
+ ps = ph_stats_new();
+ if (ps == NULL) {
+ /* The user gave up before we finished; don't pop up
+ a statistics window. */
+ return;
+ }
+
+ dlg = window_new(GTK_WINDOW_TOPLEVEL, "Wireshark: Protocol Hierarchy Statistics");
+
+ vbox = gtk_vbox_new(FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
+ gtk_container_add(GTK_CONTAINER(dlg), vbox);
+
+ current_filter=gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
+
+ if (current_filter && strlen(current_filter) != 0) {
+ g_snprintf(title, sizeof(title), "Display filter: %s", current_filter);
+ } else {
+ g_strlcpy(title, "Display filter: none", sizeof(title));
+ }
+ label = gtk_label_new(title);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ /* Data section */
+ create_tree(vbox, ps);
+
+ ph_stats_free(ps);
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(dlg, close_bt, window_cancel_button_cb);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_PROTO_HIERARCHY_DIALOG);
+
+ g_signal_connect(dlg, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+
+ gtk_widget_show_all(dlg);
+ window_present(dlg);
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/gtk/proto_hier_stats_dlg.h b/ui/gtk/proto_hier_stats_dlg.h
new file mode 100644
index 0000000000..9a8281428b
--- /dev/null
+++ b/ui/gtk/proto_hier_stats_dlg.h
@@ -0,0 +1,39 @@
+/* proto_hier_stats_dlg.h
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PROTO_HIER_STATS_DLG_H__
+#define __PROTO_HIER_STATS_DLG_H__
+
+/** @file
+ * "Protocol Hierarchy" statistics window.
+ */
+
+/** Show protocol hierarchy statistics.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+extern void proto_hier_stats_cb(GtkWidget *widget, gpointer data);
+
+#endif
diff --git a/ui/gtk/radius_stat.c b/ui/gtk/radius_stat.c
new file mode 100644
index 0000000000..0c07503b4b
--- /dev/null
+++ b/ui/gtk/radius_stat.c
@@ -0,0 +1,398 @@
+/* radius_stat.c
+ * radius-statistics for Wireshark
+ * Copyright 2006 Alejandro Vaquero <alejandrovaquero@yahoo.com>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/value_string.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-radius.h>
+
+#include "../timestats.h"
+#include "../simple_dialog.h"
+#include "../file.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_util.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/main.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+#define NUM_COLUMNS 11
+
+typedef enum _radius_category {
+ RADIUS_CAT_OVERALL = 0,
+ RADIUS_CAT_ACCESS,
+ RADIUS_CAT_ACCOUNTING,
+ RADIUS_CAT_PASSWORD,
+ RADIUS_CAT_RESOURCE_FREE,
+ RADIUS_CAT_RESOURCE_QUERY,
+ RADIUS_CAT_NAS_REBOOT,
+ RADIUS_CAT_EVENT,
+ RADIUS_CAT_DISCONNECT,
+ RADIUS_CAT_COA,
+ RADIUS_CAT_OTHERS,
+ RADIUS_CAT_NUM_TIMESTATS
+} radius_category;
+
+/* Summary of response-time calculations*/
+typedef struct _radius_rtd_t {
+ guint32 open_req_num;
+ guint32 disc_rsp_num;
+ guint32 req_dup_num;
+ guint32 rsp_dup_num;
+ timestat_t stats;
+} radius_rtd_t;
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _radiusstat_t {
+ GtkWidget *win;
+ GtkWidget *vbox;
+ char *filter;
+ GtkWidget *scrolled_window;
+ GtkTreeView *table;
+ radius_rtd_t radius_rtd[RADIUS_CAT_NUM_TIMESTATS];
+} radiusstat_t;
+
+static const value_string radius_message_code[] = {
+ { RADIUS_CAT_OVERALL, "Overall"},
+ { RADIUS_CAT_ACCESS, "Access"},
+ { RADIUS_CAT_ACCOUNTING, "Accounting"},
+ { RADIUS_CAT_PASSWORD, "Password"},
+ { RADIUS_CAT_RESOURCE_FREE, "Resource Free"},
+ { RADIUS_CAT_RESOURCE_QUERY, "Resource Query"},
+ { RADIUS_CAT_NAS_REBOOT, "NAS Reboot"},
+ { RADIUS_CAT_EVENT, "Event"},
+ { RADIUS_CAT_DISCONNECT, "Disconnect"},
+ { RADIUS_CAT_COA, "CoA"},
+ { RADIUS_CAT_OTHERS, "Other"},
+ { 0, NULL}
+};
+
+static void
+radiusstat_reset(void *prs)
+{
+ radiusstat_t *rs=(radiusstat_t *)prs;
+ int i;
+
+
+ for(i=0; i<RADIUS_CAT_NUM_TIMESTATS; i++) {
+ rs->radius_rtd[i].stats.num=0;
+ rs->radius_rtd[i].stats.min_num=0;
+ rs->radius_rtd[i].stats.max_num=0;
+ rs->radius_rtd[i].stats.min.secs=0;
+ rs->radius_rtd[i].stats.min.nsecs=0;
+ rs->radius_rtd[i].stats.max.secs=0;
+ rs->radius_rtd[i].stats.max.nsecs=0;
+ rs->radius_rtd[i].stats.tot.secs=0;
+ rs->radius_rtd[i].stats.tot.nsecs=0;
+ rs->radius_rtd[i].open_req_num = 0;
+ rs->radius_rtd[i].disc_rsp_num = 0;
+ rs->radius_rtd[i].req_dup_num = 0;
+ rs->radius_rtd[i].rsp_dup_num = 0;
+ }
+
+}
+
+
+static int
+radiusstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pri)
+{
+ radiusstat_t *rs=(radiusstat_t *)prs;
+ const radius_info_t *ri=pri;
+ nstime_t delta;
+ radius_category radius_cat = RADIUS_CAT_OTHERS;
+ int ret = 0;
+
+ switch (ri->code) {
+ case RADIUS_PKT_TYPE_ACCESS_REQUEST:
+ case RADIUS_PKT_TYPE_ACCESS_ACCEPT:
+ case RADIUS_PKT_TYPE_ACCESS_REJECT:
+ radius_cat = RADIUS_CAT_ACCESS;
+ break;
+ case RADIUS_PKT_TYPE_ACCOUNTING_REQUEST:
+ case RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE:
+ radius_cat = RADIUS_CAT_ACCOUNTING;
+ break;
+ case RADIUS_PKT_TYPE_PASSWORD_REQUEST:
+ case RADIUS_PKT_TYPE_PASSWORD_ACK:
+ case RADIUS_PKT_TYPE_PASSWORD_REJECT:
+ radius_cat = RADIUS_CAT_PASSWORD;
+ break;
+ case RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST:
+ case RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE:
+ radius_cat = RADIUS_CAT_RESOURCE_FREE;
+ break;
+ case RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST:
+ case RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE:
+ radius_cat = RADIUS_CAT_RESOURCE_QUERY;
+ break;
+ case RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST:
+ case RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE:
+ radius_cat = RADIUS_CAT_NAS_REBOOT;
+ break;
+ case RADIUS_PKT_TYPE_EVENT_REQUEST:
+ case RADIUS_PKT_TYPE_EVENT_RESPONSE:
+ radius_cat = RADIUS_CAT_EVENT;
+ break;
+ case RADIUS_PKT_TYPE_DISCONNECT_REQUEST:
+ case RADIUS_PKT_TYPE_DISCONNECT_ACK:
+ case RADIUS_PKT_TYPE_DISCONNECT_NAK:
+ radius_cat = RADIUS_CAT_DISCONNECT;
+ break;
+ case RADIUS_PKT_TYPE_COA_REQUEST:
+ case RADIUS_PKT_TYPE_COA_ACK:
+ case RADIUS_PKT_TYPE_COA_NAK:
+ radius_cat = RADIUS_CAT_COA;
+ break;
+ }
+
+ switch (ri->code) {
+
+ case RADIUS_PKT_TYPE_ACCESS_REQUEST:
+ case RADIUS_PKT_TYPE_ACCOUNTING_REQUEST:
+ case RADIUS_PKT_TYPE_PASSWORD_REQUEST:
+ case RADIUS_PKT_TYPE_EVENT_REQUEST:
+ case RADIUS_PKT_TYPE_DISCONNECT_REQUEST:
+ case RADIUS_PKT_TYPE_COA_REQUEST:
+ if(ri->is_duplicate){
+ /* Duplicate is ignored */
+ rs->radius_rtd[RADIUS_CAT_OVERALL].req_dup_num++;
+ rs->radius_rtd[radius_cat].req_dup_num++;
+ }
+ else {
+ rs->radius_rtd[RADIUS_CAT_OVERALL].open_req_num++;
+ rs->radius_rtd[radius_cat].open_req_num++;
+ }
+ break;
+
+ case RADIUS_PKT_TYPE_ACCESS_ACCEPT:
+ case RADIUS_PKT_TYPE_ACCESS_REJECT:
+ case RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE:
+ case RADIUS_PKT_TYPE_PASSWORD_ACK:
+ case RADIUS_PKT_TYPE_PASSWORD_REJECT:
+ case RADIUS_PKT_TYPE_EVENT_RESPONSE:
+ case RADIUS_PKT_TYPE_DISCONNECT_ACK:
+ case RADIUS_PKT_TYPE_DISCONNECT_NAK:
+ case RADIUS_PKT_TYPE_COA_ACK:
+ case RADIUS_PKT_TYPE_COA_NAK:
+ if(ri->is_duplicate){
+ /* Duplicate is ignored */
+ rs->radius_rtd[RADIUS_CAT_OVERALL].rsp_dup_num++;
+ rs->radius_rtd[radius_cat].rsp_dup_num++;
+ }
+ else if (!ri->request_available) {
+ /* no request was seen */
+ rs->radius_rtd[RADIUS_CAT_OVERALL].disc_rsp_num++;
+ rs->radius_rtd[radius_cat].disc_rsp_num++;
+ }
+ else {
+ rs->radius_rtd[RADIUS_CAT_OVERALL].open_req_num--;
+ rs->radius_rtd[radius_cat].open_req_num--;
+ /* calculate time delta between request and response */
+ nstime_delta(&delta, &pinfo->fd->abs_ts, &ri->req_time);
+
+ time_stat_update(&(rs->radius_rtd[RADIUS_CAT_OVERALL].stats),&delta, pinfo);
+ time_stat_update(&(rs->radius_rtd[radius_cat].stats),&delta, pinfo);
+
+ ret = 1;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static void
+radiusstat_draw(void *prs)
+{
+ radiusstat_t *rs=(radiusstat_t *)prs;
+ int i;
+ char str[5][256];
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ /* clear list before printing */
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(rs->table));
+ gtk_list_store_clear(store);
+
+ for(i=0; i<RADIUS_CAT_NUM_TIMESTATS; i++) {
+ /* nothing seen, nothing to do */
+ if(rs->radius_rtd[i].stats.num==0){
+ continue;
+ }
+ g_snprintf(str[0], 256, "%8.2f msec", nstime_to_msec(&(rs->radius_rtd[i].stats.min)));
+ g_snprintf(str[1], 256, "%8.2f msec", nstime_to_msec(&(rs->radius_rtd[i].stats.max)));
+ g_snprintf(str[2], 256, "%8.2f msec", get_average(&(rs->radius_rtd[i].stats.tot), rs->radius_rtd[i].stats.num));
+ g_snprintf(str[3], 256, "%4u (%4.2f%%)", rs->radius_rtd[i].req_dup_num,
+ rs->radius_rtd[i].stats.num?((double)rs->radius_rtd[i].req_dup_num*100)/(double)rs->radius_rtd[i].stats.num:0);
+ g_snprintf(str[4], 256, "%4u (%4.2f%%)", rs->radius_rtd[i].rsp_dup_num,
+ rs->radius_rtd[i].stats.num?((double)rs->radius_rtd[i].rsp_dup_num*100)/(double)rs->radius_rtd[i].stats.num:0);
+
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, val_to_str(i, radius_message_code,"Other"),
+ 1, rs->radius_rtd[i].stats.num,
+ 2, str[0],
+ 3, str[1],
+ 4, str[2],
+ 5, rs->radius_rtd[i].stats.min_num,
+ 6, rs->radius_rtd[i].stats.max_num,
+ 7, rs->radius_rtd[i].open_req_num,
+ 8, rs->radius_rtd[i].disc_rsp_num,
+ 9, str[3],
+ 10, str[4],
+ -1);
+ }
+}
+
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ radiusstat_t *rs=(radiusstat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(rs);
+ unprotect_thread_critical_region();
+
+ if(rs->filter){
+ g_free(rs->filter);
+ rs->filter=NULL;
+ }
+ g_free(rs);
+}
+
+static const stat_column titles[]={
+ {G_TYPE_STRING, LEFT, "Type" },
+ {G_TYPE_UINT, RIGHT, "Messages" },
+ {G_TYPE_STRING, RIGHT, "Min SRT" },
+ {G_TYPE_STRING, RIGHT, "Max SRT" },
+ {G_TYPE_STRING, RIGHT, "Avg SRT" },
+ {G_TYPE_UINT, RIGHT, "Min in Frame" },
+ {G_TYPE_UINT, RIGHT, "Max in Frame" },
+ {G_TYPE_UINT, RIGHT, "Open Requests" },
+ {G_TYPE_UINT, RIGHT, "Discarded Responses" },
+ {G_TYPE_STRING, RIGHT, "Repeated Requests" },
+ {G_TYPE_STRING, RIGHT, "Repeated Responses"}
+};
+
+static void
+gtk_radiusstat_init(const char *optarg, void *userdata _U_)
+{
+ radiusstat_t *rs;
+ GString *error_string;
+ GtkWidget *bt_close;
+ GtkWidget *bbox;
+
+ rs=g_malloc(sizeof(radiusstat_t));
+
+ if(strncmp(optarg,"radius,srt,",11) == 0){
+ rs->filter=g_strdup(optarg+11);
+ } else {
+ rs->filter=NULL;
+ }
+
+ radiusstat_reset(rs);
+
+ rs->win = dlg_window_new("RADIUS SRT"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(rs->win), TRUE);
+ gtk_window_set_default_size(GTK_WINDOW(rs->win), 600, 150);
+
+ rs->vbox=gtk_vbox_new(FALSE, 3);
+
+ init_main_stat_window(rs->win, rs->vbox, "RADIUS Service Response Time (SRT) Statistics", rs->filter);
+
+ /* init a scrolled window*/
+ rs->scrolled_window = scrolled_window_new(NULL, NULL);
+
+ rs->table = create_stat_table(rs->scrolled_window, rs->vbox, NUM_COLUMNS, titles);
+
+ error_string=register_tap_listener("radius", rs, rs->filter, 0, radiusstat_reset, radiusstat_packet, radiusstat_draw);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(rs->filter);
+ g_free(rs);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_start(GTK_BOX(rs->vbox), bbox, FALSE, FALSE, 0);
+
+ bt_close = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(rs->win, bt_close, window_cancel_button_cb);
+
+ g_signal_connect(rs->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(rs->win, "destroy", G_CALLBACK(win_destroy_cb), rs);
+
+ gtk_widget_show_all(rs->win);
+ window_present(rs->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(rs->win));
+}
+
+static tap_param radius_stat_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg radius_srt_dlg = {
+ "RADIUS Service Response Time (SRT) Statistics",
+ "radius,srt",
+ gtk_radiusstat_init,
+ -1,
+ G_N_ELEMENTS(radius_stat_params),
+ radius_stat_params
+};
+
+void
+register_tap_listener_gtkradiusstat(void)
+{
+ register_dfilter_stat(&radius_srt_dlg, "RADIUS",
+ REGISTER_STAT_GROUP_RESPONSE_TIME);
+}
+void radius_srt_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &radius_srt_dlg);
+}
+
diff --git a/ui/gtk/range_utils.c b/ui/gtk/range_utils.c
new file mode 100644
index 0000000000..f32e6061ac
--- /dev/null
+++ b/ui/gtk/range_utils.c
@@ -0,0 +1,659 @@
+/* range_utils.c
+ * Packet range routines (save, print, ...) for GTK things
+ *
+ * $Id$
+ *
+ * Ulf Lamping <ulf.lamping@web.de>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include "../globals.h"
+#include "../simple_dialog.h"
+#include "../packet-range.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/range_utils.h"
+
+
+#define RANGE_VALUES_KEY "range_values"
+#define RANGE_CAPTURED_BT_KEY "range_captured_button"
+#define RANGE_DISPLAYED_BT_KEY "range_displayed_button"
+
+#define RANGE_SELECT_ALL_KEY "range_select_all_rb"
+#define RANGE_SELECT_ALL_C_KEY "range_select_all_c_lb"
+#define RANGE_SELECT_ALL_D_KEY "range_select_all_d_lb"
+#define RANGE_SELECT_CURR_KEY "range_select_curr_rb"
+#define RANGE_SELECT_CURR_C_KEY "range_select_curr_c_lb"
+#define RANGE_SELECT_CURR_D_KEY "range_select_curr_d_lb"
+#define RANGE_SELECT_MARKED_KEY "range_select_marked_only_rb"
+#define RANGE_SELECT_MARKED_C_KEY "range_select_marked_only_c_lb"
+#define RANGE_SELECT_MARKED_D_KEY "range_select_marked_only_d_lb"
+#define RANGE_SELECT_MARKED_RANGE_KEY "range_select_marked_range_rb"
+#define RANGE_SELECT_MARKED_RANGE_C_KEY "range_select_marked_range_c_lb"
+#define RANGE_SELECT_MARKED_RANGE_D_KEY "range_select_marked_range_d_lb"
+#define RANGE_SELECT_USER_KEY "range_select_user_range_rb"
+#define RANGE_SELECT_USER_C_KEY "range_select_user_range_c_lb"
+#define RANGE_SELECT_USER_D_KEY "range_select_user_range_d_lb"
+#define RANGE_SELECT_USER_ENTRY_KEY "range_select_user_range_entry"
+
+#define RANGE_REMOVE_IGNORED_KEY "range_remove_ignored"
+#define RANGE_IGNORED_C_KEY "range_ignored_c_lb"
+#define RANGE_IGNORED_D_KEY "range_ignored_d_lb"
+
+gboolean
+range_check_validity(packet_range_t *range)
+{
+ switch (packet_range_check(range)) {
+
+ case CVT_NO_ERROR:
+ return TRUE;
+
+ case CVT_SYNTAX_ERROR:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "The specified range of packets isn't a valid range.");
+ return FALSE;
+
+ case CVT_NUMBER_TOO_BIG:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "The specified range of packets has a packet number that's too large.");
+ return FALSE;
+
+ default:
+ g_assert_not_reached();
+ return FALSE;
+ }
+}
+
+/* update all "dynamic" things */
+void
+range_update_dynamics(gpointer data)
+{
+ packet_range_t *range;
+ GtkWidget *range_displayed_bt;
+ gboolean filtered_active;
+ gint selected_num;
+ gboolean can_select;
+ gboolean selected_packets;
+ gchar label_text[100];
+ guint32 ignored_cnt = 0, displayed_ignored_cnt = 0;
+
+ range = g_object_get_data(G_OBJECT(data), RANGE_VALUES_KEY);
+
+ range_displayed_bt = g_object_get_data(G_OBJECT(data), RANGE_DISPLAYED_BT_KEY);
+ filtered_active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(range_displayed_bt));
+
+ /* Enable saving only the displayed packets only if there *are*
+ displayed packets. */
+ if (range->displayed_cnt != 0)
+ gtk_widget_set_sensitive(range_displayed_bt, TRUE);
+ else {
+ /* If saving the displayed packets is selected, select saving the
+ captured packets. */
+ filtered_active = FALSE;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g_object_get_data(G_OBJECT(data), RANGE_SELECT_ALL_KEY)), FALSE);
+ gtk_widget_set_sensitive(range_displayed_bt, FALSE);
+ }
+
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_ALL_C_KEY), !filtered_active);
+ if (range->remove_ignored) {
+ g_snprintf(label_text, sizeof(label_text), "%u", cfile.count - range->ignored_cnt);
+ } else {
+ g_snprintf(label_text, sizeof(label_text), "%u", cfile.count);
+ }
+ gtk_label_set_text(GTK_LABEL(g_object_get_data(G_OBJECT(data), RANGE_SELECT_ALL_C_KEY)), label_text);
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_ALL_D_KEY), filtered_active);
+ if (range->remove_ignored) {
+ g_snprintf(label_text, sizeof(label_text), "%u", range->displayed_cnt - range->displayed_ignored_cnt);
+ } else {
+ g_snprintf(label_text, sizeof(label_text), "%u", range->displayed_cnt);
+ }
+ gtk_label_set_text(GTK_LABEL(g_object_get_data(G_OBJECT(data), RANGE_SELECT_ALL_D_KEY)), label_text);
+
+ /* Enable saving the currently-selected packet only if there *is* a
+ currently-selected packet. */
+ selected_num = (cfile.current_frame) ? cfile.current_frame->num : 0;
+ can_select = (selected_num != 0);
+ if (can_select) {
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_CURR_KEY), TRUE);
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_CURR_C_KEY), !filtered_active);
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_CURR_D_KEY), filtered_active);
+ } else {
+ /* If "save selected packet" is selected, select "save all packets". */
+ if (range->process == range_process_selected) {
+ range->process = range_process_all;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g_object_get_data(G_OBJECT(data), RANGE_SELECT_ALL_KEY)), TRUE);
+ }
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_CURR_KEY), FALSE);
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_CURR_C_KEY), FALSE);
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_CURR_D_KEY), FALSE);
+ }
+ /* XXX: how to update the radio button label but keep the mnemonic? */
+/*g_snprintf(label_text, sizeof(label_text), "_Selected packet #%u only", selected_num);
+ gtk_label_set_text(GTK_LABEL(GTK_BIN(select_curr_rb)->child), label_text);*/
+ if (range->remove_ignored && can_select && cfile.current_frame->flags.ignored) {
+ g_snprintf(label_text, sizeof(label_text), "0");
+ } else {
+ g_snprintf(label_text, sizeof(label_text), "%u", selected_num ? 1 : 0);
+ }
+ gtk_label_set_text(GTK_LABEL(g_object_get_data(G_OBJECT(data), RANGE_SELECT_CURR_C_KEY)), label_text);
+ if (range->remove_ignored && can_select && cfile.current_frame->flags.ignored) {
+ g_snprintf(label_text, sizeof(label_text), "0");
+ } else {
+ g_snprintf(label_text, sizeof(label_text), "%u", selected_num ? 1 : 0);
+ }
+ gtk_label_set_text(GTK_LABEL(g_object_get_data(G_OBJECT(data), RANGE_SELECT_CURR_D_KEY)), label_text);
+
+ /* Enable the buttons for saving marked packets only if there *are*
+ marked packets. */
+ if (filtered_active)
+ selected_packets = (range->displayed_marked_cnt != 0);
+ else
+ selected_packets = (cfile.marked_count > 0);
+ if (selected_packets) {
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_MARKED_KEY), TRUE);
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_MARKED_C_KEY), !filtered_active);
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_MARKED_D_KEY), filtered_active);
+ }
+ else {
+ /* If "save marked packet" is selected, select "save all packets". */
+ if (range->process == range_process_marked) {
+ range->process = range_process_all;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g_object_get_data(G_OBJECT(data), RANGE_SELECT_ALL_KEY)), TRUE);
+ }
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_MARKED_KEY), FALSE);
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_MARKED_C_KEY), FALSE);
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_MARKED_D_KEY), FALSE);
+ }
+ if (range->remove_ignored) {
+ g_snprintf(label_text, sizeof(label_text), "%u", cfile.marked_count - range->ignored_marked_cnt);
+ } else {
+ g_snprintf(label_text, sizeof(label_text), "%u", cfile.marked_count);
+ }
+ gtk_label_set_text(GTK_LABEL(g_object_get_data(G_OBJECT(data), RANGE_SELECT_MARKED_C_KEY)), label_text);
+ if (range->remove_ignored) {
+ g_snprintf(label_text, sizeof(label_text), "%u", range->displayed_marked_cnt - range->displayed_ignored_marked_cnt);
+ } else {
+ g_snprintf(label_text, sizeof(label_text), "%u", range->displayed_marked_cnt);
+ }
+ gtk_label_set_text(GTK_LABEL(g_object_get_data(G_OBJECT(data), RANGE_SELECT_MARKED_D_KEY)), label_text);
+
+ /* Enable the buttons for saving the range of marked packets only if
+ there *is* a range of marked packets. */
+ if (filtered_active)
+ selected_packets = (range->displayed_mark_range_cnt != 0);
+ else
+ selected_packets = (range->mark_range_cnt != 0);
+ if (selected_packets) {
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_MARKED_RANGE_KEY), TRUE);
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_MARKED_RANGE_C_KEY), !filtered_active);
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_MARKED_RANGE_D_KEY), filtered_active);
+ }
+ else {
+ /* If "save range between first and last marked packet" is selected,
+ select "save all packets". */
+ if (range->process == range_process_marked_range) {
+ range->process = range_process_all;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(g_object_get_data(G_OBJECT(data), RANGE_SELECT_ALL_KEY)), TRUE);
+ }
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_MARKED_RANGE_KEY), FALSE);
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_MARKED_RANGE_C_KEY), FALSE);
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_MARKED_RANGE_D_KEY), FALSE);
+ }
+ if (range->remove_ignored) {
+ g_snprintf(label_text, sizeof(label_text), "%u", range->mark_range_cnt - range->ignored_mark_range_cnt);
+ } else {
+ g_snprintf(label_text, sizeof(label_text), "%u", range->mark_range_cnt);
+ }
+ gtk_label_set_text(GTK_LABEL(g_object_get_data(G_OBJECT(data), RANGE_SELECT_MARKED_RANGE_C_KEY)), label_text);
+ if (range->remove_ignored) {
+ g_snprintf(label_text, sizeof(label_text), "%u", range->displayed_mark_range_cnt - range->displayed_ignored_mark_range_cnt);
+ } else {
+ g_snprintf(label_text, sizeof(label_text), "%u", range->displayed_mark_range_cnt);
+ }
+ gtk_label_set_text(GTK_LABEL(g_object_get_data(G_OBJECT(data), RANGE_SELECT_MARKED_RANGE_D_KEY)), label_text);
+
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_USER_KEY), TRUE);
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_USER_C_KEY), !filtered_active);
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_USER_D_KEY), filtered_active);
+ if (range->remove_ignored) {
+ g_snprintf(label_text, sizeof(label_text), "%u", range->user_range_cnt - range->ignored_user_range_cnt);
+ } else {
+ g_snprintf(label_text, sizeof(label_text), "%u", range->user_range_cnt);
+ }
+ gtk_label_set_text(GTK_LABEL(g_object_get_data(G_OBJECT(data), RANGE_SELECT_USER_C_KEY)), label_text);
+ if (range->remove_ignored) {
+ g_snprintf(label_text, sizeof(label_text), "%u", range->displayed_user_range_cnt - range->displayed_ignored_user_range_cnt);
+ } else {
+ g_snprintf(label_text, sizeof(label_text), "%u", range->displayed_user_range_cnt);
+ }
+ gtk_label_set_text(GTK_LABEL(g_object_get_data(G_OBJECT(data), RANGE_SELECT_USER_D_KEY)), label_text);
+
+ switch(range->process) {
+ case(range_process_all):
+ ignored_cnt = range->ignored_cnt;
+ displayed_ignored_cnt = range->displayed_ignored_cnt;
+ break;
+ case(range_process_selected):
+ ignored_cnt = (can_select && cfile.current_frame->flags.ignored) ? 1 : 0;
+ displayed_ignored_cnt = ignored_cnt;
+ break;
+ case(range_process_marked):
+ ignored_cnt = range->ignored_marked_cnt;
+ displayed_ignored_cnt = range->displayed_ignored_marked_cnt;
+ break;
+ case(range_process_marked_range):
+ ignored_cnt = range->ignored_mark_range_cnt;
+ displayed_ignored_cnt = range->displayed_ignored_mark_range_cnt;
+ break;
+ case(range_process_user_range):
+ ignored_cnt = range->ignored_user_range_cnt;
+ displayed_ignored_cnt = range->displayed_ignored_user_range_cnt;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ if (filtered_active)
+ selected_packets = (displayed_ignored_cnt != 0);
+ else
+ selected_packets = (ignored_cnt != 0);
+
+ if (selected_packets) {
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_REMOVE_IGNORED_KEY), TRUE);
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_IGNORED_C_KEY), !filtered_active);
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_IGNORED_D_KEY), filtered_active);
+ } else {
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_REMOVE_IGNORED_KEY), FALSE);
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_IGNORED_C_KEY), FALSE);
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_IGNORED_D_KEY), FALSE);
+ }
+
+ g_snprintf(label_text, sizeof(label_text), "%u", ignored_cnt);
+ gtk_label_set_text(GTK_LABEL(g_object_get_data(G_OBJECT(data), RANGE_IGNORED_C_KEY)), label_text); g_snprintf(label_text, sizeof(label_text), "%u", displayed_ignored_cnt);
+ gtk_label_set_text(GTK_LABEL(g_object_get_data(G_OBJECT(data), RANGE_IGNORED_D_KEY)), label_text);
+}
+
+
+static void
+toggle_captured_cb(GtkWidget *widget, gpointer data)
+{
+ GtkWidget *bt;
+ packet_range_t *range;
+
+
+ range = g_object_get_data(G_OBJECT(data), RANGE_VALUES_KEY);
+
+ /* is the button now active? */
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget))) {
+ /* They changed the state of the "captured" button. */
+ range->process_filtered = FALSE;
+
+ bt = g_object_get_data(G_OBJECT(data), RANGE_CAPTURED_BT_KEY);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bt), TRUE);
+ bt = g_object_get_data(G_OBJECT(data), RANGE_DISPLAYED_BT_KEY);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bt), FALSE);
+
+ range_update_dynamics(data);
+ }
+}
+
+static void
+toggle_filtered_cb(GtkWidget *widget, gpointer data)
+{
+ GtkWidget *bt;
+ packet_range_t *range;
+
+
+ range = g_object_get_data(G_OBJECT(data), RANGE_VALUES_KEY);
+
+ /* is the button now active? */
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget))) {
+ range->process_filtered = TRUE;
+ bt = g_object_get_data(G_OBJECT(data), RANGE_CAPTURED_BT_KEY);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bt), FALSE);
+ bt = g_object_get_data(G_OBJECT(data), RANGE_DISPLAYED_BT_KEY);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(bt), TRUE);
+
+ range_update_dynamics(data);
+ }
+}
+
+static void
+toggle_select_all(GtkWidget *widget, gpointer data)
+{
+ packet_range_t *range;
+
+
+ range = g_object_get_data(G_OBJECT(data), RANGE_VALUES_KEY);
+
+ /* is the button now active? */
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget))) {
+ range->process = range_process_all;
+ range_update_dynamics(data);
+ }
+}
+
+static void
+toggle_select_selected(GtkWidget *widget, gpointer data)
+{
+ packet_range_t *range;
+
+
+ range = g_object_get_data(G_OBJECT(data), RANGE_VALUES_KEY);
+
+ /* is the button now active? */
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget))) {
+ range->process = range_process_selected;
+ range_update_dynamics(data);
+ }
+}
+
+static void
+toggle_select_marked_only(GtkWidget *widget, gpointer data)
+{
+ packet_range_t *range;
+
+
+ range = g_object_get_data(G_OBJECT(data), RANGE_VALUES_KEY);
+
+ /* is the button now active? */
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget))) {
+ range->process = range_process_marked;
+ range_update_dynamics(data);
+ }
+}
+
+static void
+toggle_select_marked_range(GtkWidget *widget, gpointer data)
+{
+ packet_range_t *range;
+
+
+ range = g_object_get_data(G_OBJECT(data), RANGE_VALUES_KEY);
+
+ /* is the button now active? */
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget))) {
+ range->process = range_process_marked_range;
+ range_update_dynamics(data);
+ }
+}
+
+static void
+toggle_select_user_range(GtkWidget *widget, gpointer data)
+{
+ packet_range_t *range;
+
+
+ range = g_object_get_data(G_OBJECT(data), RANGE_VALUES_KEY);
+
+ /* is the button now active? */
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget))) {
+ range->process = range_process_user_range;
+ range_update_dynamics(data);
+ }
+
+ /* Make the entry widget sensitive or insensitive */
+ gtk_widget_set_sensitive(g_object_get_data(G_OBJECT(data), RANGE_SELECT_USER_ENTRY_KEY), range->process == range_process_user_range);
+
+ /* When selecting user specified range, then focus on the entry */
+ if (range->process == range_process_user_range)
+ gtk_widget_grab_focus(g_object_get_data(G_OBJECT(data), RANGE_SELECT_USER_ENTRY_KEY));
+
+}
+
+static void
+toggle_remove_ignored(GtkWidget *widget, gpointer data)
+{
+ packet_range_t *range;
+
+ range = g_object_get_data(G_OBJECT(data), RANGE_VALUES_KEY);
+
+ /* is the button now active? */
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget))) {
+ range->remove_ignored = TRUE;
+ } else {
+ range->remove_ignored = FALSE;
+ }
+
+ range_update_dynamics(data);
+}
+
+static void
+range_entry(GtkWidget *widget _U_, gpointer data)
+{
+ const gchar *entry_text;
+ GtkWidget *entry;
+ packet_range_t *range;
+
+
+ range = g_object_get_data(G_OBJECT(data), RANGE_VALUES_KEY);
+ entry = g_object_get_data(G_OBJECT(data), RANGE_SELECT_USER_ENTRY_KEY);
+
+ gtk_toggle_button_set_active(g_object_get_data(G_OBJECT(data), RANGE_SELECT_USER_KEY), TRUE);
+ entry_text = gtk_entry_get_text (GTK_ENTRY (entry));
+ packet_range_convert_str(range, entry_text);
+ range_update_dynamics(data);
+}
+
+
+static void
+range_entry_in_event(GtkWidget *widget _U_, GdkEventFocus *event _U_, gpointer user_data _U_)
+{
+ /* This event is called, if the "enter" key is pressed while the key focus (right name?) */
+ /* is in the range entry field. */
+
+ /* Calling range_entry() isn't necessary as all changes are already done while the */
+ /* entry was edited. Calling it here will cause a NULL pointer exception, */
+ /* so don't do: <range_entry(widget, user_data); as we did before. */
+
+ /* What we could do here is to cause the "hosting" dialog box do whatever it */
+ /* needs to do when the default button was pressed. This is difficult as we currently */
+ /* don't have a concept to call the hosting dialog this way. */
+
+ /* XXX - As we might want to put the whole range thing in it's own dialog, this would be */
+ /* a much easier task than it would be today as we could simply close our own dialog. */
+}
+
+
+/* create a new range "widget" */
+GtkWidget *range_new(packet_range_t *range)
+{
+ GtkWidget *range_tb;
+ GtkWidget *captured_bt;
+ GtkWidget *displayed_bt;
+
+ GtkWidget *select_all_rb;
+ GtkWidget *select_all_c_lb;
+ GtkWidget *select_all_d_lb;
+ GtkWidget *select_curr_rb;
+ GtkWidget *select_curr_c_lb;
+ GtkWidget *select_curr_d_lb;
+ GtkWidget *select_marked_only_rb;
+ GtkWidget *select_marked_only_c_lb;
+ GtkWidget *select_marked_only_d_lb;
+ GtkWidget *select_marked_range_rb;
+ GtkWidget *select_marked_range_c_lb;
+ GtkWidget *select_marked_range_d_lb;
+ GtkWidget *select_user_range_rb;
+ GtkWidget *select_user_range_c_lb;
+ GtkWidget *select_user_range_d_lb;
+ GtkWidget *select_user_range_entry;
+ GtkWidget *remove_ignored_cb;
+ GtkWidget *ignored_c_lb;
+ GtkWidget *ignored_d_lb;
+
+ /* range table */
+ range_tb = gtk_table_new(7, 3, FALSE);
+ gtk_container_set_border_width(GTK_CONTAINER(range_tb), 5);
+
+ /* captured button */
+ captured_bt = gtk_toggle_button_new_with_mnemonic("_Captured");
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), captured_bt, 1, 2, 0, 1);
+ g_signal_connect(captured_bt, "toggled", G_CALLBACK(toggle_captured_cb), range_tb);
+ gtk_widget_set_tooltip_text (captured_bt,("Process all the below chosen packets"));
+
+ /* displayed button */
+ displayed_bt = gtk_toggle_button_new_with_mnemonic("_Displayed");
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), displayed_bt, 2, 3, 0, 1);
+ g_signal_connect(displayed_bt, "toggled", G_CALLBACK(toggle_filtered_cb), range_tb);
+ gtk_widget_set_tooltip_text (displayed_bt,("Process only the below chosen packets, which also passes the current display filter"));
+
+
+ /* Process all packets */
+ select_all_rb = gtk_radio_button_new_with_mnemonic_from_widget(NULL, "_All packets");
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), select_all_rb, 0, 1, 1, 2);
+ gtk_widget_set_tooltip_text (select_all_rb, ("Process all packets"));
+ g_signal_connect(select_all_rb, "toggled", G_CALLBACK(toggle_select_all), range_tb);
+
+ select_all_c_lb = gtk_label_new("?");
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), select_all_c_lb, 1, 2, 1, 2);
+ select_all_d_lb = gtk_label_new("?");
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), select_all_d_lb, 2, 3, 1, 2);
+
+
+ /* Process currently selected */
+ select_curr_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(select_all_rb), "_Selected packet only");
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), select_curr_rb, 0, 1, 2, 3);
+ gtk_widget_set_tooltip_text (select_curr_rb, ("Process the currently selected packet only"));
+ g_signal_connect(select_curr_rb, "toggled", G_CALLBACK(toggle_select_selected), range_tb);
+
+ select_curr_c_lb = gtk_label_new("?");
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), select_curr_c_lb, 1, 2, 2, 3);
+ select_curr_d_lb = gtk_label_new("?");
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), select_curr_d_lb, 2, 3, 2, 3);
+
+
+ /* Process marked packets */
+ select_marked_only_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(select_all_rb), "_Marked packets only");
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), select_marked_only_rb, 0, 1, 3, 4);
+ gtk_widget_set_tooltip_text (select_marked_only_rb, ("Process marked packets only"));
+ g_signal_connect(select_marked_only_rb, "toggled", G_CALLBACK(toggle_select_marked_only), range_tb);
+
+ select_marked_only_c_lb = gtk_label_new("?");
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), select_marked_only_c_lb, 1, 2, 3, 4);
+ select_marked_only_d_lb = gtk_label_new("?");
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), select_marked_only_d_lb, 2, 3, 3, 4);
+
+
+ /* Process packet range between first and last packet */
+ select_marked_range_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(select_all_rb), "From first _to last marked packet");
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), select_marked_range_rb, 0, 1, 4, 5);
+ gtk_widget_set_tooltip_text (select_marked_range_rb,("Process all packets between the first and last marker"));
+ g_signal_connect(select_marked_range_rb, "toggled", G_CALLBACK(toggle_select_marked_range), range_tb);
+
+ select_marked_range_c_lb = gtk_label_new("?");
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), select_marked_range_c_lb, 1, 2, 4, 5);
+ select_marked_range_d_lb = gtk_label_new("?");
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), select_marked_range_d_lb, 2, 3, 4, 5);
+
+
+ /* Process a user specified provided packet range : -10,30,40-70,80- */
+ select_user_range_rb = gtk_radio_button_new_with_mnemonic_from_widget(GTK_RADIO_BUTTON(select_all_rb), "Specify a packet _range:");
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), select_user_range_rb, 0, 1, 5, 6);
+ gtk_widget_set_tooltip_text (select_user_range_rb,("Process a specified packet range"));
+ g_signal_connect(select_user_range_rb, "toggled", G_CALLBACK(toggle_select_user_range), range_tb);
+
+ select_user_range_c_lb = gtk_label_new("?");
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), select_user_range_c_lb, 1, 2, 5, 6);
+ select_user_range_d_lb = gtk_label_new("?");
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), select_user_range_d_lb, 2, 3, 5, 6);
+
+
+ /* The entry part */
+ select_user_range_entry = gtk_entry_new();
+ gtk_entry_set_max_length (GTK_ENTRY (select_user_range_entry), 254);
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), select_user_range_entry, 0, 1, 6, 7);
+ gtk_widget_set_tooltip_text (select_user_range_entry, ("Specify a range of packet numbers : \nExample : 1-10,18,25-100,332-"));
+ g_signal_connect(select_user_range_entry,"changed", G_CALLBACK(range_entry), range_tb);
+ g_signal_connect(select_user_range_entry,"activate", G_CALLBACK(range_entry_in_event), range_tb);
+
+ /* Remove ignored packets */
+ remove_ignored_cb = gtk_check_button_new_with_mnemonic("Remove _Ignored packets");
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), remove_ignored_cb, 0, 1, 7, 8);
+ gtk_widget_set_tooltip_text (remove_ignored_cb,("Remove all packets marked as Ignored"));
+ g_signal_connect(remove_ignored_cb, "toggled", G_CALLBACK(toggle_remove_ignored), range_tb);
+
+ ignored_c_lb = gtk_label_new("?");
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), ignored_c_lb, 1, 2, 7, 8);
+ ignored_d_lb = gtk_label_new("?");
+ gtk_table_attach_defaults(GTK_TABLE(range_tb), ignored_d_lb, 2, 3, 7, 8);
+
+ gtk_widget_show_all(range_tb);
+
+
+ g_object_set_data(G_OBJECT(range_tb), RANGE_VALUES_KEY, range);
+ g_object_set_data(G_OBJECT(range_tb), RANGE_CAPTURED_BT_KEY, captured_bt);
+ g_object_set_data(G_OBJECT(range_tb), RANGE_DISPLAYED_BT_KEY, displayed_bt);
+
+ g_object_set_data(G_OBJECT(range_tb), RANGE_SELECT_ALL_KEY, select_all_rb);
+ g_object_set_data(G_OBJECT(range_tb), RANGE_SELECT_ALL_C_KEY, select_all_c_lb);
+ g_object_set_data(G_OBJECT(range_tb), RANGE_SELECT_ALL_D_KEY, select_all_d_lb);
+
+ g_object_set_data(G_OBJECT(range_tb), RANGE_SELECT_CURR_KEY, select_curr_rb);
+ g_object_set_data(G_OBJECT(range_tb), RANGE_SELECT_CURR_C_KEY, select_curr_c_lb);
+ g_object_set_data(G_OBJECT(range_tb), RANGE_SELECT_CURR_D_KEY, select_curr_d_lb);
+ g_object_set_data(G_OBJECT(range_tb), RANGE_SELECT_CURR_D_KEY, select_curr_d_lb);
+ g_object_set_data(G_OBJECT(range_tb), RANGE_SELECT_MARKED_KEY, select_marked_only_rb);
+ g_object_set_data(G_OBJECT(range_tb), RANGE_SELECT_MARKED_C_KEY, select_marked_only_c_lb);
+ g_object_set_data(G_OBJECT(range_tb), RANGE_SELECT_MARKED_D_KEY, select_marked_only_d_lb);
+ g_object_set_data(G_OBJECT(range_tb), RANGE_SELECT_MARKED_RANGE_KEY, select_marked_range_rb);
+ g_object_set_data(G_OBJECT(range_tb), RANGE_SELECT_MARKED_RANGE_C_KEY,select_marked_range_c_lb);
+ g_object_set_data(G_OBJECT(range_tb), RANGE_SELECT_MARKED_RANGE_D_KEY,select_marked_range_d_lb);
+ g_object_set_data(G_OBJECT(range_tb), RANGE_SELECT_USER_KEY, select_user_range_rb);
+ g_object_set_data(G_OBJECT(range_tb), RANGE_SELECT_USER_C_KEY, select_user_range_c_lb);
+ g_object_set_data(G_OBJECT(range_tb), RANGE_SELECT_USER_D_KEY, select_user_range_d_lb);
+ g_object_set_data(G_OBJECT(range_tb), RANGE_SELECT_USER_ENTRY_KEY, select_user_range_entry);
+
+ g_object_set_data(G_OBJECT(range_tb), RANGE_REMOVE_IGNORED_KEY, remove_ignored_cb);
+ g_object_set_data(G_OBJECT(range_tb), RANGE_IGNORED_C_KEY, ignored_c_lb);
+ g_object_set_data(G_OBJECT(range_tb), RANGE_IGNORED_D_KEY, ignored_d_lb);
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(captured_bt), !range->process_filtered);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(displayed_bt), range->process_filtered);
+
+ switch(range->process) {
+ case(range_process_all):
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_all_rb), TRUE);
+ break;
+ case(range_process_selected):
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_curr_rb), TRUE);
+ break;
+ case(range_process_marked):
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_marked_only_rb), TRUE);
+ break;
+ case(range_process_marked_range):
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_marked_range_rb), TRUE);
+ break;
+ case(range_process_user_range):
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(select_user_range_rb), TRUE);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ return range_tb;
+}
diff --git a/ui/gtk/range_utils.h b/ui/gtk/range_utils.h
new file mode 100644
index 0000000000..6842ea199b
--- /dev/null
+++ b/ui/gtk/range_utils.h
@@ -0,0 +1,55 @@
+/* range_utils.h
+ * Declarations of utilities to with range_utils.c (packet range dialog)
+ *
+ * $Id$
+ *
+ * Ulf Lamping <ulf.lamping@web.de>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __RANGE_UTILS_H__
+#define __RANGE_UTILS_H__
+
+/** @file
+ * Packet range widget used for example in the "Save" and "Print" dialogs.
+ */
+
+/** Check the validity of a packet_range_t, and put up an alert box if
+ ** it's not valid.
+ *
+ * @param range the range to check
+ * @return a Boolean that's TRUE if it's valid and FALSE if it isn't
+ */
+extern gboolean range_check_validity(packet_range_t *range);
+
+/** Create a new range widget.
+ *
+ * @param range the range to set
+ * @return the new range widget
+ */
+extern GtkWidget *range_new(packet_range_t *range);
+
+/* Update all "dynamic" range things.
+ *
+ * @param data range widget
+ */
+extern void range_update_dynamics(gpointer data);
+
+#endif
diff --git a/ui/gtk/recent.c b/ui/gtk/recent.c
new file mode 100644
index 0000000000..0a891df25d
--- /dev/null
+++ b/ui/gtk/recent.c
@@ -0,0 +1,1105 @@
+/* recent.c
+ * Recent "preference" handling routines
+ * Copyright 2004, Ulf Lamping <ulf.lamping@web.de>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifndef QT_GUI_LIB
+#include <gtk/gtk.h>
+#endif
+
+#include <epan/epan.h>
+#include <epan/filesystem.h>
+#include <epan/emem.h>
+#include <epan/prefs.h>
+#include <epan/prefs-int.h>
+#include <epan/column.h>
+
+#include "../simple_dialog.h"
+#include "../u3.h"
+#include <wsutil/file_util.h>
+
+#include "ui/gtk/recent.h"
+#ifndef QT_GUI_LIB
+#include "ui/gtk/main.h"
+#include "ui/gtk/menus.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/new_packet_list.h"
+#include "ui/gtk/file_dlg.h"
+#include "ui/gtk/cfilter_combo_utils.h"
+
+#ifdef HAVE_PCAP_REMOTE
+#include "ui/gtk/capture_dlg.h"
+#endif
+#else /* QT_GUI_LIB */
+#include "qt_ui_utils.h"
+#include "../file.h"
+#endif /* QT_GUI_LIB */
+
+#define RECENT_KEY_MAIN_TOOLBAR_SHOW "gui.toolbar_main_show"
+#define RECENT_KEY_FILTER_TOOLBAR_SHOW "gui.filter_toolbar_show"
+#define RECENT_KEY_AIRPCAP_TOOLBAR_SHOW "gui.airpcap_toolbar_show"
+#define RECENT_KEY_DRIVER_CHECK_SHOW "gui.airpcap_driver_check_show"
+#define RECENT_KEY_PACKET_LIST_SHOW "gui.packet_list_show"
+#define RECENT_KEY_TREE_VIEW_SHOW "gui.tree_view_show"
+#define RECENT_KEY_BYTE_VIEW_SHOW "gui.byte_view_show"
+#define RECENT_KEY_STATUSBAR_SHOW "gui.statusbar_show"
+#define RECENT_KEY_PACKET_LIST_COLORIZE "gui.packet_list_colorize"
+#define RECENT_GUI_TIME_FORMAT "gui.time_format"
+#define RECENT_GUI_TIME_PRECISION "gui.time_precision"
+#define RECENT_GUI_SECONDS_FORMAT "gui.seconds_format"
+#define RECENT_GUI_ZOOM_LEVEL "gui.zoom_level"
+#define RECENT_GUI_BYTES_VIEW "gui.bytes_view"
+#define RECENT_GUI_GEOMETRY_MAIN_X "gui.geometry_main_x"
+#define RECENT_GUI_GEOMETRY_MAIN_Y "gui.geometry_main_y"
+#define RECENT_GUI_GEOMETRY_MAIN_WIDTH "gui.geometry_main_width"
+#define RECENT_GUI_GEOMETRY_MAIN_HEIGHT "gui.geometry_main_height"
+#define RECENT_GUI_GEOMETRY_MAIN_MAXIMIZED "gui.geometry_main_maximized"
+#define RECENT_GUI_GEOMETRY_MAIN_UPPER_PANE "gui.geometry_main_upper_pane"
+#define RECENT_GUI_GEOMETRY_MAIN_LOWER_PANE "gui.geometry_main_lower_pane"
+#define RECENT_GUI_GEOMETRY_STATUS_PANE_LEFT "gui.geometry_status_pane"
+#define RECENT_GUI_GEOMETRY_STATUS_PANE_RIGHT "gui.geometry_status_pane_right"
+#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_GEOMETRY "gui.geom."
+#define RECENT_KEY_PRIVS_WARN_IF_ELEVATED "privs.warn_if_elevated"
+#define RECENT_KEY_PRIVS_WARN_IF_NO_NPF "privs.warn_if_no_npf"
+
+#define RECENT_FILE_NAME "recent"
+#define RECENT_COMMON_FILE_NAME "recent_common"
+
+recent_settings_t recent;
+
+static const char *ts_type_text[] =
+ { "RELATIVE", "ABSOLUTE", "ABSOLUTE_WITH_DATE", "DELTA", "DELTA_DIS", "EPOCH", "UTC", "UTC_WITH_DATE", NULL };
+
+static const char *ts_precision_text[] =
+ { "AUTO", "SEC", "DSEC", "CSEC", "MSEC", "USEC", "NSEC", NULL };
+
+static const char *ts_seconds_text[] =
+ { "SECONDS", "HOUR_MIN_SEC", NULL };
+
+/* Takes an string and a pointer to an array of strings, and a default int value.
+ * The array must be terminated by a NULL string. If the string is found in the array
+ * of strings, the index of that string in the array is returned. Otherwise, the
+ * default value that was passed as the third argument is returned.
+ */
+static int
+find_index_from_string_array(const char *needle, const char **haystack, int default_value)
+{
+ int i = 0;
+
+ while (haystack[i] != NULL) {
+ if (strcmp(needle, haystack[i]) == 0) {
+ return i;
+ }
+ i++;
+ }
+ return default_value;
+}
+
+static void
+free_col_width_info(recent_settings_t *rs)
+{
+ col_width_data *cfmt;
+
+ while (rs->col_width_list != NULL) {
+ cfmt = rs->col_width_list->data;
+ g_free(cfmt->cfield);
+ g_free(cfmt);
+ rs->col_width_list = g_list_remove_link(rs->col_width_list, rs->col_width_list);
+ }
+ g_list_free(rs->col_width_list);
+ rs->col_width_list = NULL;
+}
+
+/* Attempt to Write out "recent common" to the user's recent common file.
+ If we got an error report it with a dialog box and return FALSE,
+ otherwise return TRUE. */
+gboolean
+write_recent(void)
+{
+ char *pf_dir_path;
+ char *rf_path;
+ FILE *rf;
+
+ /* To do:
+ * - Split output lines longer than MAX_VAL_LEN
+ * - Create a function for the preference directory check/creation
+ * so that duplication can be avoided with filter.c
+ */
+
+ /* Create the directory that holds personal configuration files, if
+ necessary. */
+ if (create_persconffile_dir(&pf_dir_path) == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't create directory\n\"%s\"\nfor recent file: %s.", pf_dir_path,
+ g_strerror(errno));
+ g_free(pf_dir_path);
+ return FALSE;
+ }
+
+ rf_path = get_persconffile_path(RECENT_COMMON_FILE_NAME, FALSE, TRUE);
+ if ((rf = ws_fopen(rf_path, "w")) == NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't open recent file\n\"%s\": %s.", rf_path,
+ g_strerror(errno));
+ g_free(rf_path);
+ return FALSE;
+ }
+ g_free(rf_path);
+
+ fputs("# Recent settings file for Wireshark " VERSION ".\n"
+ "#\n"
+ "# This file is regenerated each time Wireshark is quit.\n"
+ "# So be careful, if you want to make manual changes here.\n"
+ "\n"
+ "######## Recent capture files (latest last), cannot be altered through command line ########\n"
+ "\n", rf);
+
+ menu_recent_file_write_all(rf);
+
+ fputs("\n"
+ "######## Recent capture filters (latest last), cannot be altered through command line ########\n"
+ "\n", rf);
+
+ cfilter_combo_recent_write_all(rf);
+
+ fputs("\n"
+ "######## Recent display filters (latest last), cannot be altered through command line ########\n"
+ "\n", rf);
+
+ dfilter_recent_combo_write_all(rf);
+
+#ifdef HAVE_PCAP_REMOTE
+ fputs("\n"
+ "######## Recent remote hosts, cannot be altered through command line ########\n"
+ "\n", rf);
+
+ capture_remote_combo_recent_write_all(rf);
+#endif
+
+ fprintf(rf, "\n# Main window geometry.\n");
+ fprintf(rf, "# Decimal numbers.\n");
+ fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_X ": %d\n", recent.gui_geometry_main_x);
+ fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_Y ": %d\n", recent.gui_geometry_main_y);
+ fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_WIDTH ": %d\n",
+ recent.gui_geometry_main_width);
+ fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_HEIGHT ": %d\n",
+ recent.gui_geometry_main_height);
+
+ fprintf(rf, "\n# Main window maximized.\n");
+ fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
+ fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_MAXIMIZED ": %s\n",
+ recent.gui_geometry_main_maximized == TRUE ? "TRUE" : "FALSE");
+
+ fprintf(rf, "\n# Statusbar left pane size.\n");
+ fprintf(rf, "# Decimal number.\n");
+ if (recent.gui_geometry_status_pane_left != 0) {
+ fprintf(rf, RECENT_GUI_GEOMETRY_STATUS_PANE_LEFT ": %d\n",
+ recent.gui_geometry_status_pane_left);
+ }
+ fprintf(rf, "\n# Statusbar middle pane size.\n");
+ fprintf(rf, "# Decimal number.\n");
+ if (recent.gui_geometry_status_pane_right != 0) {
+ fprintf(rf, RECENT_GUI_GEOMETRY_STATUS_PANE_RIGHT ": %d\n",
+ recent.gui_geometry_status_pane_right);
+ }
+
+ fprintf(rf, "\n# Last used Configuration Profile.\n");
+ fprintf(rf, RECENT_LAST_USED_PROFILE ": %s\n", get_profile_name());
+
+ fprintf(rf, "\n# WLAN statistics upper pane size.\n");
+ fprintf(rf, "# Decimal number.\n");
+ fprintf(rf, RECENT_GUI_GEOMETRY_WLAN_STATS_PANE ": %d\n",
+ recent.gui_geometry_wlan_stats_pane);
+
+ fprintf(rf, "\n# Warn if running with elevated permissions (e.g. as root).\n");
+ fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
+ fprintf(rf, RECENT_KEY_PRIVS_WARN_IF_ELEVATED ": %s\n",
+ recent.privs_warn_if_elevated == TRUE ? "TRUE" : "FALSE");
+
+ fprintf(rf, "\n# Warn if npf.sys isn't loaded on Windows >= 6.0.\n");
+ fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
+ fprintf(rf, RECENT_KEY_PRIVS_WARN_IF_NO_NPF ": %s\n",
+ recent.privs_warn_if_no_npf == TRUE ? "TRUE" : "FALSE");
+
+ window_geom_recent_write_all(rf);
+
+ fclose(rf);
+
+ /* XXX - catch I/O errors (e.g. "ran out of disk space") and return
+ an error indication, or maybe write to a new recent file and
+ rename that file on top of the old one only if there are not I/O
+ errors. */
+ return TRUE;
+}
+
+
+/* Attempt to Write out profile "recent" to the user's profile recent file.
+ If we got an error report it with a dialog box and return FALSE,
+ otherwise return TRUE. */
+gboolean
+write_profile_recent(void)
+{
+ char *pf_dir_path;
+ char *rf_path;
+ FILE *rf;
+
+ /* To do:
+ * - Split output lines longer than MAX_VAL_LEN
+ * - Create a function for the preference directory check/creation
+ * so that duplication can be avoided with filter.c
+ */
+
+ /* Create the directory that holds personal configuration files, if
+ necessary. */
+ if (create_persconffile_dir(&pf_dir_path) == -1) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't create directory\n\"%s\"\nfor recent file: %s.", pf_dir_path,
+ g_strerror(errno));
+ g_free(pf_dir_path);
+ return FALSE;
+ }
+
+ rf_path = get_persconffile_path(RECENT_FILE_NAME, TRUE, TRUE);
+ if ((rf = ws_fopen(rf_path, "w")) == NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't open recent file\n\"%s\": %s.", rf_path,
+ g_strerror(errno));
+ g_free(rf_path);
+ return FALSE;
+ }
+ g_free(rf_path);
+
+ fputs("# Recent settings file for Wireshark " VERSION ".\n"
+ "#\n"
+ "# This file is regenerated each time Wireshark is quit\n"
+ "# and when changing configuration profile.\n"
+ "# So be careful, if you want to make manual changes here.\n"
+ "\n", rf);
+
+ fprintf(rf, "\n# Main Toolbar show (hide).\n");
+ fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
+ fprintf(rf, RECENT_KEY_MAIN_TOOLBAR_SHOW ": %s\n",
+ recent.main_toolbar_show == TRUE ? "TRUE" : "FALSE");
+
+ fprintf(rf, "\n# Filter Toolbar show (hide).\n");
+ fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
+ fprintf(rf, RECENT_KEY_FILTER_TOOLBAR_SHOW ": %s\n",
+ recent.filter_toolbar_show == TRUE ? "TRUE" : "FALSE");
+
+#ifdef HAVE_AIRPCAP
+ fprintf(rf, "\n# Wireless Settings Toolbar show (hide).\n");
+ fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
+ fprintf(rf, RECENT_KEY_AIRPCAP_TOOLBAR_SHOW ": %s\n",
+ recent.airpcap_toolbar_show == TRUE ? "TRUE" : "FALSE");
+#endif
+
+#ifdef HAVE_AIRPCAP
+ fprintf(rf, "\n# Show (hide) old AirPcap driver warning dialog box.\n");
+ fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
+ fprintf(rf, RECENT_KEY_DRIVER_CHECK_SHOW ": %s\n",
+ recent.airpcap_driver_check_show == TRUE ? "TRUE" : "FALSE");
+#endif
+
+ fprintf(rf, "\n# Packet list show (hide).\n");
+ fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
+ fprintf(rf, RECENT_KEY_PACKET_LIST_SHOW ": %s\n",
+ recent.packet_list_show == TRUE ? "TRUE" : "FALSE");
+
+ fprintf(rf, "\n# Tree view show (hide).\n");
+ fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
+ fprintf(rf, RECENT_KEY_TREE_VIEW_SHOW ": %s\n",
+ recent.tree_view_show == TRUE ? "TRUE" : "FALSE");
+
+ fprintf(rf, "\n# Byte view show (hide).\n");
+ fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
+ fprintf(rf, RECENT_KEY_BYTE_VIEW_SHOW ": %s\n",
+ recent.byte_view_show == TRUE ? "TRUE" : "FALSE");
+
+ fprintf(rf, "\n# Statusbar show (hide).\n");
+ fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
+ fprintf(rf, RECENT_KEY_STATUSBAR_SHOW ": %s\n",
+ recent.statusbar_show == TRUE ? "TRUE" : "FALSE");
+
+ fprintf(rf, "\n# Packet list colorize (hide).\n");
+ fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
+ fprintf(rf, RECENT_KEY_PACKET_LIST_COLORIZE ": %s\n",
+ recent.packet_list_colorize == TRUE ? "TRUE" : "FALSE");
+
+ fprintf(rf, "\n# Timestamp display format.\n");
+ fprintf(rf, "# One of: RELATIVE, ABSOLUTE, ABSOLUTE_WITH_DATE, DELTA, DELTA_DIS, EPOCH, UTC, UTC_WITH_DATE\n");
+ fprintf(rf, RECENT_GUI_TIME_FORMAT ": %s\n",
+ ts_type_text[recent.gui_time_format]);
+
+ fprintf(rf, "\n# Timestamp display precision.\n");
+ fprintf(rf, "# One of: AUTO, SEC, DSEC, CSEC, MSEC, USEC, NSEC\n");
+ fprintf(rf, RECENT_GUI_TIME_PRECISION ": %s\n",
+ ts_precision_text[recent.gui_time_precision]);
+
+ fprintf(rf, "\n# Seconds display format.\n");
+ fprintf(rf, "# One of: SECONDS, HOUR_MIN_SEC\n");
+ fprintf(rf, RECENT_GUI_SECONDS_FORMAT ": %s\n",
+ ts_seconds_text[recent.gui_seconds_format]);
+
+ fprintf(rf, "\n# Zoom level.\n");
+ fprintf(rf, "# A decimal number.\n");
+ fprintf(rf, RECENT_GUI_ZOOM_LEVEL ": %d\n",
+ recent.gui_zoom_level);
+
+ fprintf(rf, "\n# Bytes view.\n");
+ fprintf(rf, "# A decimal number.\n");
+ fprintf(rf, RECENT_GUI_BYTES_VIEW ": %d\n",
+ recent.gui_bytes_view);
+
+ fprintf(rf, "\n# Main window upper (or leftmost) pane size.\n");
+ fprintf(rf, "# Decimal number.\n");
+ if (recent.gui_geometry_main_upper_pane != 0) {
+ fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_UPPER_PANE ": %d\n",
+ recent.gui_geometry_main_upper_pane);
+ }
+ fprintf(rf, "\n# Main window middle pane size.\n");
+ fprintf(rf, "# Decimal number.\n");
+ if (recent.gui_geometry_main_lower_pane != 0) {
+ fprintf(rf, RECENT_GUI_GEOMETRY_MAIN_LOWER_PANE ": %d\n",
+ recent.gui_geometry_main_lower_pane);
+ }
+
+ fprintf(rf, "\n# Packet list column pixel widths.\n");
+ fprintf(rf, "# Each pair of strings consists of a column format and its pixel width.\n");
+ new_packet_list_recent_write_all(rf);
+
+ if (get_last_open_dir() != NULL) {
+ fprintf(rf, "\n# Last directory navigated to in File Open dialog.\n");
+
+ if(u3_active())
+ fprintf(rf, RECENT_GUI_FILEOPEN_REMEMBERED_DIR ": %s\n", u3_contract_device_path(get_last_open_dir()));
+ else
+ fprintf(rf, RECENT_GUI_FILEOPEN_REMEMBERED_DIR ": %s\n", get_last_open_dir());
+ }
+
+ fclose(rf);
+
+ /* XXX - catch I/O errors (e.g. "ran out of disk space") and return
+ an error indication, or maybe write to a new recent file and
+ rename that file on top of the old one only if there are not I/O
+ errors. */
+ return TRUE;
+}
+
+
+/* write the geometry values of a window to recent file */
+void
+write_recent_geom(gpointer key _U_, gpointer value, gpointer rf)
+{
+ window_geometry_t *geom = value;
+
+ fprintf(rf, "\n# Geometry and maximized state of %s window.\n", geom->key);
+ fprintf(rf, "# Decimal integers.\n");
+ fprintf(rf, RECENT_GUI_GEOMETRY "%s.x: %d\n", geom->key, geom->x);
+ fprintf(rf, RECENT_GUI_GEOMETRY "%s.y: %d\n", geom->key, geom->y);
+ fprintf(rf, RECENT_GUI_GEOMETRY "%s.width: %d\n", geom->key,
+ geom->width);
+ fprintf(rf, RECENT_GUI_GEOMETRY "%s.height: %d\n", geom->key,
+ geom->height);
+
+ fprintf(rf, "# TRUE or FALSE (case-insensitive).\n");
+ fprintf(rf, RECENT_GUI_GEOMETRY "%s.maximized: %s\n", geom->key,
+ geom->maximized == TRUE ? "TRUE" : "FALSE");
+
+}
+
+/* set one user's recent common file key/value pair */
+static prefs_set_pref_e
+read_set_recent_common_pair_static(gchar *key, gchar *value,
+ void *private_data _U_,
+ gboolean return_range_errors _U_)
+{
+ long num;
+ char *p;
+
+ if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_MAXIMIZED) == 0) {
+ if (g_ascii_strcasecmp(value, "true") == 0) {
+ recent.gui_geometry_main_maximized = TRUE;
+ }
+ else {
+ recent.gui_geometry_main_maximized = FALSE;
+ }
+
+ } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_X) == 0) {
+ num = strtol(value, &p, 0);
+ if (p == value || *p != '\0')
+ return PREFS_SET_SYNTAX_ERR; /* number was bad */
+ recent.gui_geometry_main_x = num;
+ } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_Y) == 0) {
+ num = strtol(value, &p, 0);
+ if (p == value || *p != '\0')
+ return PREFS_SET_SYNTAX_ERR; /* number was bad */
+ recent.gui_geometry_main_y = num;
+ } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_WIDTH) == 0) {
+ num = strtol(value, &p, 0);
+ if (p == value || *p != '\0')
+ return PREFS_SET_SYNTAX_ERR; /* number was bad */
+ if (num <= 0)
+ return PREFS_SET_SYNTAX_ERR; /* number must be positive */
+ recent.gui_geometry_main_width = num;
+ } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_HEIGHT) == 0) {
+ num = strtol(value, &p, 0);
+ if (p == value || *p != '\0')
+ return PREFS_SET_SYNTAX_ERR; /* number was bad */
+ if (num <= 0)
+ return PREFS_SET_SYNTAX_ERR; /* number must be positive */
+ recent.gui_geometry_main_height = num;
+ } else if (strcmp(key, RECENT_GUI_GEOMETRY_STATUS_PANE_RIGHT) == 0) {
+ num = strtol(value, &p, 0);
+ if (p == value || *p != '\0')
+ return PREFS_SET_SYNTAX_ERR; /* number was bad */
+ if (num <= 0)
+ return PREFS_SET_SYNTAX_ERR; /* number must be positive */
+ recent.gui_geometry_status_pane_right = num;
+ recent.has_gui_geometry_status_pane = TRUE;
+ } else if (strcmp(key, RECENT_GUI_GEOMETRY_STATUS_PANE_LEFT) == 0) {
+ num = strtol(value, &p, 0);
+ if (p == value || *p != '\0')
+ return PREFS_SET_SYNTAX_ERR; /* number was bad */
+ if (num <= 0)
+ return PREFS_SET_SYNTAX_ERR; /* number must be positive */
+ recent.gui_geometry_status_pane_left = num;
+ recent.has_gui_geometry_status_pane = TRUE;
+ } else if (strcmp(key, RECENT_LAST_USED_PROFILE) == 0) {
+ if ((strcmp(value, DEFAULT_PROFILE) != 0) && profile_exists (value, FALSE)) {
+ set_profile_name (value);
+ }
+ } else if (strcmp(key, RECENT_GUI_GEOMETRY_WLAN_STATS_PANE) == 0) {
+ num = strtol(value, &p, 0);
+ if (p == value || *p != '\0')
+ return PREFS_SET_SYNTAX_ERR; /* number was bad */
+ if (num <= 0)
+ return PREFS_SET_SYNTAX_ERR; /* number must be positive */
+ recent.gui_geometry_wlan_stats_pane = num;
+ } else if (strncmp(key, RECENT_GUI_GEOMETRY, sizeof(RECENT_GUI_GEOMETRY)-1) == 0) {
+ /* now have something like "gui.geom.main.x", split it into win and sub_key */
+ char *win = &key[sizeof(RECENT_GUI_GEOMETRY)-1];
+ char *sub_key = strchr(win, '.');
+ if(sub_key) {
+ *sub_key = '\0';
+ sub_key++;
+ window_geom_recent_read_pair(win, sub_key, value);
+ }
+ } else if (strcmp(key, RECENT_KEY_PRIVS_WARN_IF_ELEVATED) == 0) {
+ if (g_ascii_strcasecmp(value, "true") == 0) {
+ recent.privs_warn_if_elevated = TRUE;
+ }
+ else {
+ recent.privs_warn_if_elevated = FALSE;
+ }
+ } else if (strcmp(key, RECENT_KEY_PRIVS_WARN_IF_NO_NPF) == 0) {
+ if (g_ascii_strcasecmp(value, "true") == 0) {
+ recent.privs_warn_if_no_npf = TRUE;
+ }
+ else {
+ recent.privs_warn_if_no_npf = FALSE;
+ }
+ }
+
+ return PREFS_SET_OK;
+}
+
+/* set one user's recent file key/value pair */
+static prefs_set_pref_e
+read_set_recent_pair_static(gchar *key, gchar *value, void *private_data _U_,
+ gboolean return_range_errors _U_)
+{
+ long num;
+ char *p;
+ GList *col_l, *col_l_elt;
+ col_width_data *cfmt;
+ const gchar *cust_format = col_format_to_string(COL_CUSTOM);
+ int cust_format_len = (int) strlen(cust_format);
+
+ if (strcmp(key, RECENT_KEY_MAIN_TOOLBAR_SHOW) == 0) {
+ if (g_ascii_strcasecmp(value, "true") == 0) {
+ recent.main_toolbar_show = TRUE;
+ }
+ else {
+ recent.main_toolbar_show = FALSE;
+ }
+ } else if (strcmp(key, RECENT_KEY_FILTER_TOOLBAR_SHOW) == 0) {
+ if (g_ascii_strcasecmp(value, "true") == 0) {
+ recent.filter_toolbar_show = TRUE;
+ }
+ else {
+ recent.filter_toolbar_show = FALSE;
+ }
+ } else if (strcmp(key, RECENT_KEY_AIRPCAP_TOOLBAR_SHOW) == 0) {
+ if (g_ascii_strcasecmp(value, "true") == 0) {
+ recent.airpcap_toolbar_show = TRUE;
+ }
+ else {
+ recent.airpcap_toolbar_show = FALSE;
+ }
+ } else if (strcmp(key, RECENT_KEY_DRIVER_CHECK_SHOW) == 0) {
+ if (g_ascii_strcasecmp(value, "true") == 0) {
+ recent.airpcap_driver_check_show = TRUE;
+ }
+ else {
+ recent.airpcap_driver_check_show = FALSE;
+ }
+ } else if (strcmp(key, RECENT_KEY_PACKET_LIST_SHOW) == 0) {
+ if (g_ascii_strcasecmp(value, "true") == 0) {
+ recent.packet_list_show = TRUE;
+ }
+ else {
+ recent.packet_list_show = FALSE;
+ }
+ } else if (strcmp(key, RECENT_KEY_TREE_VIEW_SHOW) == 0) {
+ if (g_ascii_strcasecmp(value, "true") == 0) {
+ recent.tree_view_show = TRUE;
+ }
+ else {
+ recent.tree_view_show = FALSE;
+ }
+ } else if (strcmp(key, RECENT_KEY_BYTE_VIEW_SHOW) == 0) {
+ if (g_ascii_strcasecmp(value, "true") == 0) {
+ recent.byte_view_show = TRUE;
+ }
+ else {
+ recent.byte_view_show = FALSE;
+ }
+ } else if (strcmp(key, RECENT_KEY_STATUSBAR_SHOW) == 0) {
+ if (g_ascii_strcasecmp(value, "true") == 0) {
+ recent.statusbar_show = TRUE;
+ }
+ else {
+ recent.statusbar_show = FALSE;
+ }
+ } else if (strcmp(key, RECENT_KEY_PACKET_LIST_COLORIZE) == 0) {
+ if (g_ascii_strcasecmp(value, "true") == 0) {
+ recent.packet_list_colorize = TRUE;
+ }
+ else {
+ recent.packet_list_colorize = FALSE;
+ }
+ } else if (strcmp(key, RECENT_GUI_TIME_FORMAT) == 0) {
+ recent.gui_time_format =
+ find_index_from_string_array(value, ts_type_text, TS_RELATIVE);
+ } else if (strcmp(key, RECENT_GUI_TIME_PRECISION) == 0) {
+ recent.gui_time_precision =
+ find_index_from_string_array(value, ts_precision_text, TS_PREC_AUTO);
+ } else if (strcmp(key, RECENT_GUI_SECONDS_FORMAT) == 0) {
+ recent.gui_seconds_format =
+ find_index_from_string_array(value, ts_seconds_text, TS_SECONDS_DEFAULT);
+ } else if (strcmp(key, RECENT_GUI_ZOOM_LEVEL) == 0) {
+ num = strtol(value, &p, 0);
+ if (p == value || *p != '\0')
+ return PREFS_SET_SYNTAX_ERR; /* number was bad */
+ recent.gui_zoom_level = num;
+ } else if (strcmp(key, RECENT_GUI_BYTES_VIEW) == 0) {
+ num = strtol(value, &p, 0);
+ if (p == value || *p != '\0')
+ return PREFS_SET_SYNTAX_ERR; /* number was bad */
+ recent.gui_bytes_view = num;
+ } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_MAXIMIZED) == 0) {
+ if (g_ascii_strcasecmp(value, "true") == 0) {
+ recent.gui_geometry_main_maximized = TRUE;
+ }
+ else {
+ recent.gui_geometry_main_maximized = FALSE;
+ }
+
+ } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_UPPER_PANE) == 0) {
+ num = strtol(value, &p, 0);
+ if (p == value || *p != '\0')
+ return PREFS_SET_SYNTAX_ERR; /* number was bad */
+ if (num <= 0)
+ return PREFS_SET_SYNTAX_ERR; /* number must be positive */
+ recent.gui_geometry_main_upper_pane = num;
+ recent.has_gui_geometry_main_upper_pane = TRUE;
+ } else if (strcmp(key, RECENT_GUI_GEOMETRY_MAIN_LOWER_PANE) == 0) {
+ num = strtol(value, &p, 0);
+ if (p == value || *p != '\0')
+ return PREFS_SET_SYNTAX_ERR; /* number was bad */
+ if (num <= 0)
+ return PREFS_SET_SYNTAX_ERR; /* number must be positive */
+ recent.gui_geometry_main_lower_pane = num;
+ recent.has_gui_geometry_main_lower_pane = TRUE;
+ }
+ else if (strcmp(key, RECENT_KEY_COL_WIDTH) == 0) {
+ col_l = prefs_get_string_list(value);
+ if (col_l == NULL)
+ return PREFS_SET_SYNTAX_ERR;
+ if ((g_list_length(col_l) % 2) != 0) {
+ /* A title didn't have a matching width. */
+ prefs_clear_string_list(col_l);
+ return PREFS_SET_SYNTAX_ERR;
+ }
+ /* Check to make sure all column formats are valid. */
+ col_l_elt = g_list_first(col_l);
+ while(col_l_elt) {
+ /* Make sure the format isn't empty. */
+ if (strcmp(col_l_elt->data, "") == 0) {
+ /* It is. */
+ prefs_clear_string_list(col_l);
+ return PREFS_SET_SYNTAX_ERR;
+ }
+
+ /* Check the format. */
+ if (strncmp(col_l_elt->data, cust_format, cust_format_len) != 0) {
+ if (get_column_format_from_str(col_l_elt->data) == -1) {
+ /* It's not a valid column format. */
+ prefs_clear_string_list(col_l);
+ return PREFS_SET_SYNTAX_ERR;
+ }
+ }
+
+ /* Go past the format. */
+ col_l_elt = col_l_elt->next;
+
+ /* Go past the width. */
+ col_l_elt = col_l_elt->next;
+ }
+ free_col_width_info(&recent);
+ recent.col_width_list = NULL;
+ col_l_elt = g_list_first(col_l);
+ while(col_l_elt) {
+ gchar *fmt = g_strdup(col_l_elt->data);
+ cfmt = (col_width_data *) g_malloc(sizeof(col_width_data));
+ if (strncmp(fmt, cust_format, cust_format_len) != 0) {
+ cfmt->cfmt = get_column_format_from_str(fmt);
+ cfmt->cfield = NULL;
+ } else {
+ cfmt->cfmt = COL_CUSTOM;
+ cfmt->cfield = g_strdup(&fmt[cust_format_len+1]); /* add 1 for ':' */
+ }
+ g_free (fmt);
+ if (cfmt->cfmt == -1) {
+ g_free(cfmt->cfield);
+ g_free(cfmt);
+ return PREFS_SET_SYNTAX_ERR; /* string was bad */
+ }
+
+ col_l_elt = col_l_elt->next;
+ cfmt->width = strtol(col_l_elt->data, &p, 0);
+ if (p == col_l_elt->data || (*p != '\0' && *p != ':')) {
+ g_free(cfmt->cfield);
+ g_free(cfmt);
+ return PREFS_SET_SYNTAX_ERR; /* number was bad */
+ }
+
+ if (*p == ':') {
+ cfmt->xalign = *(++p);
+ } else {
+ cfmt->xalign = COLUMN_XALIGN_DEFAULT;
+ }
+
+ col_l_elt = col_l_elt->next;
+ recent.col_width_list = g_list_append(recent.col_width_list, cfmt);
+ }
+ prefs_clear_string_list(col_l);
+ } else if (strcmp(key, RECENT_GUI_FILEOPEN_REMEMBERED_DIR) == 0) {
+ if (recent.gui_fileopen_remembered_dir) {
+ g_free (recent.gui_fileopen_remembered_dir);
+ }
+ recent.gui_fileopen_remembered_dir = g_strdup(value);
+ }
+
+ return PREFS_SET_OK;
+}
+
+
+/* set one user's recent file key/value pair */
+static prefs_set_pref_e
+read_set_recent_pair_dynamic(gchar *key, gchar *value, void *private_data _U_,
+ gboolean return_range_errors _U_)
+{
+ if (strcmp(key, RECENT_KEY_CAPTURE_FILE) == 0) {
+ if(u3_active())
+ add_menu_recent_capture_file(u3_expand_device_path(value));
+ else
+ add_menu_recent_capture_file(value);
+ } else if (strcmp(key, RECENT_KEY_DISPLAY_FILTER) == 0) {
+ dfilter_combo_add_recent(value);
+ } else if (strcmp(key, RECENT_KEY_CAPTURE_FILTER) == 0) {
+ cfilter_combo_add_recent(value);
+#ifdef HAVE_PCAP_REMOTE
+ } else if (strcmp(key, RECENT_KEY_REMOTE_HOST) == 0) {
+ capture_remote_combo_add_recent(value);
+#endif
+ }
+
+ return PREFS_SET_OK;
+}
+
+
+/*
+ * Given a string of the form "<recent name>:<recent value>", as might appear
+ * as an argument to a "-o" option, parse it and set the recent value in
+ * question. Return an indication of whether it succeeded or failed
+ * in some fashion.
+ */
+int
+recent_set_arg(char *prefarg)
+{
+ gchar *p, *colonp;
+ int ret;
+
+ colonp = strchr(prefarg, ':');
+ if (colonp == NULL)
+ return PREFS_SET_SYNTAX_ERR;
+
+ p = colonp;
+ *p++ = '\0';
+
+ /*
+ * Skip over any white space (there probably won't be any, but
+ * as we allow it in the preferences file, we might as well
+ * allow it here).
+ */
+ while (isspace((guchar)*p))
+ p++;
+ if (*p == '\0') {
+ /*
+ * Put the colon back, so if our caller uses, in an
+ * error message, the string they passed us, the message
+ * looks correct.
+ */
+ *colonp = ':';
+ return PREFS_SET_SYNTAX_ERR;
+ }
+
+ ret = read_set_recent_pair_static(prefarg, p, NULL, TRUE);
+ *colonp = ':'; /* put the colon back */
+ return ret;
+}
+
+
+/* opens the user's recent common file and read the first part */
+void
+recent_read_static(char **rf_path_return, int *rf_errno_return)
+{
+ char *rf_path;
+ FILE *rf;
+
+ /* set defaults */
+ recent.gui_geometry_main_x = 20;
+ recent.gui_geometry_main_y = 20;
+ recent.gui_geometry_main_width = DEF_WIDTH;
+ recent.gui_geometry_main_height = DEF_HEIGHT;
+ recent.gui_geometry_main_maximized= FALSE;
+
+ recent.gui_geometry_status_pane_left = (DEF_WIDTH/3);
+ recent.gui_geometry_status_pane_right = (DEF_WIDTH/3);
+ recent.gui_geometry_wlan_stats_pane = 200;
+
+ recent.privs_warn_if_elevated = TRUE;
+ recent.privs_warn_if_no_npf = TRUE;
+
+ recent.col_width_list = NULL;
+ recent.gui_fileopen_remembered_dir = NULL;
+
+ /* Construct the pathname of the user's recent common file. */
+ rf_path = get_persconffile_path(RECENT_COMMON_FILE_NAME, FALSE, FALSE);
+
+ /* Read the user's recent common file, if it exists. */
+ *rf_path_return = NULL;
+ if ((rf = ws_fopen(rf_path, "r")) != NULL) {
+ /* We succeeded in opening it; read it. */
+ read_prefs_file(rf_path, rf, read_set_recent_common_pair_static, NULL);
+
+ fclose(rf);
+ g_free(rf_path);
+ rf_path = NULL;
+ } else {
+ /* We failed to open it. If we failed for some reason other than
+ "it doesn't exist", return the errno and the pathname, so our
+ caller can report the error. */
+ if (errno != ENOENT) {
+ *rf_errno_return = errno;
+ *rf_path_return = rf_path;
+ }
+ }
+}
+
+
+
+/* opens the user's recent file and read the first part */
+void
+recent_read_profile_static(char **rf_path_return, int *rf_errno_return)
+{
+ char *rf_path, *rf_common_path;
+ FILE *rf;
+
+ /* set defaults */
+ recent.main_toolbar_show = TRUE;
+ recent.filter_toolbar_show = TRUE;
+ recent.airpcap_toolbar_show = FALSE;
+ recent.airpcap_driver_check_show = TRUE;
+ recent.packet_list_show = TRUE;
+ recent.tree_view_show = TRUE;
+ recent.byte_view_show = TRUE;
+ recent.statusbar_show = TRUE;
+ recent.packet_list_colorize = TRUE;
+ recent.gui_time_format = TS_RELATIVE;
+ recent.gui_time_precision = TS_PREC_AUTO;
+ recent.gui_seconds_format = TS_SECONDS_DEFAULT;
+ recent.gui_zoom_level = 0;
+ recent.gui_bytes_view = 0;
+
+ /* pane size of zero will autodetect */
+ recent.gui_geometry_main_upper_pane = 0;
+ recent.gui_geometry_main_lower_pane = 0;
+
+ recent.has_gui_geometry_main_upper_pane = TRUE;
+ recent.has_gui_geometry_main_lower_pane = TRUE;
+ recent.has_gui_geometry_status_pane = TRUE;
+
+ if (recent.col_width_list) {
+ free_col_width_info(&recent);
+ }
+
+ if (recent.gui_fileopen_remembered_dir) {
+ g_free (recent.gui_fileopen_remembered_dir);
+ recent.gui_fileopen_remembered_dir = NULL;
+ }
+
+ /* Construct the pathname of the user's profile recent file. */
+ rf_path = get_persconffile_path(RECENT_FILE_NAME, TRUE, FALSE);
+
+ /* Read the user's recent file, if it exists. */
+ *rf_path_return = NULL;
+ if ((rf = ws_fopen(rf_path, "r")) != NULL) {
+ /* We succeeded in opening it; read it. */
+ read_prefs_file(rf_path, rf, read_set_recent_pair_static, NULL);
+ fclose(rf);
+
+ /* XXX: The following code doesn't actually do anything since
+ * the "recent common file" always exists. Presumably the
+ * "if (!file_exists())" should actually be "if (file_exists())".
+ * However, I've left the code as is because this
+ * behaviour has existed for quite some time and I don't
+ * know what's supposed to happen at this point.
+ * ToDo: Determine if the "recent common file" should be read at this point
+ */
+ rf_common_path = get_persconffile_path(RECENT_COMMON_FILE_NAME, FALSE, FALSE);
+ if (!file_exists(rf_common_path)) {
+ /* Read older common settings from recent file */
+ rf = ws_fopen(rf_path, "r");
+ read_prefs_file(rf_path, rf, read_set_recent_common_pair_static, NULL);
+ fclose(rf);
+ }
+ g_free(rf_common_path);
+ g_free(rf_path);
+ rf_path = NULL;
+ } else {
+ /* We failed to open it. If we failed for some reason other than
+ "it doesn't exist", return the errno and the pathname, so our
+ caller can report the error. */
+ if (errno != ENOENT) {
+ *rf_errno_return = errno;
+ *rf_path_return = rf_path;
+ }
+ }
+}
+
+/* opens the user's recent file and read it out */
+void
+recent_read_dynamic(char **rf_path_return, int *rf_errno_return)
+{
+ char *rf_path;
+ FILE *rf;
+
+
+ /* Construct the pathname of the user's recent common file. */
+ rf_path = get_persconffile_path(RECENT_COMMON_FILE_NAME, FALSE, FALSE);
+ if (!file_exists (rf_path)) {
+ /* Recent common file does not exist, read from default recent */
+ g_free (rf_path);
+ rf_path = get_persconffile_path(RECENT_FILE_NAME, FALSE, FALSE);
+ }
+
+ /* Read the user's recent file, if it exists. */
+ *rf_path_return = NULL;
+ if ((rf = ws_fopen(rf_path, "r")) != NULL) {
+ /* We succeeded in opening it; read it. */
+ read_prefs_file(rf_path, rf, read_set_recent_pair_dynamic, NULL);
+#if 0
+ /* set dfilter combobox to have an empty line */
+ dfilter_combo_add_empty();
+#endif
+ fclose(rf);
+ g_free(rf_path);
+ rf_path = NULL;
+ } else {
+ /* We failed to open it. If we failed for some reason other than
+ "it doesn't exist", return the errno and the pathname, so our
+ caller can report the error. */
+ if (errno != ENOENT) {
+ *rf_errno_return = errno;
+ *rf_path_return = rf_path;
+ }
+ }
+}
+
+gint
+recent_get_column_width(gint col)
+{
+ GList *col_l;
+ col_width_data *col_w;
+ gint cfmt;
+ const gchar *cfield = NULL;
+
+ cfmt = get_column_format(col);
+ if (cfmt == COL_CUSTOM) {
+ cfield = get_column_custom_field(col);
+ }
+
+ col_l = g_list_first(recent.col_width_list);
+ while (col_l) {
+ col_w = (col_width_data *) col_l->data;
+ if (col_w->cfmt == cfmt) {
+ if (cfmt != COL_CUSTOM || strcmp (cfield, col_w->cfield) == 0) {
+ return col_w->width;
+ }
+ }
+ col_l = col_l->next;
+ }
+
+ return -1;
+}
+
+void
+recent_set_column_width(gint col, gint width)
+{
+ GList *col_l;
+ col_width_data *col_w;
+ gint cfmt;
+ const gchar *cfield = NULL;
+ gboolean found = FALSE;
+
+ cfmt = get_column_format(col);
+ if (cfmt == COL_CUSTOM) {
+ cfield = get_column_custom_field(col);
+ }
+
+ col_l = g_list_first(recent.col_width_list);
+ while (col_l) {
+ col_w = (col_width_data *) col_l->data;
+ if (col_w->cfmt == cfmt) {
+ if (cfmt != COL_CUSTOM || strcmp (cfield, col_w->cfield) == 0) {
+ col_w->width = width;
+ found = TRUE;
+ break;
+ }
+ }
+ col_l = col_l->next;
+ }
+
+ if (!found) {
+ col_w = (col_width_data *) g_malloc(sizeof(col_width_data));
+ col_w->cfmt = cfmt;
+ if (cfield) {
+ col_w->cfield = g_strdup(cfield);
+ } else {
+ col_w->cfield = NULL;
+ }
+ col_w->width = width;
+ col_w->xalign = COLUMN_XALIGN_DEFAULT;
+ recent.col_width_list = g_list_append(recent.col_width_list, col_w);
+ }
+}
+
+gchar
+recent_get_column_xalign(gint col)
+{
+ GList *col_l;
+ col_width_data *col_w;
+ gint cfmt;
+ const gchar *cfield = NULL;
+
+ cfmt = get_column_format(col);
+ if (cfmt == COL_CUSTOM) {
+ cfield = get_column_custom_field(col);
+ }
+
+ col_l = g_list_first(recent.col_width_list);
+ while (col_l) {
+ col_w = (col_width_data *) col_l->data;
+ if (col_w->cfmt == cfmt) {
+ if (cfmt != COL_CUSTOM || strcmp (cfield, col_w->cfield) == 0) {
+ return col_w->xalign;
+ }
+ }
+ col_l = col_l->next;
+ }
+
+ return 0;
+}
+
+void
+recent_set_column_xalign(gint col, gchar xalign)
+{
+ GList *col_l;
+ col_width_data *col_w;
+ gint cfmt;
+ const gchar *cfield = NULL;
+ gboolean found = FALSE;
+
+ cfmt = get_column_format(col);
+ if (cfmt == COL_CUSTOM) {
+ cfield = get_column_custom_field(col);
+ }
+
+ col_l = g_list_first(recent.col_width_list);
+ while (col_l) {
+ col_w = (col_width_data *) col_l->data;
+ if (col_w->cfmt == cfmt) {
+ if (cfmt != COL_CUSTOM || strcmp (cfield, col_w->cfield) == 0) {
+ col_w->xalign = xalign;
+ found = TRUE;
+ break;
+ }
+ }
+ col_l = col_l->next;
+ }
+
+ if (!found) {
+ col_w = (col_width_data *) g_malloc(sizeof(col_width_data));
+ col_w->cfmt = cfmt;
+ if (cfield) {
+ col_w->cfield = g_strdup(cfield);
+ } else {
+ col_w->cfield = NULL;
+ }
+ col_w->width = 40;
+ col_w->xalign = xalign;
+ recent.col_width_list = g_list_append(recent.col_width_list, col_w);
+ }
+}
diff --git a/ui/gtk/recent.h b/ui/gtk/recent.h
new file mode 100644
index 0000000000..d582573bbf
--- /dev/null
+++ b/ui/gtk/recent.h
@@ -0,0 +1,182 @@
+/* recent.h
+ * Definitions for recent "preference" handling routines
+ * Copyright 2004, Ulf Lamping <ulf.lamping@web.de>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __RECENT_H__
+#define __RECENT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <glib.h>
+
+/** @file
+ * Recent user interface settings.
+ * @ingroup main_window_group
+ */
+
+/** ???. */
+#define RECENT_KEY_CAPTURE_FILE "recent.capture_file"
+
+/** ???. */
+#define RECENT_KEY_DISPLAY_FILTER "recent.display_filter"
+
+typedef struct _col_width_data {
+ gint cfmt;
+ gchar *cfield;
+ gint width;
+ gchar xalign;
+} col_width_data;
+
+/** Defines used in col_width_data.xalign */
+#define COLUMN_XALIGN_DEFAULT 0
+#define COLUMN_XALIGN_LEFT 'L'
+#define COLUMN_XALIGN_CENTER 'C'
+#define COLUMN_XALIGN_RIGHT 'R'
+
+/** Recent settings. */
+typedef struct recent_settings_tag {
+ gboolean main_toolbar_show;
+ gboolean filter_toolbar_show;
+ gboolean airpcap_toolbar_show;
+ gboolean airpcap_driver_check_show;
+ gboolean packet_list_show;
+ gboolean tree_view_show;
+ gboolean byte_view_show;
+ gboolean statusbar_show;
+ gboolean packet_list_colorize;
+ gint gui_time_format;
+ gint gui_time_precision;
+ gint gui_seconds_format;
+ gint gui_zoom_level;
+ gint gui_bytes_view;
+
+ gint gui_geometry_main_x;
+ gint gui_geometry_main_y;
+ gint gui_geometry_main_width;
+ gint gui_geometry_main_height;
+
+ gboolean gui_geometry_main_maximized;
+
+ gboolean has_gui_geometry_main_upper_pane; /* gui_geometry_main_upper_pane is valid */
+ gint gui_geometry_main_upper_pane;
+ gboolean has_gui_geometry_main_lower_pane; /* gui_geometry_main_lower_pane is valid */
+ gint gui_geometry_main_lower_pane;
+ gboolean has_gui_geometry_status_pane; /* gui_geometry_status_pane is valid */
+ gint gui_geometry_status_pane_left;
+ gint gui_geometry_status_pane_right;
+ gint gui_geometry_wlan_stats_pane;
+ gboolean privs_warn_if_elevated;
+ gboolean privs_warn_if_no_npf;
+ GList *col_width_list; /* column widths */
+ gchar *gui_fileopen_remembered_dir; /* folder of last capture loaded in File Open dialog */
+} recent_settings_t;
+
+/** Global recent settings. */
+extern recent_settings_t recent;
+
+/** Write recent settings file.
+ *
+ * @return TRUE if succeeded, FALSE if failed
+ */
+extern gboolean write_recent(void);
+
+/** Write profile recent settings file.
+ *
+ * @return TRUE if succeeded, FALSE if failed
+ */
+extern gboolean write_profile_recent(void);
+
+/** Read recent settings file (static part).
+ *
+ * @param rf_path_return path to recent file if function failed
+ * @param rf_errno_return if failed
+ */
+extern void recent_read_static(char **rf_path_return, int *rf_errno_return);
+
+/** Read profile recent settings file (static part).
+ *
+ * @param rf_path_return path to recent file if function failed
+ * @param rf_errno_return if failed
+ */
+extern void recent_read_profile_static(char **rf_path_return, int *rf_errno_return);
+
+/** Read recent settings file (dynamic part).
+ *
+ * @param rf_path_return path to recent file if function failed
+ * @param rf_errno_return if failed
+ */
+extern void recent_read_dynamic(char **rf_path_return, int *rf_errno_return);
+
+/** Write the geometry values of a single window to the recent file.
+ *
+ * @param key unused
+ * @param value the geometry values
+ * @param rf recent file handle (FILE)
+ */
+extern void write_recent_geom(gpointer key, gpointer value, gpointer rf);
+
+/**
+ * Given a -o command line string, parse it and set the recent value in
+ * question. Return an indication of whether it succeeded or failed
+ * in some fashion.
+ *
+ * @param prefarg a string of the form "<recent name>:<recent value>", as might appear
+ * as an argument to a "-o" command line option
+ * @return PREFS_SET_OK or PREFS_SET_SYNTAX_ERR
+ */
+extern int recent_set_arg(char *prefarg);
+
+/** Get the column width for the given column
+ *
+ * @param col column number
+ */
+extern gint recent_get_column_width(gint col);
+
+/** Set the column width for the given column
+ *
+ * @param col column number
+ * @param width column width
+ */
+extern void recent_set_column_width(gint col, gint width);
+
+/** Get the column xalign for the given column
+ *
+ * @param col column number
+ */
+extern gchar recent_get_column_xalign(gint col);
+
+/** Set the column xalign for the given column
+ *
+ * @param col column number
+ * @param xalign column alignment
+ */
+extern void recent_set_column_xalign(gint col, gchar xalign);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* recent.h */
diff --git a/ui/gtk/remote_icons.h b/ui/gtk/remote_icons.h
new file mode 100644
index 0000000000..4dc205ae17
--- /dev/null
+++ b/ui/gtk/remote_icons.h
@@ -0,0 +1,224 @@
+/* This file was automatically generated. DO NOT EDIT. */
+
+/* GdkPixbuf RGBA C-Source image dump */
+
+#ifdef __SUNPRO_C
+#pragma align 4 (remote_arrow_pb_data)
+#endif
+#ifdef __GNUC__
+static const guint8 remote_arrow_pb_data[] __attribute__ ((__aligned__ (4))) =
+#else
+static const guint8 remote_arrow_pb_data[] =
+#endif
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (1024) */
+ "\0\0\4\30"
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (64) */
+ "\0\0\0@"
+ /* width (16) */
+ "\0\0\0\20"
+ /* height (16) */
+ "\0\0\0\20"
+ /* pixel_data: */
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\253\254\251\377\202\204\177\377\202\204\177\377\202"
+ "\204\177\377\202\204\177\377\202\204\177\377\202\204\177\377\202\204"
+ "\177\377\202\204\177\377\265\266\263\377\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\202\204\177"
+ "\377\310\310\307\377\353\361\367\377\353\361\367\377\353\361\367\377"
+ "\353\361\367\377\353\361\367\377\353\361\367\377\326\326\326\377\202"
+ "\204\177\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\202\204\177\377\353\361\367\377<]\244\377"
+ "Bc\250\377Bc\250\377Bc\250\377Bc\250\377<]\244\377\356\364\370\377\202"
+ "\204\177\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\377\377\377\0\202\204\177\377\354\362\370\377Bc\250\377"
+ "r\230\314\377r\230\313\377r\230\314\377r\230\314\377Bc\250\377\354\362"
+ "\370\377\202\204\177\377\253\254\251\377\202\204\177\377\202\204\177"
+ "\377\202\204\177\377\202\204\177\377\202\204\177\377\202\204\177\377"
+ "\202\204\177\377\202\204\177\377|\205\217\377]\206\302\377S}\274\377"
+ "Fq\264\377Bc\250\377\354\362\370\377\202\204\177\377\202\204\177\377"
+ "\310\310\307\377\353\361\367\377\353\361\367\377\353\361\367\377\353"
+ "\361\367\377\353\361\367\377\353\361\367\377\326\326\326\377\202\204"
+ "\177\377Dq\267\377Dq\267\377Dq\267\377Bc\250\377\353\361\367\377\202"
+ "\204\177\377\202\204\177\377\353\361\367\377<]\244\377Bc\250\377Bc\250"
+ "\377Bc\250\377Bc\250\377<]\244\377\356\364\370\377\202\204\177\377Bc"
+ "\250\377Bc\250\377Bc\250\377<]\244\377\353\361\367\377\202\204\177\377"
+ "\202\204\177\377\354\362\370\377Bc\250\377r\230\314\377r\230\313\377"
+ "r\230\314\377r\230\314\377Bc\250\377\354\362\370\377\202\204\177\377"
+ "\353\361\367\377\353\361\367\377\353\361\367\377\353\361\367\377\326"
+ "\326\326\377\202\204\177\377\202\204\177\377\354\362\370\377Bc\250\377"
+ "u\233\316\377]\206\302\377S}\274\377Fq\264\377Bc\250\377\354\362\370"
+ "\377\202\204\177\377\202\204\177\377\202\204\177\377\202\204\177\377"
+ "\202\204\177\377\202\204\177\377\302\303\300\377\202\204\177\377\354"
+ "\362\370\377Bc\250\377Dr\266\377Dq\267\377Dq\267\377Dq\267\377Bc\250"
+ "\377\353\361\367\377\202\204\177\377\353\353\353\377\353\353\353\377"
+ "\353\353\353\377\206\251\306\377\5T\225\377j{\204\377\202\204\177\377"
+ "\353\361\367\377<]\244\377Bc\250\377Bc\250\377Bc\250\377Bc\250\377<]"
+ "\244\377\353\361\367\377k~\207\377\261\302\317\377\331\331\331\377\177"
+ "\244\301\377\31y\265\377;\310\365\377\14_\240\377\202\204\177\377\326"
+ "\326\326\377\353\361\367\377\353\361\367\377\353\361\367\377\353\361"
+ "\367\377\353\361\367\377\353\361\367\377\326\326\326\377\27m\256\377"
+ "\37\203\277\377Ky\231\377\37\203\277\377F\313\365\377'\207\300\377Sy"
+ "\223\377\265\266\263\377\202\204\177\377\202\204\177\377\202\204\177"
+ "\377\202\204\177\377\202\204\177\377\202\204\177\377\202\204\177\377"
+ "\202\204\177\377\"|\273\377q\327\365\377@\220\300\377S\317\365\377-\220"
+ "\313\377\235\304\340\377\377\377\377\0\202\204\177\377\363\363\363\377"
+ "\353\353\353\377\353\353\353\377\353\353\353\377\353\353\353\377\353"
+ "\353\353\377\353\353\353\377\355\355\355\377,\212\312\377e\304\351\377"
+ "u\330\365\377D\221\300\377\220\301\342\377\377\377\377\0\377\377\377"
+ "\0\202\204\177\377\331\331\331\377\331\331\331\377\331\331\331\377\331"
+ "\331\331\377\331\331\331\377\331\331\331\377\331\331\331\377\331\331"
+ "\331\3776\226\326\377\222\341\365\377y\316\355\377\223\341\365\377M\250"
+ "\336\377\327\353\367\377\377\377\377\0\202\204\177\377\202\204\177\377"
+ "\202\204\177\377\202\204\177\377\202\204\177\377\202\204\177\377\202"
+ "\204\177\377\202\204\177\377\202\204\177\377X\224\270\377=\237\337\377"
+ "=\237\337\377=\237\337\377=\237\337\377\330\353\370\377\377\377\377\0"};
+
+
+/* GdkPixbuf RGBA C-Source image dump */
+
+#ifdef __SUNPRO_C
+#pragma align 4 (remote_globe_pb_data)
+#endif
+#ifdef __GNUC__
+static const guint8 remote_globe_pb_data[] __attribute__ ((__aligned__ (4))) =
+#else
+static const guint8 remote_globe_pb_data[] =
+#endif
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (1024) */
+ "\0\0\4\30"
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (64) */
+ "\0\0\0@"
+ /* width (16) */
+ "\0\0\0\20"
+ /* height (16) */
+ "\0\0\0\20"
+ /* pixel_data: */
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\364\371\376"
+ "\377\245\324\365\377Y\252\354\377<\227\347\3770\217\346\377<\227\347"
+ "\377Y\252\354\377\245\324\365\377\364\371\376\377\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\323\351"
+ "\370\377K\234\341\377B\224\336\377k\263\330\377\202\301\330\377\212\305"
+ "\324\377\220\336\260\377v\310\277\377F\234\323\377K\234\341\377\323\351"
+ "\370\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\323"
+ "\347\365\377>\213\316\377Y\237\316\377w\275\270\377\202\333\232\377y"
+ "\274\320\377v\260\341\377\204\324\246\377\207\343\222\377\212\343\227"
+ "\377d\267\270\377>\213\316\377\323\347\365\377\377\377\377\0\377\377"
+ "\377\0\363\370\373\377F\207\304\377R\233\266\377T\245\247\377m\317\213"
+ "\377\200\337\220\377x\274\307\377|\303\277\377\202\326\237\377\177\334"
+ "\220\377t\336\201\377k\334x\377Y\256\244\377F\207\304\377\363\370\373"
+ "\377\220\221\216\377ce`\377ce`\377ce`\377ce`\377ce`\377ce`\377ce`\377"
+ "ce`\377g\211h\377P\225\242\377V\303v\377Q\327`\377V\322i\3772l\253\377"
+ "\240\301\334\377ce`\377\264\264\263\377\343\354\364\377\343\354\364\377"
+ "\343\354\364\377\343\354\364\377\343\354\364\377\343\354\364\377\307"
+ "\307\307\377ce`\3770n\240\377@\311T\3774\262Y\377+\226m\377>\205\234"
+ "\377Q}\256\377ce`\377\343\354\364\377)B\210\377-G\214\377-G\214\377-"
+ "G\214\377-G\214\377)B\210\377\347\360\365\377ce`\377.\316=\377)\275A"
+ "\377\34tw\377\33i\233\3774v\267\3771\\\217\377ce`\377\345\355\365\377"
+ "-G\214\377Tz\272\377Tz\270\377Tz\272\377Tz\272\377-G\214\377\345\355"
+ "\365\377ce`\377\27\204S\377\32\2672\377\21N\227\377\25yl\377(j\244\377"
+ "$L\177\377ce`\377\345\355\365\377-G\214\377W}\274\377Bg\255\377:_\245"
+ "\3770S\233\377-G\214\377\345\355\365\377ce`\377\23\210N\377\22\262)\377"
+ "\16Sz\377\21\301\35\377#\246A\3770V\207\377ce`\377\345\355\365\377-G"
+ "\214\377/T\235\377/S\237\377/S\237\377/S\237\377-G\214\377\343\354\364"
+ "\377ce`\377\31\200q\377\25nk\377\17\2223\377\15\270\30\377\40\224B\377"
+ "Nt\240\377ce`\377\343\354\364\377)B\210\377-G\214\377-G\214\377-G\214"
+ "\377-G\214\377)B\210\377\343\354\364\377ce`\377+u\313\377\"b\256\377"
+ "\21\253\33\377\23\253\34\377\40bk\377\235\270\322\377ce`\377\307\307"
+ "\307\377\343\354\364\377\343\354\364\377\343\354\364\377\343\354\364"
+ "\377\343\354\364\377\343\354\364\377\307\307\307\377ce`\377>\210\337"
+ "\3771o\310\377\33\237\"\377\35}K\377Ao\246\377\363\367\371\377\234\235"
+ "\232\377ce`\377ce`\377ce`\377ce`\377ce`\377ce`\377ce`\377ce`\377\\\177"
+ "\242\377M\224\352\377>{\326\377${U\3771k\241\377\320\341\356\377\377"
+ "\377\377\0ce`\377\356\356\356\377\343\343\343\377\343\343\343\377\343"
+ "\343\343\377\343\343\343\377\343\343\343\377\343\343\343\377\346\346"
+ "\346\377ce`\377\77\203\334\377*h\270\377A{\273\377\320\342\360\377\377"
+ "\377\377\0\377\377\377\0ce`\377\313\313\313\377\313\313\313\377\313\313"
+ "\313\377\313\313\313\377\313\313\313\377\313\313\313\377\313\313\313"
+ "\377\313\313\313\377ce`\377N\213\310\377\235\303\343\377\363\370\373"
+ "\377\377\377\377\0\377\377\377\0\377\377\377\0ce`\377ce`\377ce`\377c"
+ "e`\377ce`\377ce`\377ce`\377ce`\377ce`\377ce`\377\377\377\377\0\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"};
+
+
+/* GdkPixbuf RGBA C-Source image dump */
+
+#ifdef __SUNPRO_C
+#pragma align 4 (remote_sat_pb_data)
+#endif
+#ifdef __GNUC__
+static const guint8 remote_sat_pb_data[] __attribute__ ((__aligned__ (4))) =
+#else
+static const guint8 remote_sat_pb_data[] =
+#endif
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (1024) */
+ "\0\0\4\30"
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (64) */
+ "\0\0\0@"
+ /* width (16) */
+ "\0\0\0\20"
+ /* height (16) */
+ "\0\0\0\20"
+ /* pixel_data: */
+ "\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377"
+ "\0\377\377\377\0\377\377\377\0\277\277\277\377\256\256\256\377\377\377"
+ "\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377"
+ "\377\0\370\370\370\377z\177\214\377do\207\377`j\177\377inz\377\242\244"
+ "\251\377\377\377\377\0\311\317\337\377{\202\222\377\244\244\246\377\377"
+ "\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0\377"
+ "\377\377\0u{\212\377\240\303\355\377\246\311\363\377\251\313\363\377"
+ "\251\311\361\377\203\233\304\377V`\202\377js\217\377x\202\247\377\363"
+ "\364\367\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"
+ "\377\377\377\0\330\330\332\377\177\217\256\377\252\313\363\377\257\317"
+ "\363\377\260\317\363\377\202\232\310\377\207\240\314\377\230\262\336"
+ "\377s\207\262\377QYu\377\363\363\364\377\377\377\377\0\377\377\377\0"
+ "\220\221\216\377ce`\377ce`\377ce`\377ce`\377ce`\377ce`\377ce`\377ce`"
+ "\377}\204\210\377\221\247\320\377\232\262\334\377}\224\301\377TZi\377"
+ "\352\352\352\377\377\377\377\0ce`\377\264\264\263\377\343\354\364\377"
+ "\343\354\364\377\343\354\364\377\343\354\364\377\343\354\364\377\343"
+ "\354\364\377\307\307\307\377ce`\377\311\336\367\377\241\270\336\377\214"
+ "\244\317\377\244\277\345\377QUc\377\371\371\371\377ce`\377\343\354\364"
+ "\377)B\210\377-G\214\377-G\214\377-G\214\377-G\214\377)B\210\377\347"
+ "\360\365\377ce`\377\322\343\367\377\311\336\364\377\224\252\322\377\267"
+ "\323\364\377\214\246\314\377orx\377ce`\377\345\355\365\377-G\214\377"
+ "Tz\272\377Tz\270\377Tz\272\377Tz\272\377-G\214\377\345\355\365\377ce"
+ "`\377\337\355\371\377\326\346\370\377\232\257\326\377\274\327\364\377"
+ "\260\317\363\377NWk\377ce`\377\345\355\365\377-G\214\377W}\274\377Bg"
+ "\255\377:_\245\3770S\233\377-G\214\377\345\355\365\377ce`\377\345\360"
+ "\371\377\313\334\360\377\252\277\341\377\275\330\367\377\262\320\364"
+ "\377l\201\244\377ce`\377\345\355\365\377-G\214\377/T\235\377/S\237\377"
+ "/S\237\377/S\237\377-G\214\377\343\354\364\377ce`\377\222\240\274\377"
+ "\273\313\343\377\303\330\363\377\275\330\364\377\260\320\364\377\\i\202"
+ "\377ce`\377\343\354\364\377)B\210\377-G\214\377-G\214\377-G\214\377-"
+ "G\214\377)B\210\377\343\354\364\377ce`\377ALv\377AMu\377Wd\214\377`r"
+ "\233\377LXu\377rsu\377ce`\377\307\307\307\377\343\354\364\377\343\354"
+ "\364\377\343\354\364\377\343\354\364\377\343\354\364\377\343\354\364"
+ "\377\307\307\307\377ce`\377=Fg\3777@]\37749L\377BFO\377\235\235\235\377"
+ "\377\377\377\0\234\235\232\377ce`\377ce`\377ce`\377ce`\377ce`\377ce`"
+ "\377ce`\377ce`\377iq\201\377R^\207\377ISu\377\257\257\260\377\377\377"
+ "\377\0\377\377\377\0\377\377\377\0ce`\377\356\356\356\377\343\343\343"
+ "\377\343\343\343\377\343\343\343\377\343\343\343\377\343\343\343\377"
+ "\343\343\343\377\346\346\346\377ce`\377R^\207\377R^\207\377Z]e\377\377"
+ "\377\377\0\377\377\377\0\377\377\377\0ce`\377\313\313\313\377\313\313"
+ "\313\377\313\313\313\377\313\313\313\377\313\313\313\377\313\313\313"
+ "\377\313\313\313\377\313\313\313\377ce`\377KUy\377;AQ\377\233\234\234"
+ "\377\377\377\377\0\377\377\377\0\377\377\377\0ce`\377ce`\377ce`\377c"
+ "e`\377ce`\377ce`\377ce`\377ce`\377ce`\377ce`\377\230\230\232\377\352"
+ "\352\352\377\377\377\377\0\377\377\377\0\377\377\377\0\377\377\377\0"};
+
+
diff --git a/ui/gtk/rlc_lte_stat_dlg.c b/ui/gtk/rlc_lte_stat_dlg.c
new file mode 100644
index 0000000000..fb8b8058c6
--- /dev/null
+++ b/ui/gtk/rlc_lte_stat_dlg.c
@@ -0,0 +1,1578 @@
+/* rlc_lte_stat_dlg.c
+ * Copyright 2010 Martin Mathieson
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+/* TODO:
+ - per-channel graph tap?
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "ui/gtk/gtkglobals.h"
+
+#include <epan/packet.h>
+#include <epan/packet_info.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-rlc-lte.h>
+
+#include "../simple_dialog.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/main.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+/**********************************************/
+/* Table column identifiers and title strings */
+
+enum {
+ UEID_COLUMN,
+ UL_FRAMES_COLUMN,
+ UL_BYTES_COLUMN,
+ UL_BW_COLUMN,
+ UL_ACKS_COLUMN,
+ UL_NACKS_COLUMN,
+ UL_MISSING_COLUMN,
+ DL_FRAMES_COLUMN,
+ DL_BYTES_COLUMN,
+ DL_BW_COLUMN,
+ DL_ACKS_COLUMN,
+ DL_NACKS_COLUMN,
+ DL_MISSING_COLUMN,
+ UE_TABLE_COLUMN,
+ NUM_UE_COLUMNS
+};
+
+enum {
+ CHANNEL_NAME,
+ CHANNEL_MODE,
+ CHANNEL_PRIORITY,
+ CHANNEL_UL_FRAMES,
+ CHANNEL_UL_BYTES,
+ CHANNEL_UL_BW,
+ CHANNEL_UL_ACKS,
+ CHANNEL_UL_NACKS,
+ CHANNEL_UL_MISSING,
+ CHANNEL_DL_FRAMES,
+ CHANNEL_DL_BYTES,
+ CHANNEL_DL_BW,
+ CHANNEL_DL_ACKS,
+ CHANNEL_DL_NACKS,
+ CHANNEL_DL_MISSING,
+ CHANNEL_TABLE_COLUMN,
+ NUM_CHANNEL_COLUMNS
+};
+
+static const gchar *ue_titles[] = { "UEId",
+ "UL Frames", "UL Bytes", "UL MBit/sec", "UL ACKs", "UL NACKs", "UL Missing",
+ "DL Frames", "DL Bytes", "DL MBit/sec", "DL ACKs", "DL NACKs", "DL Missing"};
+
+static const gchar *channel_titles[] = { "", "Mode", "Priority",
+ "UL Frames", "UL Bytes", "UL MBit/sec", "UL ACKs", "UL NACKs", "UL Missing",
+ "DL Frames", "DL Bytes", "DL MBit/sec", "DL ACKs", "DL NACKs", "DL Missing"};
+
+/* Stats kept for one channel */
+typedef struct rlc_channel_stats {
+ guint8 inUse;
+ guint8 rlcMode;
+ guint8 priority;
+ guint16 channelType;
+ guint16 channelId;
+
+ guint32 UL_frames;
+ guint32 UL_bytes;
+ nstime_t UL_time_start;
+ nstime_t UL_time_stop;
+
+ guint32 DL_frames;
+ guint32 DL_bytes;
+ nstime_t DL_time_start;
+ nstime_t DL_time_stop;
+
+ guint32 UL_acks;
+ guint32 UL_nacks;
+
+ guint32 DL_acks;
+ guint32 DL_nacks;
+
+ guint32 UL_missing;
+ guint32 DL_missing;
+
+ GtkTreeIter iter;
+ gboolean iter_valid;
+} rlc_channel_stats;
+
+
+/* Stats for one UE */
+typedef struct rlc_lte_row_data {
+ /* Key for matching this row */
+ guint16 ueid;
+
+ gboolean is_predefined_data;
+
+ guint32 UL_frames;
+ guint32 UL_total_bytes;
+ nstime_t UL_time_start;
+ nstime_t UL_time_stop;
+ guint32 UL_total_acks;
+ guint32 UL_total_nacks;
+ guint32 UL_total_missing;
+
+ guint32 DL_frames;
+ guint32 DL_total_bytes;
+ nstime_t DL_time_start;
+ nstime_t DL_time_stop;
+ guint32 DL_total_acks;
+ guint32 DL_total_nacks;
+ guint32 DL_total_missing;
+
+ rlc_channel_stats CCCH_stats;
+ rlc_channel_stats srb_stats[2];
+ rlc_channel_stats drb_stats[32];
+} rlc_lte_row_data;
+
+
+/* Common channel stats */
+typedef struct rlc_lte_common_stats {
+ guint32 bcch_frames;
+ guint32 bcch_bytes;
+ guint32 pcch_frames;
+ guint32 pcch_bytes;
+} rlc_lte_common_stats;
+
+
+/* One row/UE in the UE table */
+typedef struct rlc_lte_ep {
+ struct rlc_lte_ep* next;
+ struct rlc_lte_row_data stats;
+ GtkTreeIter iter;
+ gboolean iter_valid;
+} rlc_lte_ep_t;
+
+
+/* Used to keep track of whole RLC LTE statistics window */
+typedef struct rlc_lte_stat_t {
+ GtkTreeView *ue_table;
+ rlc_lte_ep_t *ep_list;
+ guint32 total_frames;
+
+ char *filter;
+
+ /* Top-level dialog and labels */
+ GtkWidget *dlg_w;
+ GtkWidget *ues_lb;
+
+ /* Other widgets */
+ GtkWidget *ul_filter_bt;
+ GtkWidget *dl_filter_bt;
+ GtkWidget *uldl_filter_bt;
+ GtkWidget *show_only_control_pdus_cb;
+ GtkWidget *show_dct_errors_cb;
+ GtkWidget *dct_error_substring_lb;
+ GtkWidget *dct_error_substring_te;
+ GtkWidget *sn_filter_lb;
+ GtkWidget *sn_filter_te;
+
+ /* Common stats */
+ rlc_lte_common_stats common_stats;
+ GtkWidget *common_bcch_frames;
+ GtkWidget *common_bcch_bytes;
+ GtkWidget *common_pcch_frames;
+ GtkWidget *common_pcch_bytes;
+
+ gboolean show_mac;
+
+ /* State used to attempt to re-select chosen UE/channel */
+ guint16 reselect_ue;
+ guint16 reselect_channel_type;
+ guint16 reselect_channel_id;
+
+ GtkTreeView *channel_table;
+} rlc_lte_stat_t;
+
+
+static int get_channel_selection(rlc_lte_stat_t *hs,
+ guint16 *ueid, guint8 *rlcMode,
+ guint16 *channelType, guint16 *channelId);
+
+/* Show filter controls appropriate to current selection */
+static void enable_filter_controls(guint8 enabled, guint8 rlcMode, rlc_lte_stat_t *hs)
+{
+ guint8 show_dct_errors = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hs->show_dct_errors_cb));
+
+ gtk_widget_set_sensitive(hs->ul_filter_bt, enabled);
+ gtk_widget_set_sensitive(hs->dl_filter_bt, enabled);
+ gtk_widget_set_sensitive(hs->uldl_filter_bt, enabled);
+ gtk_widget_set_sensitive(hs->show_dct_errors_cb, enabled);
+
+ /* Enabling substring control only if errors enabled */
+ gtk_widget_set_sensitive(hs->dct_error_substring_lb, enabled && show_dct_errors);
+ gtk_widget_set_sensitive(hs->dct_error_substring_te, enabled && show_dct_errors);
+
+ switch (rlcMode) {
+ case RLC_TM_MODE:
+ gtk_widget_set_sensitive(hs->show_only_control_pdus_cb, FALSE);
+ gtk_widget_set_sensitive(hs->sn_filter_lb, FALSE);
+ gtk_widget_set_sensitive(hs->sn_filter_te, FALSE);
+ break;
+ case RLC_UM_MODE:
+ gtk_widget_set_sensitive(hs->show_only_control_pdus_cb, FALSE);
+ gtk_widget_set_sensitive(hs->sn_filter_lb, TRUE);
+ gtk_widget_set_sensitive(hs->sn_filter_te, TRUE);
+ break;
+ case RLC_AM_MODE:
+ gtk_widget_set_sensitive(hs->show_only_control_pdus_cb, TRUE);
+ gtk_widget_set_sensitive(hs->sn_filter_lb, TRUE);
+ gtk_widget_set_sensitive(hs->sn_filter_te, TRUE);
+ break;
+
+ default:
+ gtk_widget_set_sensitive(hs->show_only_control_pdus_cb, FALSE);
+ gtk_widget_set_sensitive(hs->sn_filter_lb, FALSE);
+ gtk_widget_set_sensitive(hs->sn_filter_te, FALSE);
+ break;
+ }
+}
+
+
+
+/* Reset the statistics window */
+static void
+rlc_lte_stat_reset(void *phs)
+{
+ rlc_lte_stat_t* rlc_lte_stat = (rlc_lte_stat_t *)phs;
+ rlc_lte_ep_t* list = rlc_lte_stat->ep_list;
+ gchar title[256];
+ GtkListStore *store;
+
+ /* Set the title */
+ if (rlc_lte_stat->dlg_w != NULL) {
+ g_snprintf(title, sizeof(title), "Wireshark: LTE RLC Traffic Statistics: %s (filter=\"%s\")",
+ cf_get_display_name(&cfile),
+ strlen(rlc_lte_stat->filter) ? rlc_lte_stat->filter : "none");
+ gtk_window_set_title(GTK_WINDOW(rlc_lte_stat->dlg_w), title);
+ }
+
+ g_snprintf(title, sizeof(title), "0 UEs");
+ gtk_frame_set_label(GTK_FRAME(rlc_lte_stat->ues_lb), title);
+
+ rlc_lte_stat->total_frames = 0;
+ memset(&rlc_lte_stat->common_stats, 0, sizeof(rlc_lte_common_stats));
+
+ /* Remove all entries from the UE list */
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(rlc_lte_stat->ue_table));
+ gtk_list_store_clear(store);
+
+ if (!list) {
+ return;
+ }
+
+ rlc_lte_stat->ep_list = NULL;
+}
+
+
+/* Allocate a rlc_lte_ep_t struct to store info for new UE */
+static rlc_lte_ep_t* alloc_rlc_lte_ep(struct rlc_lte_tap_info *si, packet_info *pinfo _U_)
+{
+ rlc_lte_ep_t* ep;
+ int n;
+
+ if (!si) {
+ return NULL;
+ }
+
+ if (!(ep = g_malloc(sizeof(rlc_lte_ep_t)))) {
+ return NULL;
+ }
+
+ /* Copy SI data into ep->stats */
+ ep->stats.ueid = si->ueid;
+
+ /* Counts for new UE are all 0 */
+ ep->stats.UL_frames = 0;
+ ep->stats.DL_frames = 0;
+ ep->stats.UL_total_bytes = 0;
+ ep->stats.DL_total_bytes = 0;
+ memset(&ep->stats.DL_time_start, 0, sizeof(nstime_t));
+ memset(&ep->stats.DL_time_stop, 0, sizeof(nstime_t));
+ ep->stats.UL_total_acks = 0;
+ ep->stats.DL_total_acks = 0;
+ ep->stats.UL_total_nacks = 0;
+ ep->stats.DL_total_nacks = 0;
+ ep->stats.UL_total_missing = 0;
+ ep->stats.DL_total_missing = 0;
+
+ memset(&ep->stats.CCCH_stats, 0, sizeof(rlc_channel_stats));
+ for (n=0; n < 2; n++) {
+ memset(&ep->stats.srb_stats[n], 0, sizeof(rlc_channel_stats));
+ }
+ for (n=0; n < 32; n++) {
+ memset(&ep->stats.drb_stats[n], 0, sizeof(rlc_channel_stats));
+ }
+
+ ep->next = NULL;
+ ep->iter_valid = FALSE;
+
+ return ep;
+}
+
+
+/* Return string for RLC mode for display */
+static const char *print_rlc_channel_mode(guint8 mode)
+{
+ static char unknown[16];
+
+ switch (mode) {
+ case RLC_TM_MODE: return "TM";
+ case RLC_UM_MODE: return "UM";
+ case RLC_AM_MODE: return "AM";
+ case RLC_PREDEF: return "Predef";
+
+ default:
+ g_snprintf(unknown, 32, "Unknown (%u)", mode);
+ return unknown;
+ }
+}
+
+
+/* Process stat struct for a RLC LTE frame */
+static int
+rlc_lte_stat_packet(void *phs, packet_info *pinfo, epan_dissect_t *edt _U_,
+ const void *phi)
+{
+ /* Get reference to stat window instance */
+ rlc_lte_stat_t *hs = (rlc_lte_stat_t *)phs;
+ rlc_lte_ep_t *tmp = NULL, *te = NULL;
+ rlc_channel_stats *channel_stats = NULL;
+
+ /* Cast tap info struct */
+ struct rlc_lte_tap_info *si = (struct rlc_lte_tap_info *)phi;
+
+ /* Need this */
+ if (!hs) {
+ return 0;
+ }
+
+ /* Are we ignoring RLC frames that were found in MAC frames, or only those
+ that were logged separately? */
+ if ((!hs->show_mac && si->loggedInMACFrame) ||
+ (hs->show_mac && !si->loggedInMACFrame)) {
+ return 0;
+ }
+
+ /* Inc top-level frame count */
+ hs->total_frames++;
+
+ /* Common channel stats */
+ switch (si->channelType) {
+ case CHANNEL_TYPE_BCCH_BCH:
+ case CHANNEL_TYPE_BCCH_DL_SCH:
+ hs->common_stats.bcch_frames++;
+ hs->common_stats.bcch_bytes += si->pduLength;
+ return 1;
+ case CHANNEL_TYPE_PCCH:
+ hs->common_stats.pcch_frames++;
+ hs->common_stats.pcch_bytes += si->pduLength;
+ return 1;
+
+ default:
+ break;
+ }
+
+ /* For per-UE data, must create a new row if none already existing */
+ if (!hs->ep_list) {
+ /* Allocate new list */
+ hs->ep_list = alloc_rlc_lte_ep(si, pinfo);
+ /* Make it the first/only entry */
+ te = hs->ep_list;
+ } else {
+ /* Look among existing rows for this UEId */
+ for (tmp = hs->ep_list; (tmp != NULL); tmp = tmp->next) {
+ if (tmp->stats.ueid == si->ueid) {
+ te = tmp;
+ break;
+ }
+ }
+
+ /* Not found among existing, so create a new one anyway */
+ if (te == NULL) {
+ if ((te = alloc_rlc_lte_ep(si, pinfo))) {
+ /* Add new item to end of list */
+ rlc_lte_ep_t *p = hs->ep_list;
+ while (p->next) {
+ p = p->next;
+ }
+ p->next = te;
+ te->next = NULL;
+ }
+ }
+ }
+
+ /* Really should have a row pointer by now */
+ if (!te) {
+ return 0;
+ }
+
+ /* Update entry with details from si */
+ te->stats.ueid = si->ueid;
+
+ /* Top-level traffic stats */
+ if (si->direction == DIRECTION_UPLINK) {
+ /* Update time range */
+ if (te->stats.UL_frames == 0) {
+ te->stats.UL_time_start = si->time;
+ }
+ te->stats.UL_time_stop = si->time;
+
+ te->stats.UL_frames++;
+ te->stats.UL_total_bytes += si->pduLength;
+ }
+ else {
+ /* Update time range */
+ if (te->stats.DL_frames == 0) {
+ te->stats.DL_time_start = si->time;
+ }
+ te->stats.DL_time_stop = si->time;
+
+ te->stats.DL_frames++;
+ te->stats.DL_total_bytes += si->pduLength;
+ }
+
+ /* Find channel struct */
+ switch (si->channelType) {
+ case CHANNEL_TYPE_CCCH:
+ channel_stats = &te->stats.CCCH_stats;
+ break;
+
+ case CHANNEL_TYPE_SRB:
+ channel_stats = &te->stats.srb_stats[si->channelId-1];
+ break;
+
+ case CHANNEL_TYPE_DRB:
+ channel_stats = &te->stats.drb_stats[si->channelId-1];
+ break;
+
+ default:
+ /* Shouldn't get here... */
+ return 0;
+ }
+
+ if (channel_stats != NULL) {
+ channel_stats->inUse = TRUE;
+ channel_stats->iter_valid = FALSE;
+ channel_stats->rlcMode = si->rlcMode;
+ channel_stats->channelType = si->channelType;
+ channel_stats->channelId = si->channelId;
+ if (si->priority != 0) {
+ channel_stats->priority = si->priority;
+ }
+ }
+ else {
+ /* Giving up if no channel found... */
+ return 0;
+ }
+
+ if (si->direction == DIRECTION_UPLINK) {
+ /* Update time range */
+ if (channel_stats->UL_frames == 0) {
+ channel_stats->UL_time_start = si->time;
+ }
+ channel_stats->UL_time_stop = si->time;
+
+ channel_stats->UL_frames++;
+ channel_stats->UL_bytes += si->pduLength;
+ channel_stats->UL_nacks += si->noOfNACKs;
+ channel_stats->UL_missing += si->missingSNs;
+ if (si->isControlPDU) {
+ channel_stats->UL_acks++;
+ te->stats.UL_total_acks++;
+ }
+ te->stats.UL_total_nacks += si->noOfNACKs;
+ te->stats.UL_total_missing += si->missingSNs;
+ }
+ else {
+ /* Update time range */
+ if (channel_stats->DL_frames == 0) {
+ channel_stats->DL_time_start = si->time;
+ }
+ channel_stats->DL_time_stop = si->time;
+
+ channel_stats->DL_frames++;
+ channel_stats->DL_bytes += si->pduLength;
+ channel_stats->DL_nacks += si->noOfNACKs;
+ channel_stats->DL_missing += si->missingSNs;
+ if (si->isControlPDU) {
+ channel_stats->DL_acks++;
+ te->stats.DL_total_acks++;
+ }
+ te->stats.DL_total_nacks += si->noOfNACKs;
+ te->stats.DL_total_missing += si->missingSNs;
+ }
+
+ return 1;
+}
+
+
+/* The channels for any UE would need to be re-added to the list */
+static void invalidate_channel_iters(rlc_lte_stat_t *hs)
+{
+ gint n;
+ rlc_lte_ep_t *ep = hs->ep_list;
+
+ while (ep) {
+ ep->stats.CCCH_stats.iter_valid = FALSE;
+ for (n=0; n < 2; n++) {
+ ep->stats.srb_stats[n].iter_valid = FALSE;
+ }
+ for (n=0; n < 32; n++) {
+ ep->stats.drb_stats[n].iter_valid = FALSE;
+ }
+
+ ep = ep->next;
+ }
+}
+
+
+/* Calculate and return a bandwidth figure, in Mbs */
+static float calculate_bw(nstime_t *start_time, nstime_t *stop_time, guint32 bytes)
+{
+ if (memcmp(start_time, stop_time, sizeof(nstime_t)) != 0) {
+ float elapsed_ms = (((float)stop_time->secs - (float)start_time->secs) * 1000) +
+ (((float)stop_time->nsecs - (float)start_time->nsecs) / 1000000);
+ return ((bytes * 8) / elapsed_ms) / 1000;
+ }
+ else {
+ return 0.0;
+ }
+}
+
+
+
+/* Draw the channels table according to the current UE selection */
+static void
+rlc_lte_channels(rlc_lte_ep_t *rlc_stat_ep, rlc_lte_stat_t *hs)
+{
+ GtkListStore *channels_store = GTK_LIST_STORE(gtk_tree_view_get_model(hs->channel_table));
+ rlc_channel_stats *channel_stats;
+ char buff[32];
+ int n;
+
+ /* Clear any existing rows */
+ gtk_list_store_clear(channels_store);
+ invalidate_channel_iters(hs);
+
+ if (rlc_stat_ep == NULL) {
+ return;
+ }
+
+ /* Add one row for each channel */
+
+ /* CCCH */
+ channel_stats = &rlc_stat_ep->stats.CCCH_stats;
+ if (channel_stats->inUse) {
+
+ if (!channel_stats->iter_valid) {
+ /* Add to list control if not drawn this UE before */
+ gtk_list_store_append(channels_store, &channel_stats->iter);
+ channel_stats->iter_valid = TRUE;
+ }
+
+ /* Set each column for this row */
+ gtk_list_store_set(channels_store, &channel_stats->iter,
+ CHANNEL_NAME, "CCCH",
+ CHANNEL_MODE, print_rlc_channel_mode(channel_stats->rlcMode),
+ CHANNEL_PRIORITY, 0,
+ CHANNEL_UL_FRAMES, channel_stats->UL_frames,
+ CHANNEL_UL_BYTES, channel_stats->UL_bytes,
+ CHANNEL_DL_FRAMES, channel_stats->DL_frames,
+ CHANNEL_DL_BYTES, channel_stats->DL_bytes,
+ CHANNEL_TABLE_COLUMN, channel_stats,
+ -1);
+ }
+
+
+ /* SRB */
+ for (n=0; n < 2; n++) {
+ channel_stats = &rlc_stat_ep->stats.srb_stats[n];
+ if (channel_stats->inUse) {
+
+ /* Calculate bandwidth */
+ float UL_bw = calculate_bw(&channel_stats->UL_time_start,
+ &channel_stats->UL_time_stop,
+ channel_stats->UL_bytes);
+ float DL_bw = calculate_bw(&channel_stats->DL_time_start,
+ &channel_stats->DL_time_stop,
+ channel_stats->DL_bytes);
+
+ if (!channel_stats->iter_valid) {
+ /* Add to list control if not drawn this UE before */
+ gtk_list_store_append(channels_store, &channel_stats->iter);
+ channel_stats->iter_valid = TRUE;
+ }
+
+ g_snprintf(buff, 32, "SRB-%u", n+1);
+
+ /* Set each column for this row */
+ gtk_list_store_set(channels_store, &channel_stats->iter,
+ CHANNEL_NAME, buff,
+ CHANNEL_MODE, print_rlc_channel_mode(channel_stats->rlcMode),
+ CHANNEL_PRIORITY, channel_stats->priority,
+ CHANNEL_UL_FRAMES, channel_stats->UL_frames,
+ CHANNEL_UL_BYTES, channel_stats->UL_bytes,
+ CHANNEL_UL_BW, UL_bw,
+ CHANNEL_UL_ACKS, channel_stats->UL_acks,
+ CHANNEL_UL_NACKS, channel_stats->UL_nacks,
+ CHANNEL_UL_MISSING, channel_stats->UL_missing,
+ CHANNEL_DL_FRAMES, channel_stats->DL_frames,
+ CHANNEL_DL_BYTES, channel_stats->DL_bytes,
+ CHANNEL_DL_BW, DL_bw,
+ CHANNEL_DL_ACKS, channel_stats->DL_acks,
+ CHANNEL_DL_NACKS, channel_stats->DL_nacks,
+ CHANNEL_DL_MISSING, channel_stats->DL_missing,
+ CHANNEL_TABLE_COLUMN, channel_stats,
+ -1);
+ }
+ }
+
+
+ /* DRB */
+ for (n=0; n < 32; n++) {
+ channel_stats = &rlc_stat_ep->stats.drb_stats[n];
+ if (channel_stats->inUse) {
+
+ /* Calculate bandwidth */
+ float UL_bw = calculate_bw(&channel_stats->UL_time_start,
+ &channel_stats->UL_time_stop,
+ channel_stats->UL_bytes);
+ float DL_bw = calculate_bw(&channel_stats->DL_time_start,
+ &channel_stats->DL_time_stop,
+ channel_stats->DL_bytes);
+
+ if (!channel_stats->iter_valid) {
+ /* Add to list control if not drawn this UE before */
+ gtk_list_store_append(channels_store, &channel_stats->iter);
+ channel_stats->iter_valid = TRUE;
+ }
+
+ g_snprintf(buff, 32, "DRB-%u", n+1);
+
+ /* Set each column for this row */
+ gtk_list_store_set(channels_store, &channel_stats->iter,
+ CHANNEL_NAME, buff,
+ CHANNEL_MODE, print_rlc_channel_mode(channel_stats->rlcMode),
+ CHANNEL_PRIORITY, channel_stats->priority,
+ CHANNEL_UL_FRAMES, channel_stats->UL_frames,
+ CHANNEL_UL_BYTES, channel_stats->UL_bytes,
+ CHANNEL_UL_BW, UL_bw,
+ CHANNEL_UL_ACKS, channel_stats->UL_acks,
+ CHANNEL_UL_NACKS, channel_stats->UL_nacks,
+ CHANNEL_UL_MISSING, channel_stats->UL_missing,
+ CHANNEL_DL_FRAMES, channel_stats->DL_frames,
+ CHANNEL_DL_BYTES, channel_stats->DL_bytes,
+ CHANNEL_DL_BW, DL_bw,
+ CHANNEL_DL_ACKS, channel_stats->DL_acks,
+ CHANNEL_DL_NACKS, channel_stats->DL_nacks,
+ CHANNEL_DL_MISSING, channel_stats->DL_missing,
+ CHANNEL_TABLE_COLUMN, channel_stats,
+ -1);
+ }
+ }
+}
+
+
+
+/* (Re)draw the whole dialog window */
+static void
+rlc_lte_stat_draw(void *phs)
+{
+ gchar buff[32];
+ guint16 number_of_ues = 0;
+ gchar title[256];
+
+ /* Look up the statistics window */
+ rlc_lte_stat_t *hs = (rlc_lte_stat_t *)phs;
+ rlc_lte_ep_t* list = hs->ep_list, *tmp = 0;
+
+ GtkListStore *ues_store;
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ rlc_channel_stats *channel_stats = NULL;
+
+ /* Common channel data */
+ g_snprintf(buff, sizeof(buff), "BCCH Frames: %u", hs->common_stats.bcch_frames);
+ gtk_label_set_text(GTK_LABEL(hs->common_bcch_frames), buff);
+ g_snprintf(buff, sizeof(buff), "BCCH Bytes: %u", hs->common_stats.bcch_bytes);
+ gtk_label_set_text(GTK_LABEL(hs->common_bcch_bytes), buff);
+ g_snprintf(buff, sizeof(buff), "PCCH Frames: %u", hs->common_stats.pcch_frames);
+ gtk_label_set_text(GTK_LABEL(hs->common_pcch_frames), buff);
+ g_snprintf(buff, sizeof(buff), "PCCH Bytes: %u", hs->common_stats.pcch_bytes);
+ gtk_label_set_text(GTK_LABEL(hs->common_pcch_bytes), buff);
+
+ /* Per-UE table entries */
+ ues_store = GTK_LIST_STORE(gtk_tree_view_get_model(hs->ue_table));
+
+ /* Set title that shows how many UEs currently in table */
+ for (tmp = list; (tmp!=NULL); tmp=tmp->next, number_of_ues++);
+ g_snprintf(title, sizeof(title), "%u UEs", number_of_ues);
+ gtk_frame_set_label(GTK_FRAME(hs->ues_lb), title);
+
+ /* Update title to include number of UEs and frames */
+ g_snprintf(title, sizeof(title), "Wireshark: LTE RLC Traffic Statistics: %s (%u UEs, %u frames) (filter=\"%s\")",
+ cf_get_display_name(&cfile),
+ number_of_ues,
+ hs->total_frames,
+ strlen(hs->filter) ? hs->filter : "none");
+ gtk_window_set_title(GTK_WINDOW(hs->dlg_w), title);
+
+
+ /* For each row/UE in the model */
+ for (tmp = list; tmp; tmp=tmp->next) {
+ /* Calculate bandwidth */
+ float UL_bw = calculate_bw(&tmp->stats.UL_time_start,
+ &tmp->stats.UL_time_stop,
+ tmp->stats.UL_total_bytes);
+ float DL_bw = calculate_bw(&tmp->stats.DL_time_start,
+ &tmp->stats.DL_time_stop,
+ tmp->stats.DL_total_bytes);
+
+ if (tmp->iter_valid != TRUE) {
+ /* Add to list control if not drawn this UE before */
+ gtk_list_store_append(ues_store, &tmp->iter);
+ tmp->iter_valid = TRUE;
+ }
+
+ /* Set each column for this row */
+ gtk_list_store_set(ues_store, &tmp->iter,
+ UEID_COLUMN, tmp->stats.ueid,
+ UL_FRAMES_COLUMN, tmp->stats.UL_frames,
+ UL_BYTES_COLUMN, tmp->stats.UL_total_bytes,
+ UL_BW_COLUMN, UL_bw,
+ UL_ACKS_COLUMN, tmp->stats.UL_total_acks,
+ UL_NACKS_COLUMN, tmp->stats.UL_total_nacks,
+ UL_MISSING_COLUMN, tmp->stats.UL_total_missing,
+ DL_FRAMES_COLUMN, tmp->stats.DL_frames,
+ DL_BYTES_COLUMN, tmp->stats.DL_total_bytes,
+ DL_BW_COLUMN, DL_bw,
+ DL_ACKS_COLUMN, tmp->stats.DL_total_acks,
+ DL_NACKS_COLUMN, tmp->stats.DL_total_nacks,
+ DL_MISSING_COLUMN, tmp->stats.DL_total_missing,
+ UE_TABLE_COLUMN, tmp,
+ -1);
+ }
+
+ /* Reselect UE? */
+ if (hs->reselect_ue != 0) {
+ GtkTreeIter *ue_iter = NULL;
+ rlc_lte_ep_t *ep = hs->ep_list;
+ while (ep != NULL) {
+ if (ep->stats.ueid == hs->reselect_ue) {
+ ue_iter = &ep->iter;
+ break;
+ }
+ ep = ep->next;
+ }
+ if (ue_iter != NULL) {
+ gtk_tree_selection_select_iter(gtk_tree_view_get_selection(hs->ue_table), ue_iter);
+ }
+ }
+
+ /* If there is a UE selected, update its counters in details window */
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->ue_table));
+ if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ rlc_lte_ep_t *ep;
+
+ gtk_tree_model_get(model, &iter, UE_TABLE_COLUMN, &ep, -1);
+ rlc_lte_channels(ep, hs);
+
+ /* Reselect channel? */
+ switch (hs->reselect_channel_type) {
+ case CHANNEL_TYPE_CCCH:
+ channel_stats = &(ep->stats.CCCH_stats);
+ break;
+ case CHANNEL_TYPE_DRB:
+ channel_stats = &(ep->stats.drb_stats[hs->reselect_channel_id-1]);
+ break;
+ case CHANNEL_TYPE_SRB:
+ channel_stats = &(ep->stats.srb_stats[hs->reselect_channel_id-1]);
+ break;
+ default:
+ break;
+ }
+
+ if ((channel_stats != NULL) && channel_stats->inUse && channel_stats->iter_valid) {
+ gtk_tree_selection_select_iter(gtk_tree_view_get_selection(hs->channel_table), &channel_stats->iter);
+ }
+ }
+}
+
+/* When DCT errors check-box is toggled, enable substring controls accordingly */
+static void rlc_lte_dct_errors_cb(GtkTreeSelection *sel _U_, gpointer data)
+{
+ rlc_lte_stat_t *hs = (rlc_lte_stat_t*)data;
+ guint8 show_dct_errors = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hs->show_dct_errors_cb));
+
+ gtk_widget_set_sensitive(hs->dct_error_substring_lb, show_dct_errors);
+ gtk_widget_set_sensitive(hs->dct_error_substring_te, show_dct_errors);
+}
+
+/* What to do when a UE list item is selected/unselected */
+static void rlc_lte_select_ue_cb(GtkTreeSelection *sel, gpointer data)
+{
+ rlc_lte_ep_t *ep;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ rlc_lte_stat_t *hs = (rlc_lte_stat_t*)data;
+
+ if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ /* Show details of selected UE */
+ gtk_tree_model_get(model, &iter, UE_TABLE_COLUMN, &ep, -1);
+ hs->reselect_ue = ep->stats.ueid;
+ rlc_lte_channels(ep, hs);
+ }
+ else {
+ rlc_lte_channels(NULL, hs);
+ }
+
+ /* Channel will be deselected */
+ enable_filter_controls(FALSE, 0, hs);
+}
+
+
+/* What to do when a channel list item is selected/unselected */
+static void rlc_lte_select_channel_cb(GtkTreeSelection *sel, gpointer data)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ rlc_lte_stat_t *hs = (rlc_lte_stat_t *)data;
+
+ if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ guint16 ueid;
+ guint8 rlcMode;
+
+ /* Remember selected channel */
+ get_channel_selection(hs, &ueid, &rlcMode,
+ &(hs->reselect_channel_type), &(hs->reselect_channel_id));
+
+ /* Enable buttons */
+ enable_filter_controls(TRUE, rlcMode, hs);
+
+ }
+ else {
+ /* No channel selected - disable buttons */
+ enable_filter_controls(FALSE, 0, hs);
+ }
+}
+
+
+/* Destroy the stats window */
+static void win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ rlc_lte_stat_t *hs = (rlc_lte_stat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(hs);
+ unprotect_thread_critical_region();
+
+ if (hs->dlg_w != NULL) {
+ window_destroy(hs->dlg_w);
+ hs->dlg_w = NULL;
+ }
+ rlc_lte_stat_reset(hs);
+ g_free(hs);
+}
+
+
+
+static void
+toggle_show_mac(GtkWidget *widget, gpointer data)
+{
+ rlc_lte_stat_t *hs = (rlc_lte_stat_t *)data;
+
+ /* Read state */
+ hs->show_mac = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
+
+ /* Retap */
+ cf_retap_packets(&cfile);
+}
+
+
+
+/* Check that a UE / channel is currently selected. If so, fill in out
+ parameters with details of channel.
+ Return TRUE if a channel is selected */
+static int get_channel_selection(rlc_lte_stat_t *hs,
+ guint16 *ueid, guint8 *rlcMode,
+ guint16 *channelType, guint16 *channelId)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ /* Check UE selection */
+ GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->ue_table));
+ if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ rlc_lte_ep_t *ep;
+
+ gtk_tree_model_get(model, &iter, UE_TABLE_COLUMN, &ep, -1);
+ *ueid = ep->stats.ueid;
+
+ /* Check channel selection */
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->channel_table));
+ if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
+ /* Find details of selected channel */
+ rlc_channel_stats *channel_stats;
+ gtk_tree_model_get(model, &iter, CHANNEL_TABLE_COLUMN, &channel_stats, -1);
+ *rlcMode = channel_stats->rlcMode;
+ *channelType = channel_stats->channelType;
+ *channelId = channel_stats->channelId;
+ }
+ else {
+ return FALSE;
+ }
+ }
+ else {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/* Build and set a display filter to match the given channel settings */
+typedef enum ChannelDirection_t {UL_Only, DL_Only, UL_and_DL} ChannelDirection_t;
+static void set_channel_filter_expression(guint16 ueid,
+ guint8 rlcMode,
+ guint16 channelType,
+ guint16 channelId,
+ ChannelDirection_t channelDirection,
+ gint filterOnSN,
+ gint statusOnlyPDUs,
+ gint showDCTErrors,
+ const gchar *DCTErrorSubstring,
+ rlc_lte_stat_t *hs)
+{
+ #define MAX_FILTER_LEN 1024
+ static char buffer[MAX_FILTER_LEN];
+ int offset = 0;
+
+ /* Show DCT errors */
+ if (showDCTErrors) {
+ if (strlen(DCTErrorSubstring) > 0) {
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
+ "(dct2000.error-comment and (dct2000.comment contains \"%s\")) or (",
+ DCTErrorSubstring);
+ }
+ else {
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
+ "dct2000.error-comment or (");
+ }
+ }
+
+ /* Include dialog filter */
+ if (strlen(hs->filter)) {
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, "%s and ", hs->filter);
+ }
+
+ /* Should we exclude MAC frames? */
+ if (!hs->show_mac) {
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, "not mac-lte and ");
+ }
+ else {
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, "mac-lte and ");
+ }
+
+ /* UEId */
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, "(rlc-lte.ueid == %u) and ", ueid);
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, "(rlc-lte.channel-type == %u)", channelType);
+
+ /* Channel-id for srb/drb */
+ if ((channelType == CHANNEL_TYPE_SRB) || (channelType == CHANNEL_TYPE_DRB)) {
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, " and (rlc-lte.channel-id == %u)", channelId);
+ }
+
+ /* Direction (also depends upon RLC mode) */
+ switch (channelDirection) {
+ case UL_Only:
+ if (rlcMode == RLC_AM_MODE) {
+ /* Always filter status PDUs */
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
+ " and (rlc-lte.direction == 1 and rlc-lte.am.frame_type == 0)");
+ if (!statusOnlyPDUs) {
+ /* Also filter data */
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
+ " or (rlc-lte.direction == 0 and rlc-lte.am.frame_type == 1)");
+ }
+ }
+ else {
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, " and (rlc-lte.direction == 0)");
+ }
+ break;
+ case DL_Only:
+ if (rlcMode == RLC_AM_MODE) {
+ /* Always filter status PDs */
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
+ " and (rlc-lte.direction == 0 and rlc-lte.am.frame_type == 0)");
+ if (!statusOnlyPDUs) {
+ /* Also filter data */
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
+ " or (rlc-lte.direction == 1 and rlc-lte.am.frame_type == 1)");
+ }
+ }
+ else {
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, " and (rlc-lte.direction == 1)");
+ }
+ break;
+ case UL_and_DL:
+ if (rlcMode == RLC_AM_MODE) {
+ if (statusOnlyPDUs) {
+ g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, " and (rlc-lte.am.frame_type == 0)");
+ }
+ }
+
+ default:
+ break;
+ }
+
+ /* Filter on a specific sequence number */
+ if (filterOnSN != -1) {
+ switch (rlcMode) {
+ case RLC_AM_MODE:
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
+ " and ((rlc-lte.am.fixed.sn == %u) or "
+ "(rlc-lte.am.ack-sn == %u) or "
+ "(rlc-lte.am.nack-sn == %u))",
+ filterOnSN, (filterOnSN+1) % 1024, filterOnSN);
+ break;
+ case RLC_UM_MODE:
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
+ " and (rlc-lte.um.sn == %u)", filterOnSN);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /* Close () if open */
+ if (showDCTErrors) {
+ offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, ")");
+ }
+
+
+ /* Set its value to our new string */
+ gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), buffer);
+
+ /* Run the filter */
+ main_filter_packets(&cfile, buffer, TRUE);
+}
+
+/* Respond to UL filter button being clicked by building and using filter */
+static void ul_filter_clicked(GtkWindow *win _U_, rlc_lte_stat_t* hs)
+{
+ guint16 ueid;
+ guint8 rlcMode;
+ guint16 channelType;
+ guint16 channelId;
+ int sn = -1;
+ const gchar *sn_string = "";
+
+ /* Read SN to filter on (if present) */
+ sn_string = gtk_entry_get_text(GTK_ENTRY(hs->sn_filter_te));
+ if (strlen(sn_string) > 0) {
+ sn = atoi(sn_string);
+ }
+
+ if (!get_channel_selection(hs, &ueid, &rlcMode, &channelType, &channelId)) {
+ return;
+ }
+
+ set_channel_filter_expression(ueid, rlcMode, channelType, channelId, UL_Only, sn,
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hs->show_only_control_pdus_cb)),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hs->show_dct_errors_cb)),
+ gtk_entry_get_text(GTK_ENTRY(hs->dct_error_substring_te)),
+ hs);
+}
+
+/* Respond to DL filter button being clicked by building and using filter */
+static void dl_filter_clicked(GtkWindow *win _U_, rlc_lte_stat_t* hs)
+{
+ guint16 ueid;
+ guint8 rlcMode;
+ guint16 channelType;
+ guint16 channelId;
+ int sn = -1;
+ const gchar *sn_string = "";
+
+ /* Read SN to filter on (if present) */
+ sn_string = gtk_entry_get_text(GTK_ENTRY(hs->sn_filter_te));
+ if (strlen(sn_string) > 0) {
+ sn = atoi(sn_string);
+ }
+
+ if (!get_channel_selection(hs, &ueid, &rlcMode, &channelType, &channelId)) {
+ return;
+ }
+
+ set_channel_filter_expression(ueid, rlcMode, channelType, channelId, DL_Only, sn,
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hs->show_only_control_pdus_cb)),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hs->show_dct_errors_cb)),
+ gtk_entry_get_text(GTK_ENTRY(hs->dct_error_substring_te)),
+ hs);
+}
+
+/* Respond to UL/DL filter button being clicked by building and using filter */
+static void uldl_filter_clicked(GtkWindow *win _U_, rlc_lte_stat_t* hs)
+{
+ guint16 ueid;
+ guint8 rlcMode;
+ guint16 channelType;
+ guint16 channelId;
+ int sn = -1;
+ const gchar *sn_string = "";
+
+ /* Read SN to filter on (if present) */
+ sn_string = gtk_entry_get_text(GTK_ENTRY(hs->sn_filter_te));
+ if (strlen(sn_string) > 0) {
+ sn = atoi(sn_string);
+ }
+
+ if (!get_channel_selection(hs, &ueid, &rlcMode, &channelType, &channelId)) {
+ return;
+ }
+
+ set_channel_filter_expression(ueid, rlcMode, channelType, channelId, UL_and_DL, sn,
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hs->show_only_control_pdus_cb)),
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hs->show_dct_errors_cb)),
+ gtk_entry_get_text(GTK_ENTRY(hs->dct_error_substring_te)),
+ hs);
+}
+
+
+/* Create a new RLC LTE stats dialog */
+static void gtk_rlc_lte_stat_init(const char *optarg, void *userdata _U_)
+{
+ rlc_lte_stat_t *hs;
+ const char *filter = NULL;
+ GString *error_string;
+ GtkWidget *ues_scrolled_window;
+ GtkWidget *channels_scrolled_window;
+ GtkWidget *bbox;
+ GtkWidget *top_level_vbox;
+
+ GtkWidget *pdu_source_lb;
+ GtkWidget *common_channel_lb;
+ GtkWidget *channels_lb;
+ GtkWidget *filter_buttons_lb;
+
+ GtkWidget *common_row_hbox;
+ GtkWidget *show_mac_cb;
+ GtkWidget *ues_vb;
+ GtkWidget *channels_vb;
+ GtkWidget *filter_vb;
+ GtkWidget *filter_buttons_hb;
+ GtkWidget *sn_filter_hb;
+
+ GtkWidget *close_bt;
+ GtkWidget *help_bt;
+ GtkListStore *store;
+
+ GtkTreeView *tree_view;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *sel;
+ gchar title[256];
+ gint i;
+
+ /* Check for a filter string */
+ if (strncmp(optarg, "rlc-lte,stat,", 13) == 0) {
+ /* Skip those characters from filter to display */
+ filter = optarg + 13;
+ }
+ else {
+ /* No filter */
+ filter = NULL;
+ }
+
+
+ /* Create dialog */
+ hs = g_malloc(sizeof(rlc_lte_stat_t));
+ hs->ep_list = NULL;
+
+ /* Copy filter (so can be used for window title at reset) */
+ if (filter) {
+ hs->filter = g_strdup(filter);
+ }
+ else {
+ hs->filter = NULL;
+ }
+
+
+ /* Set title */
+ g_snprintf(title, sizeof(title), "Wireshark: LTE RLC Statistics: %s",
+ cf_get_display_name(&cfile));
+ hs->dlg_w = window_new_with_geom(GTK_WINDOW_TOPLEVEL, title, "LTE RLC Statistics");
+
+ /* Window size */
+ gtk_window_set_default_size(GTK_WINDOW(hs->dlg_w), 750, 300);
+
+ /* Will stack widgets vertically inside dlg */
+ top_level_vbox = gtk_vbox_new(FALSE, 3); /* FALSE = not homogeneous */
+ gtk_container_add(GTK_CONTAINER(hs->dlg_w), top_level_vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(top_level_vbox), 6);
+ gtk_widget_show(top_level_vbox);
+
+ /**********************************************/
+ /* Exclude-MAC checkbox */
+ pdu_source_lb = gtk_frame_new("PDUs to use");
+ show_mac_cb = gtk_check_button_new_with_mnemonic("Show RLC PDUs found inside logged MAC frames");
+ gtk_container_add(GTK_CONTAINER(pdu_source_lb), show_mac_cb);
+ gtk_widget_set_tooltip_text(show_mac_cb, "Can either use separately-logged RLC PDUs, OR find them "
+ "decoded inside MAC PDUs (enabled in MAC dissector preferences)");
+
+
+ /* MAC on by default */
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(show_mac_cb), TRUE);
+ hs->show_mac = TRUE;
+ gtk_box_pack_start(GTK_BOX(top_level_vbox), pdu_source_lb, FALSE, FALSE, 0);
+ /* TODO: add tooltips... */
+ g_signal_connect(show_mac_cb, "toggled", G_CALLBACK(toggle_show_mac), hs);
+
+
+ /**********************************************/
+ /* Common Channel data */
+ /**********************************************/
+ common_channel_lb = gtk_frame_new("Common Channel Data");
+
+ /* Will add BCCH and PCCH counters into one row */
+ common_row_hbox = gtk_hbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(common_channel_lb), common_row_hbox);
+ gtk_container_set_border_width(GTK_CONTAINER(common_row_hbox), 5);
+ gtk_box_pack_start(GTK_BOX(top_level_vbox), common_channel_lb, FALSE, FALSE, 0);
+
+ /* Create labels (that will hold label and counter value) */
+ hs->common_bcch_frames = gtk_label_new("BCCH Frames:");
+ gtk_misc_set_alignment(GTK_MISC(hs->common_bcch_frames), 0.0f, .5f);
+ gtk_container_add(GTK_CONTAINER(common_row_hbox), hs->common_bcch_frames);
+ gtk_widget_show(hs->common_bcch_frames);
+
+ hs->common_bcch_bytes = gtk_label_new("BCCH Bytes:");
+ gtk_misc_set_alignment(GTK_MISC(hs->common_bcch_bytes), 0.0f, .5f);
+ gtk_container_add(GTK_CONTAINER(common_row_hbox), hs->common_bcch_bytes);
+ gtk_widget_show(hs->common_bcch_bytes);
+
+ hs->common_pcch_frames = gtk_label_new("PCCH Frames:");
+ gtk_misc_set_alignment(GTK_MISC(hs->common_pcch_frames), 0.0f, .5f);
+ gtk_container_add(GTK_CONTAINER(common_row_hbox), hs->common_pcch_frames);
+ gtk_widget_show(hs->common_pcch_frames);
+
+ hs->common_pcch_bytes = gtk_label_new("PCCH Bytes:");
+ gtk_misc_set_alignment(GTK_MISC(hs->common_pcch_bytes), 0.0f, .5f);
+ gtk_container_add(GTK_CONTAINER(common_row_hbox), hs->common_pcch_bytes);
+ gtk_widget_show(hs->common_pcch_bytes);
+
+
+ /**********************************************/
+ /* UE List */
+ /**********************************************/
+
+ hs->ues_lb = gtk_frame_new("UE Data (0 UEs)");
+ ues_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(hs->ues_lb), ues_vb);
+ gtk_container_set_border_width(GTK_CONTAINER(ues_vb), 5);
+
+ ues_scrolled_window = scrolled_window_new(NULL, NULL);
+ gtk_box_pack_start(GTK_BOX(ues_vb), ues_scrolled_window, TRUE, TRUE, 0);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(ues_scrolled_window),
+ GTK_SHADOW_IN);
+
+ /* Create the table of UE data */
+ store = gtk_list_store_new(NUM_UE_COLUMNS, G_TYPE_INT,
+ G_TYPE_INT, G_TYPE_INT, G_TYPE_FLOAT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, /* UL */
+ G_TYPE_INT, G_TYPE_INT, G_TYPE_FLOAT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, /* DL */
+ G_TYPE_POINTER);
+ hs->ue_table = GTK_TREE_VIEW(tree_view_new(GTK_TREE_MODEL(store)));
+ gtk_container_add(GTK_CONTAINER (ues_scrolled_window), GTK_WIDGET(hs->ue_table));
+ g_object_unref(G_OBJECT(store));
+
+ tree_view = hs->ue_table;
+ gtk_tree_view_set_headers_visible(tree_view, TRUE);
+ gtk_tree_view_set_headers_clickable(tree_view, TRUE);
+
+ /* Create the titles for each column of the per-UE table */
+ for (i = 0; i < UE_TABLE_COLUMN; i++) {
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(ue_titles[i], renderer,
+ "text", i, NULL);
+ gtk_tree_view_column_set_sort_column_id(column, i);
+
+ if (i == 0) {
+ /* Expand first column (RNTI, which is Key) */
+ gtk_tree_view_column_set_expand(column, TRUE);
+ } else {
+ /* For other columns, set all of the free space to be on the left */
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+ }
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_append_column(tree_view, column);
+ }
+
+ /* Set callback function for selecting a row in the UE table */
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->ue_table));
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+ g_signal_connect(sel, "changed", G_CALLBACK(rlc_lte_select_ue_cb), hs);
+
+ gtk_box_pack_start(GTK_BOX(top_level_vbox), hs->ues_lb, TRUE, TRUE, 0);
+
+
+ /**********************************************/
+ /* Channels of selected UE */
+ /**********************************************/
+ channels_lb = gtk_frame_new("Channels of selected UE");
+
+ channels_vb = gtk_vbox_new(FALSE, 6);
+ gtk_container_add(GTK_CONTAINER(channels_lb), channels_vb);
+ gtk_container_set_border_width(GTK_CONTAINER(channels_vb), 5);
+
+ channels_scrolled_window = scrolled_window_new(NULL, NULL);
+ gtk_box_pack_start(GTK_BOX(channels_vb), channels_scrolled_window, TRUE, TRUE, 0);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(channels_scrolled_window),
+ GTK_SHADOW_IN);
+
+ /* Create the table of UE data */
+ store = gtk_list_store_new(NUM_CHANNEL_COLUMNS,
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, /* name, type, priority */
+ G_TYPE_INT, G_TYPE_INT, G_TYPE_FLOAT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, /* UL */
+ G_TYPE_INT, G_TYPE_INT, G_TYPE_FLOAT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, /* DL */
+ G_TYPE_POINTER);
+ hs->channel_table = GTK_TREE_VIEW(tree_view_new(GTK_TREE_MODEL(store)));
+ gtk_container_add(GTK_CONTAINER (channels_scrolled_window), GTK_WIDGET(hs->channel_table));
+ g_object_unref(G_OBJECT(store));
+
+ tree_view = hs->channel_table;
+ gtk_tree_view_set_headers_visible(tree_view, TRUE);
+ gtk_tree_view_set_headers_clickable(tree_view, TRUE);
+
+ /* Create the titles for each column of the per-UE table */
+ for (i = 0; i < CHANNEL_TABLE_COLUMN; i++) {
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(channel_titles[i], renderer,
+ "text", i, NULL);
+ gtk_tree_view_column_set_sort_column_id(column, i);
+
+ if (i == 0) {
+ /* Expand first column (Type) */
+ gtk_tree_view_column_set_expand(column, TRUE);
+ } else {
+ /* For other columns, set all of the free space to be on the left */
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+ }
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_append_column(tree_view, column);
+ }
+
+ /* Set callback function for selecting a row in the channel table */
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->channel_table));
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+ g_signal_connect(sel, "changed", G_CALLBACK(rlc_lte_select_channel_cb), hs);
+
+ gtk_box_pack_start(GTK_BOX(top_level_vbox), channels_lb, TRUE, TRUE, 0);
+
+
+ /**********************************************/
+ /* Channel filters */
+ /**********************************************/
+
+ filter_buttons_lb = gtk_frame_new("Filter on selected channel");
+
+ filter_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(filter_buttons_lb), filter_vb);
+
+ /* Horizontal row of filter buttons */
+ filter_buttons_hb = gtk_hbox_new(FALSE, 6);
+ gtk_container_add(GTK_CONTAINER(filter_vb), filter_buttons_hb);
+ gtk_container_set_border_width(GTK_CONTAINER(filter_buttons_hb), 2);
+
+ /* UL only */
+ hs->ul_filter_bt = gtk_button_new_with_label("Set UL display filter for this channel");
+ gtk_box_pack_start(GTK_BOX(filter_buttons_hb), hs->ul_filter_bt, TRUE, TRUE, 0);
+ g_signal_connect(hs->ul_filter_bt, "clicked", G_CALLBACK(ul_filter_clicked), hs);
+ gtk_widget_show(hs->ul_filter_bt);
+ gtk_widget_set_tooltip_text(hs->ul_filter_bt, "Generate and set a display filter to show frames "
+ "associated with the channel, in the UL direction only. "
+ "N.B. DL Status PDUs sent on this channel will also be shown for AM");
+
+ /* DL only */
+ hs->dl_filter_bt = gtk_button_new_with_label("Set DL display filter for this channel");
+ gtk_box_pack_start(GTK_BOX(filter_buttons_hb), hs->dl_filter_bt, TRUE, TRUE, 0);
+ g_signal_connect(hs->dl_filter_bt, "clicked", G_CALLBACK(dl_filter_clicked), hs);
+ gtk_widget_show(hs->dl_filter_bt);
+ gtk_widget_set_tooltip_text(hs->dl_filter_bt, "Generate and set a display filter to show frames "
+ "associated with the channel, in the DL direction only. "
+ "N.B. UL Status PDUs sent on this channel will also be shown for AM");
+
+ /* UL and DL */
+ hs->uldl_filter_bt = gtk_button_new_with_label("Set UL / DL display filter for this channel");
+ gtk_box_pack_start(GTK_BOX(filter_buttons_hb), hs->uldl_filter_bt, TRUE, TRUE, 0);
+ g_signal_connect(hs->uldl_filter_bt, "clicked", G_CALLBACK(uldl_filter_clicked), hs);
+ gtk_widget_show(hs->uldl_filter_bt);
+ gtk_widget_set_tooltip_text(hs->uldl_filter_bt, "Generate and set a display filter to show frames "
+ "associated with the channel, in UL and DL");
+
+ /* Allow filtering on specific SN number. */
+ /* Row with label and text entry control */
+ sn_filter_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(filter_vb), sn_filter_hb);
+ gtk_widget_show(sn_filter_hb);
+
+ /* Allow filtering only to select status PDUs for AM */
+ hs->show_only_control_pdus_cb = gtk_check_button_new_with_mnemonic("Show only status PDUs");
+ gtk_container_add(GTK_CONTAINER(sn_filter_hb), hs->show_only_control_pdus_cb);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hs->show_only_control_pdus_cb), FALSE);
+ gtk_widget_set_tooltip_text(hs->show_only_control_pdus_cb, "Generated filters will only show AM status PDUs "
+ "(i.e. if you filter on UL you'll see ACKs/NACK replies sent in the DL)");
+
+ /* Allow DCT errors to be shown... */
+ hs->show_dct_errors_cb = gtk_check_button_new_with_mnemonic("Show DCT2000 error strings...");
+ gtk_container_add(GTK_CONTAINER(sn_filter_hb), hs->show_dct_errors_cb);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hs->show_dct_errors_cb), FALSE);
+ g_signal_connect(hs->show_dct_errors_cb, "toggled", G_CALLBACK(rlc_lte_dct_errors_cb), hs);
+ gtk_widget_set_tooltip_text(hs->show_dct_errors_cb, "When checked, generated filters will "
+ "include DCT2000 error strings");
+
+ /* ... optionally limited by a substring */
+ hs->dct_error_substring_lb = gtk_label_new("...containing");
+ gtk_box_pack_start(GTK_BOX(sn_filter_hb), hs->dct_error_substring_lb, FALSE, FALSE, 0);
+ gtk_widget_show(hs->dct_error_substring_lb);
+
+ hs->dct_error_substring_te = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(sn_filter_hb), hs->dct_error_substring_te, FALSE, FALSE, 0);
+ gtk_widget_show(hs->dct_error_substring_te);
+ gtk_widget_set_tooltip_text(hs->dct_error_substring_te,
+ "If given, only match error strings containing this substring");
+
+ /* Allow filtering of a particular sequence number */
+ hs->sn_filter_te = gtk_entry_new();
+ gtk_box_pack_end(GTK_BOX(sn_filter_hb), hs->sn_filter_te, FALSE, FALSE, 0);
+ gtk_widget_show(hs->sn_filter_te);
+ gtk_widget_set_tooltip_text(hs->sn_filter_te, "Can limit generated filters to a given sequence number (0-1023). "
+ "Will also include relevant AM status PDUs");
+
+ hs->sn_filter_lb = gtk_label_new("Sequence number to filter on:");
+ gtk_box_pack_end(GTK_BOX(sn_filter_hb), hs->sn_filter_lb, FALSE, FALSE, 0);
+ gtk_widget_show(hs->sn_filter_lb);
+
+
+ /* Add filters box to top-level window */
+ gtk_box_pack_start(GTK_BOX(top_level_vbox), filter_buttons_lb, FALSE, FALSE, 0);
+
+ enable_filter_controls(FALSE, 0, hs);
+
+ /**********************************************/
+ /* Register the tap listener */
+ /**********************************************/
+
+ error_string = register_tap_listener("rlc-lte", hs,
+ filter, 0,
+ rlc_lte_stat_reset,
+ rlc_lte_stat_packet,
+ rlc_lte_stat_draw);
+ if (error_string) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(hs);
+ return;
+ }
+
+
+ /************************************/
+ /* Bottom button row. */
+ /************************************/
+
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
+ gtk_box_pack_end(GTK_BOX(top_level_vbox), bbox, FALSE, FALSE, 0);
+
+ /* Add the close button */
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(hs->dlg_w, close_bt, window_cancel_button_cb);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_LTE_RLC_TRAFFIC_DIALOG);
+
+ /* Set callbacks */
+ g_signal_connect(hs->dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(hs->dlg_w, "destroy", G_CALLBACK(win_destroy_cb), hs);
+
+ /* Show the window */
+ gtk_widget_show_all(hs->dlg_w);
+ window_present(hs->dlg_w);
+
+ /* Retap */
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(hs->dlg_w));
+}
+
+
+static tap_param rlc_lte_stat_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg rlc_lte_stat_dlg = {
+ "LTE RLC Stats",
+ "rlc-lte,stat",
+ gtk_rlc_lte_stat_init,
+ -1,
+ G_N_ELEMENTS(rlc_lte_stat_params),
+ rlc_lte_stat_params
+};
+
+
+/* Register this tap listener (need void on own so line register function found) */
+void
+register_tap_listener_rlc_lte_stat(void)
+{
+ register_dfilter_stat(&rlc_lte_stat_dlg, "_LTE/_RLC", REGISTER_STAT_GROUP_TELEPHONY);
+}
+
+void rlc_lte_stat_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &rlc_lte_stat_dlg);
+}
+
diff --git a/ui/gtk/rpc_progs.c b/ui/gtk/rpc_progs.c
new file mode 100644
index 0000000000..b385860b12
--- /dev/null
+++ b/ui/gtk/rpc_progs.c
@@ -0,0 +1,424 @@
+/* rpc_progs.c
+ * rpc_progs 2002 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* This module provides rpc call/reply SRT statistics to Wireshark.
+ * It is only used by Wireshark and not TShark
+ *
+ * It serves as an example on how to use the tap api.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-rpc.h>
+
+#include "../stat_menu.h"
+#include "../globals.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/main.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+#define NANOSECS_PER_SEC 1000000000
+
+static GtkWidget *win=NULL;
+static GtkWidget *table=NULL;
+static int num_progs=0;
+
+/* used to keep track of statistics for a specific program/version */
+typedef struct _rpc_program_t {
+ struct _rpc_program_t *next;
+ guint32 program;
+ GtkWidget *wprogram;
+ gchar sprogram[24];
+
+ guint32 version;
+ GtkWidget *wversion;
+ gchar sversion[16];
+
+ int num;
+ GtkWidget *wnum;
+ gchar snum[16];
+
+ nstime_t min;
+ GtkWidget *wmin;
+ gchar smin[16];
+
+ nstime_t max;
+ GtkWidget *wmax;
+ gchar smax[16];
+
+ nstime_t tot;
+ GtkWidget *wavg;
+ gchar savg[16];
+} rpc_program_t;
+
+static rpc_program_t *prog_list=NULL;
+
+
+static char *
+rpcprogs_gen_title(void)
+{
+ char *title;
+
+ title = g_strdup_printf("ONC-RPC Program Statistics: %s",
+ cf_get_display_name(&cfile));
+ return title;
+}
+
+static void
+rpcprogs_reset(void *dummy _U_)
+{
+ rpc_program_t *rp;
+
+ while(prog_list){
+ rp=prog_list;
+ prog_list=prog_list->next;
+
+ gtk_widget_destroy(rp->wprogram);
+ rp->wprogram=NULL;
+ gtk_widget_destroy(rp->wversion);
+ rp->wversion=NULL;
+ gtk_widget_destroy(rp->wnum);
+ rp->wnum=NULL;
+ gtk_widget_destroy(rp->wmin);
+ rp->wmin=NULL;
+ gtk_widget_destroy(rp->wmax);
+ rp->wmax=NULL;
+ gtk_widget_destroy(rp->wavg);
+ rp->wavg=NULL;
+ g_free(rp);
+ }
+ gtk_table_resize(GTK_TABLE(table), 1, 6);
+ num_progs=0;
+}
+
+static void
+add_new_program(rpc_program_t *rp)
+{
+ num_progs++;
+ gtk_table_resize(GTK_TABLE(table), num_progs+1, 6);
+ rp->wprogram=gtk_label_new("0");
+ gtk_table_attach_defaults(GTK_TABLE(table), rp->wprogram, 0,1,num_progs,num_progs+1);
+ gtk_widget_show(rp->wprogram);
+ rp->wversion=gtk_label_new("0");
+ gtk_table_attach_defaults(GTK_TABLE(table), rp->wversion, 1,2,num_progs,num_progs+1);
+ gtk_widget_show(rp->wversion);
+ rp->wnum=gtk_label_new("0");
+ gtk_table_attach_defaults(GTK_TABLE(table), rp->wnum, 2,3,num_progs,num_progs+1);
+ gtk_widget_show(rp->wnum);
+ rp->wmin=gtk_label_new("0");
+ gtk_table_attach_defaults(GTK_TABLE(table), rp->wmin, 3,4,num_progs,num_progs+1);
+ gtk_widget_show(rp->wmin);
+ rp->wmax=gtk_label_new("0");
+ gtk_table_attach_defaults(GTK_TABLE(table), rp->wmax, 4,5,num_progs,num_progs+1);
+ gtk_widget_show(rp->wmax);
+ rp->wavg=gtk_label_new("0");
+ gtk_table_attach_defaults(GTK_TABLE(table), rp->wavg, 5,6,num_progs,num_progs+1);
+ gtk_widget_show(rp->wavg);
+
+ rp->num=0;
+ rp->min.secs=0;
+ rp->min.nsecs=0;
+ rp->max.secs=0;
+ rp->max.nsecs=0;
+ rp->tot.secs=0;
+ rp->tot.nsecs=0;
+}
+
+
+
+static gboolean
+rpcprogs_packet(void *dummy _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *arg)
+{
+ const rpc_call_info_value *ri = arg;
+ nstime_t delta;
+ rpc_program_t *rp;
+
+ if(!prog_list){
+ /* the list was empty */
+ rp=g_malloc(sizeof(rpc_program_t));
+ add_new_program(rp);
+ rp->next=NULL;
+ rp->program=ri->prog;
+ rp->version=ri->vers;
+ prog_list=rp;
+ } else if((ri->prog==prog_list->program)
+ &&(ri->vers==prog_list->version)){
+ rp=prog_list;
+ } else if( (ri->prog<prog_list->program)
+ ||((ri->prog==prog_list->program)&&(ri->vers<prog_list->version))){
+ /* we should be first entry in list */
+ rp=g_malloc(sizeof(rpc_program_t));
+ add_new_program(rp);
+ rp->next=prog_list;
+ rp->program=ri->prog;
+ rp->version=ri->vers;
+ prog_list=rp;
+ } else {
+ /* we go somewhere else in the list */
+ for(rp=prog_list;rp;rp=rp->next){
+ if((rp->next)
+ && (rp->next->program==ri->prog)
+ && (rp->next->version==ri->vers)){
+ rp=rp->next;
+ break;
+ }
+ if((!rp->next)
+ || (rp->next->program>ri->prog)
+ || ( (rp->next->program==ri->prog)
+ &&(rp->next->version>ri->vers))){
+ rpc_program_t *trp;
+ trp=g_malloc(sizeof(rpc_program_t));
+ add_new_program(trp);
+ trp->next=rp->next;
+ trp->program=ri->prog;
+ trp->version=ri->vers;
+ rp->next=trp;
+ rp=trp;
+ break;
+ }
+ }
+ }
+
+
+ /* we are only interested in reply packets */
+ if(ri->request || !rp){
+ return FALSE;
+ }
+
+ /* calculate time delta between request and reply */
+ nstime_delta(&delta, &pinfo->fd->abs_ts, &ri->req_time);
+
+ if((rp->max.secs==0)
+ && (rp->max.nsecs==0) ){
+ rp->max.secs=delta.secs;
+ rp->max.nsecs=delta.nsecs;
+ }
+
+ if((rp->min.secs==0)
+ && (rp->min.nsecs==0) ){
+ rp->min.secs=delta.secs;
+ rp->min.nsecs=delta.nsecs;
+ }
+
+ if( (delta.secs<rp->min.secs)
+ ||( (delta.secs==rp->min.secs)
+ &&(delta.nsecs<rp->min.nsecs) ) ){
+ rp->min.secs=delta.secs;
+ rp->min.nsecs=delta.nsecs;
+ }
+
+ if( (delta.secs>rp->max.secs)
+ ||( (delta.secs==rp->max.secs)
+ &&(delta.nsecs>rp->max.nsecs) ) ){
+ rp->max.secs=delta.secs;
+ rp->max.nsecs=delta.nsecs;
+ }
+
+ rp->tot.secs += delta.secs;
+ rp->tot.nsecs += delta.nsecs;
+ if(rp->tot.nsecs>NANOSECS_PER_SEC){
+ rp->tot.nsecs-=NANOSECS_PER_SEC;
+ rp->tot.secs++;
+ }
+ rp->num++;
+
+ return TRUE;
+}
+
+
+static void
+rpcprogs_draw(void *dummy _U_)
+{
+ rpc_program_t *rp;
+ int i;
+ guint64 td;
+
+ for(rp=prog_list,i=1;rp;rp=rp->next,i++){
+ /* Ignore procedures with no calls */
+ if(rp->num==0){
+ td=0;
+ continue;
+ }
+ /* Scale the average SRT in units of 1us and round to the nearest us.
+ tot.secs is a time_t which may be 32 or 64 bits (or even floating)
+ depending on the platform. After casting tot.secs to a 64 bits int, it
+ would take a capture with a duration of over 136 *years* to
+ overflow the secs portion of td. */
+ td = ((guint64)(rp->tot.secs))*NANOSECS_PER_SEC + rp->tot.nsecs;
+ td = ((td / rp->num) + 500) / 1000;
+
+ g_snprintf(rp->sprogram, sizeof(rp->sprogram), "%s",rpc_prog_name(rp->program));
+ gtk_label_set_text(GTK_LABEL(rp->wprogram), rp->sprogram);
+
+ g_snprintf(rp->sversion, sizeof(rp->sversion), "%d",rp->version);
+ gtk_label_set_text(GTK_LABEL(rp->wversion), rp->sversion);
+
+ g_snprintf(rp->snum, sizeof(rp->snum), "%d",rp->num);
+ gtk_label_set_text(GTK_LABEL(rp->wnum), rp->snum);
+
+ g_snprintf(rp->smin, sizeof(rp->smin), "%3d.%06d",(int)rp->min.secs, (rp->min.nsecs+500)/1000);
+ gtk_label_set_text(GTK_LABEL(rp->wmin), rp->smin);
+
+ g_snprintf(rp->smax, sizeof(rp->smax), "%3d.%06d",(int)rp->max.secs, (rp->max.nsecs+500)/1000);
+ gtk_label_set_text(GTK_LABEL(rp->wmax), rp->smax);
+
+ g_snprintf(rp->savg, sizeof(rp->savg), "%3d.%06d",(int)(td/1000000),(int)(td%1000000));
+ gtk_label_set_text(GTK_LABEL(rp->wavg), rp->savg);
+
+ }
+}
+
+/* since the gtk2 implementation of tap is multithreaded we must protect
+ * remove_tap_listener() from modifying the list while draw_tap_listener()
+ * is running. the other protected block is in main.c
+ *
+ * there should not be any other critical regions in gtk2
+ */
+static void
+win_destroy_cb(void *dummy _U_, gpointer data _U_)
+{
+ rpc_program_t *rp, *rp2;
+
+ protect_thread_critical_region();
+ remove_tap_listener(win);
+ unprotect_thread_critical_region();
+
+ win=NULL;
+ for(rp=prog_list;rp;){
+ rp2=rp->next;
+ g_free(rp);
+ rp=rp2;
+ }
+ prog_list=NULL;
+}
+
+
+/* When called, this function will start rpcprogs
+ */
+static void
+gtk_rpcprogs_init(const char *optarg _U_, void* userdata _U_)
+{
+ char *title_string;
+ GtkWidget *vbox;
+ GtkWidget *stat_label;
+ GtkWidget *tmp;
+ GString *error_string;
+ GtkWidget *bt_close;
+ GtkWidget *bbox;
+
+ if(win){
+ gdk_window_raise(gtk_widget_get_window(win));
+ return;
+ }
+
+ title_string = rpcprogs_gen_title();
+ win = dlg_window_new(title_string); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(win), TRUE);
+
+ vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ stat_label=gtk_label_new(title_string);
+ g_free(title_string);
+ gtk_box_pack_start(GTK_BOX(vbox), stat_label, FALSE, FALSE, 0);
+
+
+ table=gtk_table_new(1, 5, TRUE);
+ gtk_container_add(GTK_CONTAINER(vbox), table);
+
+ tmp=gtk_label_new("Program");
+ gtk_table_attach_defaults(GTK_TABLE(table), tmp, 0,1,0,1);
+ gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
+
+ tmp=gtk_label_new("Version");
+ gtk_table_attach_defaults(GTK_TABLE(table), tmp, 1,2,0,1);
+ gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_RIGHT);
+
+ tmp=gtk_label_new("Calls");
+ gtk_table_attach_defaults(GTK_TABLE(table), tmp, 2,3,0,1);
+ gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_RIGHT);
+
+ tmp=gtk_label_new("Min SRT");
+ gtk_table_attach_defaults(GTK_TABLE(table), tmp, 3,4,0,1);
+ gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_RIGHT);
+
+ tmp=gtk_label_new("Max SRT");
+ gtk_table_attach_defaults(GTK_TABLE(table), tmp, 4,5,0,1);
+ gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_RIGHT);
+
+ tmp=gtk_label_new("Avg SRT");
+ gtk_table_attach_defaults(GTK_TABLE(table), tmp, 5,6,0,1);
+ gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_RIGHT);
+
+ error_string=register_tap_listener("rpc", win, NULL, 0, rpcprogs_reset, rpcprogs_packet, rpcprogs_draw);
+ if(error_string){
+ fprintf(stderr, "wireshark: Couldn't register rpc,programs tap: %s\n",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ bt_close = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(win, bt_close, window_cancel_button_cb);
+
+ g_signal_connect(win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(win, "destroy", G_CALLBACK(win_destroy_cb), NULL);
+
+ gtk_widget_show_all(win);
+ window_present(win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(win));
+}
+
+void
+gtk_rpcprogs_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ gtk_rpcprogs_init("",NULL);
+}
+
+void
+register_tap_listener_gtkrpcprogs(void)
+{
+ register_stat_cmd_arg("rpc,programs", gtk_rpcprogs_init,NULL);
+}
diff --git a/ui/gtk/rpc_stat.c b/ui/gtk/rpc_stat.c
new file mode 100644
index 0000000000..b4b6ddfc9c
--- /dev/null
+++ b/ui/gtk/rpc_stat.c
@@ -0,0 +1,537 @@
+/* rpc_stat.c
+ * rpc_stat 2002 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* This module provides rpc call/reply SRT (Server Response Time) statistics
+ * to Wireshark.
+ *
+ * It serves as an example on how to use the tap api.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-rpc.h>
+
+#include "../stat_menu.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/service_response_time_table.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/filter_autocomplete.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _rpcstat_t {
+ GtkWidget *win;
+ srt_stat_table srt_table;
+ const char *prog;
+ guint32 program;
+ guint32 version;
+ guint32 num_procedures;
+} rpcstat_t;
+
+static char *
+rpcstat_gen_title(rpcstat_t *rs)
+{
+ char *title;
+
+ title = g_strdup_printf("ONC-RPC Service Response Time statistics for %s version %d: %s",
+ rs->prog, rs->version, cf_get_display_name(&cfile));
+ return title;
+}
+
+static void
+rpcstat_set_title(rpcstat_t *rs)
+{
+ char *title;
+
+ title = rpcstat_gen_title(rs);
+ gtk_window_set_title(GTK_WINDOW(rs->win), title);
+ g_free(title);
+}
+
+static void
+rpcstat_reset(void *arg)
+{
+ rpcstat_t *rs = arg;
+
+ reset_srt_table_data(&rs->srt_table);
+ rpcstat_set_title(rs);
+}
+
+
+static gboolean
+rpcstat_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *arg2)
+{
+ rpcstat_t *rs = arg;
+ const rpc_call_info_value *ri = arg2;
+
+ /* we are only interested in reply packets */
+ if(ri->request){
+ return FALSE;
+ }
+ /* we are only interested in certain program/versions */
+ if( (ri->prog!=rs->program) || (ri->vers!=rs->version) ){
+ return FALSE;
+ }
+ /* maybe we have discovered a new procedure?
+ * then we might need to extend our tables
+ */
+ if(ri->proc>=rs->num_procedures){
+ guint32 i;
+ if(ri->proc>256){
+ /* no program have probably ever more than this many
+ * procedures anyway and it prevents us from allocating
+ * infinite memory if passed a garbage procedure id
+ */
+ return FALSE;
+ }
+ for(i=rs->num_procedures;i<=ri->proc;i++){
+ init_srt_table_row(&rs->srt_table, i, rpc_proc_name(rs->program, rs->version, i));
+ }
+ rs->num_procedures=ri->proc+1;
+ }
+ add_srt_table_data(&rs->srt_table, ri->proc, &ri->req_time, pinfo);
+
+ return TRUE;
+}
+
+static void
+rpcstat_draw(void *arg)
+{
+ rpcstat_t *rs = arg;
+
+ draw_srt_table_data(&rs->srt_table);
+}
+
+
+
+static guint32 rpc_program=0;
+static guint32 rpc_version=0;
+static gint32 rpc_min_vers=-1;
+static gint32 rpc_max_vers=-1;
+static gint32 rpc_min_proc=-1;
+static gint32 rpc_max_proc=-1;
+
+static void
+rpcstat_find_procs(gpointer *key, gpointer *value _U_, gpointer *user_data _U_)
+{
+ rpc_proc_info_key *k=(rpc_proc_info_key *)key;
+
+ if(k->prog!=rpc_program){
+ return;
+ }
+ if(k->vers!=rpc_version){
+ return;
+ }
+ if(rpc_min_proc==-1){
+ rpc_min_proc=k->proc;
+ rpc_max_proc=k->proc;
+ }
+ if((gint32)k->proc<rpc_min_proc){
+ rpc_min_proc=k->proc;
+ }
+ if((gint32)k->proc>rpc_max_proc){
+ rpc_max_proc=k->proc;
+ }
+
+ return;
+}
+
+static void
+rpcstat_find_vers(gpointer *key, gpointer *value _U_, gpointer *user_data _U_)
+{
+ rpc_proc_info_key *k=(rpc_proc_info_key *)key;
+
+ if(k->prog!=rpc_program){
+ return;
+ }
+ if(rpc_min_vers==-1){
+ rpc_min_vers=k->vers;
+ rpc_max_vers=k->vers;
+ }
+ if((gint32)k->vers<rpc_min_vers){
+ rpc_min_vers=k->vers;
+ }
+ if((gint32)k->vers>rpc_max_vers){
+ rpc_max_vers=k->vers;
+ }
+
+ return;
+}
+
+/* since the gtk2 implementation of tap is multithreaded we must protect
+ * remove_tap_listener() from modifying the list while draw_tap_listener()
+ * is running. the other protected block is in main.c
+ *
+ * there should not be any other critical regions in gtk2
+ */
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ rpcstat_t *rs=(rpcstat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(rs);
+ unprotect_thread_critical_region();
+
+ free_srt_table_data(&rs->srt_table);
+ g_free(rs);
+}
+
+/* When called, this function will create a new instance of gtk2-rpcstat.
+ */
+static void
+gtk_rpcstat_init(const char *optarg, void* userdata _U_)
+{
+ rpcstat_t *rs;
+ guint32 i;
+ char *title_string;
+ char *filter_string;
+ GtkWidget *vbox;
+ GtkWidget *stat_label;
+ GtkWidget *filter_label;
+ GtkWidget *bbox;
+ GtkWidget *close_bt;
+ int program, version, pos;
+ const char *filter=NULL;
+ GString *error_string;
+ int hf_index;
+ header_field_info *hfi;
+
+ pos=0;
+ if(sscanf(optarg,"rpc,srt,%d,%d,%n",&program,&version,&pos)==2){
+ if(pos){
+ filter=optarg+pos;
+ } else {
+ filter=NULL;
+ }
+ } else {
+ fprintf(stderr, "wireshark: invalid \"-z rpc,srt,<program>,<version>[,<filter>]\" argument\n");
+ exit(1);
+ }
+
+ rpc_program=program;
+ rpc_version=version;
+ rs=g_malloc(sizeof(rpcstat_t));
+ rs->prog=rpc_prog_name(rpc_program);
+ rs->program=rpc_program;
+ rs->version=rpc_version;
+ hf_index=rpc_prog_hf(rpc_program, rpc_version);
+ hfi=proto_registrar_get_nth(hf_index);
+
+ rs->win = dlg_window_new("rpc-stat"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(rs->win), TRUE);
+ gtk_window_set_default_size(GTK_WINDOW(rs->win), 550, 400);
+ rpcstat_set_title(rs);
+
+ vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(rs->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ title_string = rpcstat_gen_title(rs);
+ stat_label=gtk_label_new(title_string);
+ g_free(title_string);
+ gtk_box_pack_start(GTK_BOX(vbox), stat_label, FALSE, FALSE, 0);
+
+ filter_string = g_strdup_printf("Filter: %s", filter ? filter : "");
+ filter_label=gtk_label_new(filter_string);
+ g_free(filter_string);
+ gtk_label_set_line_wrap(GTK_LABEL(filter_label), TRUE);
+ gtk_box_pack_start(GTK_BOX(vbox), filter_label, FALSE, FALSE, 0);
+
+ rpc_min_proc=-1;
+ rpc_max_proc=-1;
+ g_hash_table_foreach(rpc_procs, (GHFunc)rpcstat_find_procs, NULL);
+ rs->num_procedures=rpc_max_proc+1;
+
+ /* We must display TOP LEVEL Widget before calling init_srt_table() */
+ gtk_widget_show_all(rs->win);
+
+ init_srt_table(&rs->srt_table, rpc_max_proc+1, vbox, hfi->abbrev);
+
+ for(i=0;i<rs->num_procedures;i++){
+ init_srt_table_row(&rs->srt_table, i, rpc_proc_name(rpc_program, rpc_version, i));
+ }
+
+
+ error_string=register_tap_listener("rpc", rs, filter, 0, rpcstat_reset, rpcstat_packet, rpcstat_draw);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ free_srt_table_data(&rs->srt_table);
+ g_free(rs);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(rs->win, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(rs->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(rs->win, "destroy", G_CALLBACK(win_destroy_cb), rs);
+
+ gtk_widget_show_all(rs->win);
+ window_present(rs->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(rs->win));
+}
+
+
+
+
+static GtkWidget *dlg=NULL;
+static GtkWidget *filter_entry;
+
+static void
+rpcstat_start_button_clicked(GtkWidget *item _U_, gpointer data _U_)
+{
+ GString *str;
+ const char *filter;
+
+ str = g_string_new("rpc,srt");
+ g_string_append_printf(str, ",%d,%d", rpc_program, rpc_version);
+ filter=gtk_entry_get_text(GTK_ENTRY(filter_entry));
+ if(filter[0]!=0){
+ g_string_append_printf(str, ",%s", filter);
+ }
+
+ gtk_rpcstat_init(str->str,NULL);
+ g_string_free(str, TRUE);
+}
+
+
+static void
+rpcstat_version_select(GtkWidget *vers_combo_box, gpointer user_data _U_)
+{
+ gpointer ptr;
+
+ if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(vers_combo_box), &ptr)) {
+ g_assert_not_reached(); /* Programming error: somehow no active item */
+ }
+
+ rpc_version=GPOINTER_TO_INT(ptr);
+}
+
+
+static void
+rpcstat_program_select(GtkWidget *prog_combo_box, gpointer user_data)
+{
+ rpc_prog_info_key *k;
+ GtkWidget *vers_combo_box;
+ int i;
+
+ vers_combo_box = user_data;
+
+ if (! ws_combo_box_get_active_pointer(GTK_COMBO_BOX(prog_combo_box), (gpointer)&k)) {
+ g_assert_not_reached(); /* Programming error: somehow no active item */
+ }
+ rpc_program=k->prog;
+
+ /* re-create version menu */
+ rpc_version=0;
+ g_signal_handlers_disconnect_by_func(vers_combo_box, G_CALLBACK(rpcstat_version_select), NULL );
+ ws_combo_box_clear_text_and_pointer(GTK_COMBO_BOX(vers_combo_box));
+ rpc_min_vers=-1;
+ rpc_max_vers=-1;
+ g_hash_table_foreach(rpc_procs, (GHFunc)rpcstat_find_vers, NULL);
+ for(i=rpc_min_vers;i<=rpc_max_vers;i++){
+ char vs[5];
+ g_snprintf(vs, sizeof(vs), "%d",i);
+ ws_combo_box_append_text_and_pointer(GTK_COMBO_BOX(vers_combo_box),
+ vs, GINT_TO_POINTER(i));
+ }
+ g_signal_connect(vers_combo_box, "changed", G_CALLBACK(rpcstat_version_select), NULL);
+ ws_combo_box_set_active(GTK_COMBO_BOX(vers_combo_box), 0); /* default: will trigger rpcstat_version_select callback */
+}
+
+
+
+static void
+rpcstat_list_programs(gpointer *key, gpointer *value, gpointer user_data)
+{
+ rpc_prog_info_key *k=(rpc_prog_info_key *)key;
+ rpc_prog_info_value *v=(rpc_prog_info_value *)value;
+ GtkComboBox *prog_combo_box = user_data;
+
+ ws_combo_box_append_text_and_pointer(prog_combo_box, v->progname, k);
+
+ if(!rpc_program){
+ rpc_program=k->prog;
+ }
+}
+
+static void
+dlg_destroy_cb(GtkWidget *w _U_, gpointer user_data _U_)
+{
+ dlg=NULL;
+}
+
+void
+gtk_rpcstat_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ GtkWidget *dlg_box;
+ GtkWidget *prog_box, *prog_label;
+ GtkWidget *vers_label;
+ GtkWidget *prog_combo_box, *vers_combo_box;
+ GtkWidget *filter_box, *filter_bt;
+ GtkWidget *bbox, *start_button, *cancel_button;
+ const char *filter;
+ static construct_args_t args = {
+ "Service Response Time Statistics Filter",
+ TRUE,
+ FALSE,
+ FALSE
+ };
+
+ /* if the window is already open, bring it to front */
+ if(dlg){
+ gdk_window_raise(gtk_widget_get_window(dlg));
+ return;
+ }
+
+ dlg=dlg_window_new("Wireshark: Compute ONC-RPC SRT statistics");
+ gtk_window_set_default_size(GTK_WINDOW(dlg), 300, -1);
+
+ dlg_box=gtk_vbox_new(FALSE, 10);
+ gtk_container_set_border_width(GTK_CONTAINER(dlg_box), 10);
+ gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
+ gtk_widget_show(dlg_box);
+
+ /* Program box */
+ prog_box=gtk_hbox_new(FALSE, 10);
+
+ /* Program label */
+ gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
+ prog_label=gtk_label_new("Program:");
+ gtk_box_pack_start(GTK_BOX(prog_box), prog_label, FALSE, FALSE, 0);
+ gtk_widget_show(prog_label);
+
+ /* Program menu */
+ prog_combo_box=ws_combo_box_new_text_and_pointer();
+ g_hash_table_foreach(rpc_progs, (GHFunc)rpcstat_list_programs, prog_combo_box);
+ gtk_box_pack_start(GTK_BOX(prog_box), prog_combo_box, TRUE, TRUE, 0);
+ gtk_widget_show(prog_combo_box);
+
+ /* Version label */
+ gtk_container_set_border_width(GTK_CONTAINER(prog_box), 10);
+ vers_label=gtk_label_new("Version:");
+ gtk_box_pack_start(GTK_BOX(prog_box), vers_label, FALSE, FALSE, 0);
+ gtk_widget_show(vers_label);
+
+ /* Note: version combo box rows set when rpcstat_program_select callback invoked below */
+ vers_combo_box=ws_combo_box_new_text_and_pointer();
+ gtk_box_pack_start(GTK_BOX(prog_box), vers_combo_box, TRUE, TRUE, 0);
+ gtk_widget_show(vers_combo_box);
+
+ gtk_box_pack_start(GTK_BOX(dlg_box), prog_box, TRUE, TRUE, 0);
+
+ g_signal_connect(prog_combo_box, "changed", G_CALLBACK(rpcstat_program_select), vers_combo_box);
+ ws_combo_box_set_active(GTK_COMBO_BOX(prog_combo_box), 0); /* invokes rpcstat_program_select callback */
+
+ gtk_widget_show(prog_box);
+
+ /* Filter box */
+ filter_box=gtk_hbox_new(FALSE, 3);
+
+ /* Filter label */
+ filter_bt=gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
+ g_signal_connect(filter_bt, "clicked", G_CALLBACK(display_filter_construct_cb), &args);
+ gtk_box_pack_start(GTK_BOX(filter_box), filter_bt, FALSE, FALSE, 0);
+ gtk_widget_show(filter_bt);
+
+ /* Filter entry */
+ filter_entry=gtk_entry_new();
+ g_signal_connect(filter_entry, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
+ g_object_set_data(G_OBJECT(filter_box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+ g_signal_connect(filter_entry, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
+ g_signal_connect(dlg, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
+
+ /* filter prefs dialog */
+ g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, filter_entry);
+ /* filter prefs dialog */
+
+ gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, TRUE, TRUE, 0);
+ filter=gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
+ if(filter){
+ gtk_entry_set_text(GTK_ENTRY(filter_entry), filter);
+ } else {
+ colorize_filter_te_as_empty(filter_entry);
+ }
+ gtk_widget_show(filter_entry);
+
+ gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
+ gtk_widget_show(filter_box);
+
+ /* button box */
+ bbox = dlg_button_row_new(WIRESHARK_STOCK_CREATE_STAT, GTK_STOCK_CANCEL, NULL);
+ gtk_box_pack_start(GTK_BOX(dlg_box), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+
+ start_button = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_CREATE_STAT);
+ g_signal_connect_swapped(start_button, "clicked",
+ G_CALLBACK(rpcstat_start_button_clicked), NULL);
+
+ cancel_button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ window_set_cancel_button(dlg, cancel_button, window_cancel_button_cb);
+
+ /* Give the initial focus to the "Filter" entry box. */
+ gtk_widget_grab_focus(filter_entry);
+
+ gtk_widget_grab_default(start_button );
+
+ g_signal_connect(dlg, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(dlg, "destroy", G_CALLBACK(dlg_destroy_cb), NULL);
+
+ gtk_widget_show_all(dlg);
+ window_present(dlg);
+}
+
+
+void
+register_tap_listener_gtkrpcstat(void)
+{
+ register_stat_cmd_arg("rpc,srt,", gtk_rpcstat_init, NULL);
+}
+
diff --git a/ui/gtk/rtp_analysis.c b/ui/gtk/rtp_analysis.c
new file mode 100644
index 0000000000..1d647056ab
--- /dev/null
+++ b/ui/gtk/rtp_analysis.c
@@ -0,0 +1,3911 @@
+/* rtp_analysis.c
+ * RTP analysis addition for Wireshark
+ *
+ * $Id$
+ *
+ * Copyright 2003, Alcatel Business Systems
+ * By Lars Ruoff <lars.ruoff@gmx.net>
+ *
+ * based on tap_rtp.c
+ * Copyright 2003, Iskratel, Ltd, Kranj
+ * By Miha Jemec <m.jemec@iskratel.si>
+ *
+ * Graph. Copyright 2004, Verso Technology
+ * By Alejandro Vaquero <alejandro.vaquero@verso.com>
+ * Based on io_stat.c by Ronnie Sahlberg
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <locale.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include <gtk/gtk.h>
+
+#include <epan/epan_dissect.h>
+#include <epan/filesystem.h>
+#include <epan/pint.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-rtp.h>
+#include <epan/rtp_pt.h>
+#include <epan/addr_resolv.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/strutil.h>
+
+#include "../util.h"
+#include "../g711.h"
+#include "../alert_box.h"
+#include "../simple_dialog.h"
+#include "../stat_menu.h"
+#include "../progress_dlg.h"
+#include "../tempfile.h"
+#include <wsutil/file_util.h>
+
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/file_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/pixmap_save.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/rtp_analysis.h"
+#include "ui/gtk/rtp_stream.h"
+#include "ui/gtk/rtp_stream_dlg.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/utf8_entities.h"
+
+#ifdef HAVE_LIBPORTAUDIO
+#include "ui/gtk/graph_analysis.h"
+#include "ui/gtk/voip_calls.h"
+#include "ui/gtk/rtp_player.h"
+#endif /* HAVE_LIBPORTAUDIO */
+
+#include "ui/gtk/old-gtk-compat.h"
+
+enum
+{
+ PACKET_COLUMN,
+ SEQUENCE_COLUMN,
+ TIMESTAMP_COLUMN,
+ DELTA_COLUMN,
+ JITTER_COLUMN,
+ SKEW_COLUMN,
+ IPBW_COLUMN,
+ MARKER_COLUMN,
+ STATUS_COLUMN,
+ DATE_COLUMN,
+ LENGTH_COLUMN,
+ FOREGROUND_COLOR_COL,
+ BACKGROUND_COLOR_COL,
+ N_COLUMN /* The number of columns */
+};
+/****************************************************************************/
+
+#define NUM_COLS 9
+#define NUM_GRAPH_ITEMS 100000
+#define MAX_YSCALE 16
+#define AUTO_MAX_YSCALE_INDEX 0
+#define AUTO_MAX_YSCALE 0
+#define MAX_GRAPHS 6
+#define GRAPH_FWD_JITTER 0
+#define GRAPH_FWD_DIFF 1
+#define GRAPH_FWD_DELTA 2
+#define GRAPH_REV_JITTER 3
+#define GRAPH_REV_DIFF 4
+#define GRAPH_REV_DELTA 5
+static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
+
+#define MAX_PIXELS_PER_TICK 4
+#define DEFAULT_PIXELS_PER_TICK_INDEX 2
+static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
+static const char *graph_descr[MAX_GRAPHS] = {"Fwd Jitter", "Fwd Difference", "Fwd Delta", "Rvr Jitter", "Rvr Difference", "Rvr Delta"};
+/* unit is in ms */
+#define MAX_TICK_VALUES 5
+#define DEFAULT_TICK_INTERVAL_VALUES_INDEX 1
+static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
+typedef struct _dialog_graph_graph_item_t {
+ guint32 value;
+ guint32 flags;
+} dialog_graph_graph_item_t;
+
+typedef struct _dialog_graph_graph_t {
+ struct _user_data_t *ud;
+ dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
+ int plot_style;
+ gboolean display;
+ GtkWidget *display_button;
+ int hf_index;
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkRGBA rgba_color;
+#endif
+ GdkColor color;
+ gchar title[100];
+} dialog_graph_graph_t;
+
+
+typedef struct _dialog_graph_t {
+ gboolean needs_redraw;
+ gint32 interval_index; /* index into tick_interval_values_array */
+ gint32 interval; /* measurement interval in ms */
+ guint32 last_interval;
+ guint32 max_interval; /* XXX max_interval and num_items are redundant */
+ guint32 num_items;
+ struct _dialog_graph_graph_t graph[MAX_GRAPHS];
+ GtkWidget *window;
+ GtkWidget *draw_area;
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_surface_t *surface;
+#else
+ GdkPixmap *pixmap;
+#endif
+ GtkAdjustment *scrollbar_adjustment;
+ GtkWidget *scrollbar;
+ int surface_width;
+ int surface_height;
+ int pixels_per_tick_index; /* index into pixels_per_tick array */
+ int pixels_per_tick;
+ int max_y_units_index; /* index into yscale_max array */
+ int max_y_units;
+ double start_time;
+} dialog_graph_t;
+
+typedef struct _dialog_data_t {
+ GtkWidget *window;
+ GtkWidget *list_fwd;
+ GtkTreeIter iter;
+ GtkWidget *list_rev;
+ GtkWidget *label_stats_fwd;
+ GtkWidget *label_stats_rev;
+ GtkWidget *selected_list;
+ guint number_of_nok;
+ GtkTreeSelection *selected_list_sel;
+ gint selected_list_row;
+ GtkWidget *notebook;
+ GtkWidget *save_voice_as_w;
+ GtkWidget *save_csv_as_w;
+ gint notebook_signal_id;
+ dialog_graph_t dialog_graph;
+} dialog_data_t;
+
+#define OK_TEXT "[ Ok ]"
+
+/* type of error when saving voice in a file didn't succeed */
+typedef enum {
+ TAP_RTP_WRONG_CODEC,
+ TAP_RTP_WRONG_LENGTH,
+ TAP_RTP_PADDING_ERROR,
+ TAP_RTP_SHORT_FRAME,
+ TAP_RTP_FILE_OPEN_ERROR,
+ TAP_RTP_FILE_WRITE_ERROR,
+ TAP_RTP_NO_DATA
+} error_type_t;
+
+typedef struct _tap_rtp_save_info_t {
+ FILE *fp;
+ guint32 count;
+ error_type_t error_type;
+ gboolean saved;
+} tap_rtp_save_info_t;
+
+
+/* structure that holds the information about the forward and reversed direction */
+struct _info_direction {
+ tap_rtp_stat_t statinfo;
+ tap_rtp_save_info_t saveinfo;
+};
+
+#define SILENCE_PCMU (guint8)0xFF
+#define SILENCE_PCMA (guint8)0x55
+
+/* structure that holds general information about the connection
+* and structures for both directions */
+typedef struct _user_data_t {
+ /* tap associated data*/
+ address ip_src_fwd;
+ guint16 port_src_fwd;
+ address ip_dst_fwd;
+ guint16 port_dst_fwd;
+ guint32 ssrc_fwd;
+ address ip_src_rev;
+ guint16 port_src_rev;
+ address ip_dst_rev;
+ guint16 port_dst_rev;
+ guint32 ssrc_rev;
+
+ struct _info_direction forward;
+ struct _info_direction reversed;
+
+ char *f_tempname;
+ char *r_tempname;
+
+ /* dialog associated data */
+ dialog_data_t dlg;
+
+} user_data_t;
+
+
+/* Column titles. */
+static const gchar *titles[11] = {
+ "Packet",
+ "Sequence",
+ "Time stamp",
+ "Delta (ms)",
+ "Jitter (ms)",
+ "Skew(ms)",
+ "IP BW (kbps)",
+ "Marker",
+ "Status",
+ "Date",
+ "Length"
+};
+
+#define SAVE_FORWARD_DIRECTION_MASK 0x01
+#define SAVE_REVERSE_DIRECTION_MASK 0x02
+#define SAVE_BOTH_DIRECTION_MASK (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
+
+#define SAVE_NONE_FORMAT 0
+#define SAVE_WAV_FORMAT 1
+#define SAVE_AU_FORMAT 2
+#define SAVE_SW_FORMAT 3
+#define SAVE_RAW_FORMAT 4
+
+
+static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data);
+/****************************************************************************/
+static void enable_graph(dialog_graph_graph_t *dgg)
+{
+
+ dgg->display=TRUE;
+
+}
+
+static void dialog_graph_reset(user_data_t* user_data);
+
+
+
+/****************************************************************************/
+/* TAP FUNCTIONS */
+
+/****************************************************************************/
+/* when there is a [re]reading of packet's */
+static void
+rtp_reset(void *user_data_arg)
+{
+ user_data_t *user_data = user_data_arg;
+ user_data->forward.statinfo.first_packet = TRUE;
+ user_data->reversed.statinfo.first_packet = TRUE;
+ user_data->forward.statinfo.max_delta = 0;
+ user_data->reversed.statinfo.max_delta = 0;
+ user_data->forward.statinfo.max_jitter = 0;
+ user_data->reversed.statinfo.max_jitter = 0;
+ user_data->forward.statinfo.max_skew = 0;
+ user_data->reversed.statinfo.max_skew = 0;
+ user_data->forward.statinfo.mean_jitter = 0;
+ user_data->reversed.statinfo.mean_jitter = 0;
+ user_data->forward.statinfo.delta = 0;
+ user_data->reversed.statinfo.delta = 0;
+ user_data->forward.statinfo.diff = 0;
+ user_data->reversed.statinfo.diff = 0;
+ user_data->forward.statinfo.jitter = 0;
+ user_data->reversed.statinfo.jitter = 0;
+ user_data->forward.statinfo.skew = 0;
+ user_data->reversed.statinfo.skew = 0;
+ user_data->forward.statinfo.sumt = 0;
+ user_data->reversed.statinfo.sumt = 0;
+ user_data->forward.statinfo.sumTS = 0;
+ user_data->reversed.statinfo.sumTS = 0;
+ user_data->forward.statinfo.sumt2 = 0;
+ user_data->reversed.statinfo.sumt2 = 0;
+ user_data->forward.statinfo.sumtTS = 0;
+ user_data->reversed.statinfo.sumtTS = 0;
+ user_data->forward.statinfo.bandwidth = 0;
+ user_data->reversed.statinfo.bandwidth = 0;
+ user_data->forward.statinfo.total_bytes = 0;
+ user_data->reversed.statinfo.total_bytes = 0;
+ user_data->forward.statinfo.bw_start_index = 0;
+ user_data->reversed.statinfo.bw_start_index = 0;
+ user_data->forward.statinfo.bw_index = 0;
+ user_data->reversed.statinfo.bw_index = 0;
+ user_data->forward.statinfo.timestamp = 0;
+ user_data->reversed.statinfo.timestamp = 0;
+ user_data->forward.statinfo.max_nr = 0;
+ user_data->reversed.statinfo.max_nr = 0;
+ user_data->forward.statinfo.total_nr = 0;
+ user_data->reversed.statinfo.total_nr = 0;
+ user_data->forward.statinfo.sequence = 0;
+ user_data->reversed.statinfo.sequence = 0;
+ user_data->forward.statinfo.start_seq_nr = 0;
+ user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
+ user_data->forward.statinfo.stop_seq_nr = 0;
+ user_data->reversed.statinfo.stop_seq_nr = 0;
+ user_data->forward.statinfo.cycles = 0;
+ user_data->reversed.statinfo.cycles = 0;
+ user_data->forward.statinfo.under = FALSE;
+ user_data->reversed.statinfo.under = FALSE;
+ user_data->forward.statinfo.start_time = 0;
+ user_data->reversed.statinfo.start_time = 0;
+ user_data->forward.statinfo.time = 0;
+ user_data->reversed.statinfo.time = 0;
+ user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
+ user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
+
+ user_data->forward.saveinfo.count = 0;
+ user_data->reversed.saveinfo.count = 0;
+ user_data->forward.saveinfo.saved = FALSE;
+ user_data->reversed.saveinfo.saved = FALSE;
+
+ /* clear the dialog box lists */
+ gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd))));
+ gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev))));
+
+ /* reset graph info */
+ dialog_graph_reset(user_data);
+
+#ifdef HAVE_LIBPORTAUDIO
+ /* reset the RTP player */
+ reset_rtp_player();
+#endif
+ /* XXX check for error at fclose? */
+ if (user_data->forward.saveinfo.fp != NULL)
+ fclose(user_data->forward.saveinfo.fp);
+ if (user_data->reversed.saveinfo.fp != NULL)
+ fclose(user_data->reversed.saveinfo.fp);
+ user_data->forward.saveinfo.fp = ws_fopen(user_data->f_tempname, "wb");
+ if (user_data->forward.saveinfo.fp == NULL)
+ user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
+ user_data->reversed.saveinfo.fp = ws_fopen(user_data->r_tempname, "wb");
+ if (user_data->reversed.saveinfo.fp == NULL)
+ user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
+ return;
+}
+
+/****************************************************************************/
+static gboolean rtp_packet_add_graph(dialog_graph_graph_t *dgg, tap_rtp_stat_t *statinfo, packet_info *pinfo, guint32 value)
+{
+ dialog_graph_graph_item_t *it;
+ guint32 idx;
+ double rtp_time;
+
+ /*
+ * We sometimes get called when dgg is disabled.
+ * This is a bug since the tap listener should be removed first
+ */
+ if(!dgg->display){
+ return FALSE;
+ }
+
+ dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
+
+ /*
+ * Find which interval this is supposed to go in and store the
+ * interval index as idx
+ */
+ if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
+ dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
+ }
+ rtp_time = nstime_to_msec(&pinfo->fd->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
+ if(rtp_time<0){
+ return FALSE;
+ }
+ idx = (guint32)(rtp_time)/dgg->ud->dlg.dialog_graph.interval;
+
+ /* some sanity checks */
+ if(idx>=NUM_GRAPH_ITEMS){
+ return FALSE;
+ }
+
+ /* update num_items */
+ if(idx > dgg->ud->dlg.dialog_graph.num_items){
+ dgg->ud->dlg.dialog_graph.num_items=idx;
+ dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
+ }
+
+ /*
+ * Find the appropriate dialog_graph_graph_item_t structure
+ */
+ it=&dgg->items[idx];
+
+ /*
+ * Use the max value to highlight RTP problems
+ */
+ if (value > it->value) {
+ it->value=value;
+ }
+ it->flags = it->flags | statinfo->flags;
+
+ return TRUE;
+}
+
+/****************************************************************************/
+/* here we can redraw the output */
+/* not used yet */
+static void rtp_draw(void *prs _U_)
+{
+ return;
+}
+
+/* forward declarations */
+static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num, guint32 timestamp,
+ double delta, double jitter, double skew ,double bandwidth, gchar *status, gboolean marker,
+ gchar *timeStr, guint32 pkt_len,gchar *color_str, guint32 flags);
+
+static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data,
+ tap_rtp_stat_t *statinfo, packet_info *pinfo,
+ const struct _rtp_info *rtpinfo);
+
+static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
+ tap_rtp_stat_t *statinfo,
+ packet_info *pinfo,
+ const struct _rtp_info *rtpinfo);
+
+
+/****************************************************************************/
+/* whenever a RTP packet is seen by the tap listener */
+static int rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *rtpinfo_arg)
+{
+ user_data_t *user_data = user_data_arg;
+ const struct _rtp_info *rtpinfo = rtpinfo_arg;
+ gboolean rtp_selected = FALSE;
+
+ /* we ignore packets that are not displayed */
+ if (pinfo->fd->flags.passed_dfilter == 0)
+ return 0;
+ /* also ignore RTP Version != 2 */
+ else if (rtpinfo->info_version !=2)
+ return 0;
+ /* is it the forward direction? */
+ else if (user_data->ssrc_fwd == rtpinfo->info_sync_src
+ && CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
+ && user_data->port_src_fwd == pinfo->srcport
+ && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
+ && user_data->port_dst_fwd == pinfo->destport) {
+ rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
+ rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_JITTER]),
+ &(user_data->forward.statinfo), pinfo,
+ (guint32)(user_data->forward.statinfo.jitter*1000));
+ rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DIFF]),
+ &(user_data->forward.statinfo), pinfo,
+ (guint32)(user_data->forward.statinfo.diff*1000));
+ rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DELTA]),
+ &(user_data->forward.statinfo), pinfo,
+ (guint32)(user_data->forward.statinfo.delta*1000));
+ rtp_packet_add_info(user_data->dlg.list_fwd, user_data,
+ &(user_data->forward.statinfo), pinfo, rtpinfo);
+ rtp_packet_save_payload(&(user_data->forward.saveinfo),
+ &(user_data->forward.statinfo), pinfo, rtpinfo);
+ rtp_selected = TRUE;
+ }
+ /* is it the reversed direction? */
+ else if (user_data->ssrc_rev == rtpinfo->info_sync_src
+ && CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
+ && user_data->port_src_rev == pinfo->srcport
+ && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
+ && user_data->port_dst_rev == pinfo->destport) {
+ rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
+ rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_JITTER]),
+ &(user_data->reversed.statinfo), pinfo,
+ (guint32)(user_data->reversed.statinfo.jitter*1000));
+ rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DIFF]),
+ &(user_data->reversed.statinfo), pinfo,
+ (guint32)(user_data->reversed.statinfo.diff*1000));
+ rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DELTA]),
+ &(user_data->reversed.statinfo), pinfo,
+ (guint32)(user_data->reversed.statinfo.delta*1000));
+ rtp_packet_add_info(user_data->dlg.list_rev, user_data,
+ &(user_data->reversed.statinfo), pinfo, rtpinfo);
+ rtp_packet_save_payload(&(user_data->reversed.saveinfo),
+ &(user_data->reversed.statinfo), pinfo, rtpinfo);
+ rtp_selected = TRUE;
+ }
+ /* add this RTP for future listening using the RTP Player*/
+#ifdef HAVE_LIBPORTAUDIO
+ if (rtp_selected)
+ add_rtp_packet(rtpinfo, pinfo);
+#endif
+
+ return 0;
+}
+
+/*
+Replaced by using the strings instead.
+static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
+static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
+static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
+static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
+COLOR_T_EVENT g_snprintf(color_str,sizeof(color_str),"#ef8c bfff ffff");
+static const GdkColor COLOR_FOREGROUND = {0, 0x0000, 0x0000, 0x0000};
+*/
+/****************************************************************************/
+/* adds statistics information from the packet to the list */
+static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data,
+ tap_rtp_stat_t *statinfo, packet_info *pinfo,
+ const struct _rtp_info *rtpinfo)
+{
+ guint16 msecs;
+ gchar timeStr[32];
+ struct tm *tm_tmp;
+ time_t then;
+ gchar status[40];
+ gchar color_str[14];
+ then = pinfo->fd->abs_ts.secs;
+ msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
+ tm_tmp = localtime(&then);
+ g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
+ tm_tmp->tm_mon + 1,
+ tm_tmp->tm_mday,
+ tm_tmp->tm_year + 1900,
+ tm_tmp->tm_hour,
+ tm_tmp->tm_min,
+ tm_tmp->tm_sec,
+ msecs);
+
+ /* Default to using black on white text if nothing below overrides it */
+ g_snprintf(color_str,sizeof(color_str),"#ffffffffffff");
+
+ if (statinfo->pt == PT_CN) {
+ g_snprintf(status,sizeof(status),"Comfort noise (PT=13, RFC 3389)");
+ /* color = COLOR_CN; */
+ g_snprintf(color_str,sizeof(color_str),"#bfffbfffffff");
+ }
+ else if (statinfo->pt == PT_CN_OLD) {
+ g_snprintf(status,sizeof(status),"Comfort noise (PT=19, reserved)");
+ /* color = COLOR_CN; */
+ g_snprintf(color_str,sizeof(color_str),"#bfffbfffffff");
+ }
+ else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
+ g_snprintf(status,sizeof(status),"Wrong sequence nr.");
+ /* color = COLOR_ERROR; */
+ g_snprintf(color_str,sizeof(color_str),"#ffffbfffbfff");
+ }
+ else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
+ if (statinfo->flags & STAT_FLAG_PT_T_EVENT){
+ g_snprintf(status,sizeof(status),"Payload changed to PT=%u telephone/event", statinfo->pt);
+ }else{
+ g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
+ }
+ /* color = COLOR_WARNING; */
+ g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
+ }
+ else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
+ g_snprintf(status,sizeof(status),"Incorrect timestamp");
+ /* color = COLOR_WARNING; */
+ g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
+ }
+ else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
+ && !(statinfo->flags & STAT_FLAG_FIRST)
+ && !(statinfo->flags & STAT_FLAG_PT_CN)
+ && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
+ && !(statinfo->flags & STAT_FLAG_MARKER)) {
+ g_snprintf(status,sizeof(status),"Marker missing?");
+ /* color = COLOR_WARNING; */
+ g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
+ }else if (statinfo->flags & STAT_FLAG_PT_T_EVENT){
+ g_snprintf(status,sizeof(status),"PT=%u telephone/event", statinfo->pt);
+ /* XXX add color? */
+ /* color = COLOR_T_EVENT; */
+ g_snprintf(color_str,sizeof(color_str),"#ef8cbfffffff");
+ }else {
+ if (statinfo->flags & STAT_FLAG_MARKER) {
+ /* color = COLOR_WARNING; */
+ g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
+ }
+ g_snprintf(status,sizeof(status),OK_TEXT);
+ }
+ /* is this the first packet we got in this direction? */
+ if (statinfo->flags & STAT_FLAG_FIRST) {
+ add_to_list(list, user_data,
+ pinfo->fd->num, rtpinfo->info_seq_num,
+ statinfo->timestamp,
+ 0,
+ 0,
+ 0,
+ statinfo->bandwidth,
+ status,
+ rtpinfo->info_marker_set,
+ timeStr, pinfo->fd->pkt_len,
+ color_str,
+ statinfo->flags);
+ }
+ else {
+ add_to_list(list, user_data,
+ pinfo->fd->num, rtpinfo->info_seq_num,
+ statinfo->timestamp,
+ statinfo->delta,
+ statinfo->jitter,
+ statinfo->skew,
+ statinfo->bandwidth,
+ status,
+ rtpinfo->info_marker_set,
+ timeStr, pinfo->fd->pkt_len,
+ color_str,
+ statinfo->flags);
+ }
+ return 0;
+}
+
+#define MAX_SILENCE_TICKS 1000000
+/****************************************************************************/
+static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
+ tap_rtp_stat_t *statinfo,
+ packet_info *pinfo,
+ const struct _rtp_info *rtpinfo)
+{
+ guint i;
+ const guint8 *data;
+ guint8 tmp;
+ size_t nchars;
+
+ /* is this the first packet we got in this direction? */
+ if (statinfo->flags & STAT_FLAG_FIRST) {
+ if (saveinfo->fp == NULL) {
+ saveinfo->saved = FALSE;
+ saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
+ }
+ else
+ saveinfo->saved = TRUE;
+ }
+
+ /* save the voice information */
+ /* if there was already an error, we quit */
+ if (saveinfo->saved == FALSE)
+ return 0;
+
+ /* if the captured length and packet length aren't equal, we quit
+ * if also the RTP dissector thinks there is some information missing */
+ if ((pinfo->fd->pkt_len != pinfo->fd->cap_len) &&
+ (!rtpinfo->info_all_data_present)) {
+ saveinfo->saved = FALSE;
+ saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
+ return 0;
+ }
+
+ /* if padding bit is set, but the padding count is bigger
+ * then the whole RTP data - error with padding count */
+ if ( (rtpinfo->info_padding_set != FALSE) &&
+ (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
+ saveinfo->saved = FALSE;
+ saveinfo->error_type = TAP_RTP_PADDING_ERROR;
+ return 0;
+ }
+
+ /* do we need to insert some silence? */
+ if ((rtpinfo->info_marker_set) &&
+ !(statinfo->flags & STAT_FLAG_FIRST) &&
+ !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) &&
+ (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) {
+ /* the amount of silence should be the difference between
+ * the last timestamp and the current one minus x
+ * x should equal the amount of information in the last frame
+ * XXX not done yet */
+ for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
+ rtpinfo->info_padding_count) && i < MAX_SILENCE_TICKS; i++) {
+ switch (statinfo->reg_pt) {
+ case PT_PCMU:
+ tmp = SILENCE_PCMU;
+ break;
+ case PT_PCMA:
+ tmp = SILENCE_PCMA;
+ break;
+ default:
+ tmp = 0;
+ break;
+ }
+ nchars = fwrite(&tmp, 1, 1, saveinfo->fp);
+ if (nchars != 1) {
+ /* Write error or short write */
+ saveinfo->saved = FALSE;
+ saveinfo->error_type = TAP_RTP_FILE_WRITE_ERROR;
+ return 0;
+ }
+ saveinfo->count++;
+ }
+ fflush(saveinfo->fp);
+ }
+
+
+ if (rtpinfo->info_payload_type == PT_CN
+ || rtpinfo->info_payload_type == PT_CN_OLD) {
+ }
+ /*all other payloads*/
+ else {
+ if (!rtpinfo->info_all_data_present) {
+ /* Not all the data was captured. */
+ saveinfo->saved = FALSE;
+ saveinfo->error_type = TAP_RTP_SHORT_FRAME;
+ return 0;
+ }
+
+ /* we put the pointer at the beginning of the RTP
+ * payload, that is, at the beginning of the RTP data
+ * plus the offset of the payload from the beginning
+ * of the RTP data */
+ data = rtpinfo->info_data + rtpinfo->info_payload_offset;
+ nchars = fwrite(data, sizeof(unsigned char), (rtpinfo->info_payload_len - rtpinfo->info_padding_count), saveinfo->fp);
+ if (nchars != (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) {
+ /* Write error or short write */
+ saveinfo->saved = FALSE;
+ saveinfo->error_type = TAP_RTP_FILE_WRITE_ERROR;
+ return 0;
+ }
+ saveinfo->count+=(rtpinfo->info_payload_len - rtpinfo->info_padding_count);
+
+ fflush(saveinfo->fp);
+ saveinfo->saved = TRUE;
+ return 0;
+ }
+
+ return 0;
+}
+
+
+/****************************************************************************/
+/* CALLBACKS */
+
+/****************************************************************************/
+
+/****************************************************************************/
+/* close the dialog window and remove the tap listener */
+static void on_destroy(GtkWidget *win _U_, user_data_t *user_data)
+{
+ /* remove tap listener */
+ protect_thread_critical_region();
+ remove_tap_listener(user_data);
+ unprotect_thread_critical_region();
+
+ /* close and remove temporary files */
+ if (user_data->forward.saveinfo.fp != NULL)
+ fclose(user_data->forward.saveinfo.fp);
+ if (user_data->reversed.saveinfo.fp != NULL)
+ fclose(user_data->reversed.saveinfo.fp);
+ /*XXX: test for error **/
+ ws_remove(user_data->f_tempname);
+ ws_remove(user_data->r_tempname);
+
+#if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
+ /* destroy save_voice_as window if open */
+ if (user_data->dlg.save_voice_as_w != NULL)
+ window_destroy(user_data->dlg.save_voice_as_w);
+#endif
+ /* destroy graph window if open */
+ if (user_data->dlg.dialog_graph.window != NULL)
+ window_destroy(user_data->dlg.dialog_graph.window);
+
+ /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exception using GTK1*/
+ g_signal_handler_disconnect(user_data->dlg.notebook, user_data->dlg.notebook_signal_id);
+
+ g_free(user_data->f_tempname);
+ g_free(user_data->r_tempname);
+ g_free(user_data);
+}
+
+
+/****************************************************************************/
+static void on_notebook_switch_page(GtkNotebook *notebook _U_,
+ gpointer *page _U_,
+ gint page_num _U_,
+ user_data_t *user_data _U_)
+{
+ user_data->dlg.selected_list =
+ (page_num==0) ? user_data->dlg.list_fwd : user_data->dlg.list_rev ;
+
+ user_data->dlg.selected_list_row = 0;
+}
+
+/****************************************************************************/
+
+static void on_list_select_row(GtkTreeSelection *selection,
+ user_data_t *user_data/*gpointer data */)
+{
+ user_data->dlg.selected_list_sel = selection;
+}
+
+
+/****************************************************************************/
+static void dialog_graph_set_title(user_data_t* user_data)
+{
+ char *title;
+ if (!user_data->dlg.dialog_graph.window){
+ return;
+ }
+ title = g_strdup_printf("RTP Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
+ get_addr_name(&(user_data->ip_src_fwd)),
+ user_data->port_src_fwd,
+ get_addr_name(&(user_data->ip_dst_fwd)),
+ user_data->port_dst_fwd,
+ get_addr_name(&(user_data->ip_src_rev)),
+ user_data->port_src_rev,
+ get_addr_name(&(user_data->ip_dst_rev)),
+ user_data->port_dst_rev);
+
+ gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
+ g_free(title);
+
+}
+
+
+/****************************************************************************/
+static void dialog_graph_reset(user_data_t* user_data)
+{
+ int i, j;
+
+ user_data->dlg.dialog_graph.needs_redraw=TRUE;
+ for(i=0;i<MAX_GRAPHS;i++){
+ for(j=0;j<NUM_GRAPH_ITEMS;j++){
+ dialog_graph_graph_item_t *dggi;
+ dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
+ dggi->value=0;
+ dggi->flags=0;
+ }
+ }
+ user_data->dlg.dialog_graph.last_interval=0xffffffff;
+ user_data->dlg.dialog_graph.max_interval=0;
+ user_data->dlg.dialog_graph.num_items=0;
+
+ /* create the color titles near the filter buttons */
+ for(i=0;i<MAX_GRAPHS;i++){
+ /* it is forward */
+ if (i<(MAX_GRAPHS/2)){
+ g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
+ sizeof(user_data->dlg.dialog_graph.graph[0].title),
+ "%s: %s:%u to %s:%u (SSRC=0x%X)",
+ graph_descr[i],
+ get_addr_name(&(user_data->ip_src_fwd)),
+ user_data->port_src_fwd,
+ get_addr_name(&(user_data->ip_dst_fwd)),
+ user_data->port_dst_fwd,
+ user_data->ssrc_fwd);
+ /* it is reverse */
+ } else {
+ g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
+ sizeof(user_data->dlg.dialog_graph.graph[0].title),
+ "%s: %s:%u to %s:%u (SSRC=0x%X)",
+ graph_descr[i],
+ get_addr_name(&(user_data->ip_src_rev)),
+ user_data->port_src_rev,
+ get_addr_name(&(user_data->ip_dst_rev)),
+ user_data->port_dst_rev,
+ user_data->ssrc_rev);
+ }
+ }
+
+ dialog_graph_set_title(user_data);
+}
+
+/****************************************************************************/
+static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
+{
+ dialog_graph_graph_item_t *it;
+
+ it=&dgg->items[idx];
+
+ return it->value;
+}
+
+/****************************************************************************/
+static void print_time_scale_string(char *buf, int buf_len, guint32 t)
+{
+ if(t>=10000000){
+ g_snprintf(buf, buf_len, "%ds",t/1000000);
+ } else if(t>=1000000){
+ g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
+ } else if(t>=10000){
+ g_snprintf(buf, buf_len, "%dms",t/1000);
+ } else if(t>=1000){
+ g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
+ } else {
+ g_snprintf(buf, buf_len, "%dus",t);
+ }
+}
+
+/****************************************************************************/
+static void dialog_graph_draw(user_data_t* user_data)
+{
+ int i, lwidth;
+ guint32 last_interval, first_interval, interval_delta, delta_multiplier;
+ gint32 current_interval;
+ guint32 left_x_border;
+ guint32 right_x_border;
+ guint32 top_y_border;
+ guint32 bottom_y_border;
+ PangoLayout *layout;
+ int label_width, label_height;
+ int label_width_mid, label_height_mid;
+ guint32 draw_width, draw_height;
+ char label_string[15];
+ GtkAllocation widget_alloc;
+ cairo_t *cr;
+
+ /* new variables */
+ guint32 num_time_intervals;
+ guint32 max_value; /* max value of seen data */
+ guint32 max_y; /* max value of the Y scale */
+
+ if(!user_data->dlg.dialog_graph.needs_redraw){
+ return;
+ }
+ user_data->dlg.dialog_graph.needs_redraw=FALSE;
+
+ /*
+ * Find the length of the intervals we have data for
+ * so we know how large arrays we need to malloc()
+ */
+ num_time_intervals=user_data->dlg.dialog_graph.num_items;
+ /* if there isnt anything to do, just return */
+ if(num_time_intervals==0){
+ return;
+ }
+ num_time_intervals+=1;
+ /* XXX move this check to _packet() */
+ if(num_time_intervals>NUM_GRAPH_ITEMS){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "RTP Graph error. There are too many entries, bailing out");
+ return;
+ }
+
+ /*
+ * find the max value so we can autoscale the y axis
+ */
+ max_value=0;
+ for(i=0;i<MAX_GRAPHS;i++){
+ int idx;
+
+ if(!user_data->dlg.dialog_graph.graph[i].display){
+ continue;
+ }
+ for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
+ guint32 val;
+
+ val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
+
+ /* keep track of the max value we have encountered */
+ if(val>max_value){
+ max_value=val;
+ }
+ }
+ }
+
+ /*
+ * Clear out old plot
+ */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ gtk_widget_get_allocation(user_data->dlg.dialog_graph.draw_area, &widget_alloc);
+ cairo_rectangle (cr,
+ 0,
+ 0,
+ widget_alloc.width,
+ widget_alloc.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+ /*
+ * Calculate the y scale we should use
+ */
+ if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
+ max_y=yscale_max[MAX_YSCALE-1];
+ for(i=MAX_YSCALE-1;i>0;i--){
+ if(max_value<yscale_max[i]){
+ max_y=yscale_max[i];
+ }
+ }
+ } else {
+ /* the user had specified an explicit y scale to use */
+ max_y=user_data->dlg.dialog_graph.max_y_units;
+ }
+
+ /*
+ * Calculate size of borders surrounding the plot
+ * The border on the right side needs to be adjusted depending
+ * on the width of the text labels.
+ */
+ print_time_scale_string(label_string, sizeof(label_string), max_y);
+ layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
+ pango_layout_get_pixel_size(layout, &label_width, &label_height);
+ print_time_scale_string(label_string, sizeof(label_string), max_y*5/10);
+ layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
+ pango_layout_get_pixel_size(layout, &label_width_mid, &label_height_mid);
+ if (label_width_mid > label_width) {
+ label_width = label_width_mid;
+ label_height = label_height_mid;
+ }
+
+ left_x_border=10;
+ right_x_border=label_width+20;
+ top_y_border=10;
+ bottom_y_border=label_height+20;
+
+
+ /*
+ * Calculate the size of the drawing area for the actual plot
+ */
+ draw_width=user_data->dlg.dialog_graph.surface_width-right_x_border-left_x_border;
+ draw_height=user_data->dlg.dialog_graph.surface_height-top_y_border-bottom_y_border;
+
+
+ /*
+ * Draw the y axis and labels
+ * (we always draw the y scale with 11 ticks along the axis)
+ */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, user_data->dlg.dialog_graph.surface_width-right_x_border+1.5, top_y_border+0.5);
+ cairo_line_to(cr, user_data->dlg.dialog_graph.surface_width-right_x_border+1.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+ for(i=0;i<=10;i++){
+ int xwidth;
+
+ xwidth=5;
+ if(!(i%5)){
+ /* first, middle and last tick are slightly longer */
+ xwidth=10;
+ }
+ /* draw the tick */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr,
+ user_data->dlg.dialog_graph.surface_width-right_x_border+1.5,
+ user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10+0.5);
+
+ cairo_line_to(cr,
+ user_data->dlg.dialog_graph.surface_width-right_x_border+1.5+xwidth,
+ user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ /* draw the labels */
+ if(i==0){
+ print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_move_to (cr,
+ user_data->dlg.dialog_graph.surface_width-right_x_border+15+label_width-lwidth,
+ user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10-label_height/2);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+ if(i==5){
+ print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_move_to (cr,
+ user_data->dlg.dialog_graph.surface_width-right_x_border+15+label_width-lwidth,
+ user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10-label_height/2);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+ if(i==10){
+ print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_move_to (cr,
+ user_data->dlg.dialog_graph.surface_width-right_x_border+15+label_width-lwidth,
+ user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10-label_height/2);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+ }
+
+
+
+ /*
+ * if we have not specified the last_interval via the gui,
+ * then just pick the current end of the capture so that is scrolls
+ * nicely when doing live captures
+ */
+ if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
+ last_interval=user_data->dlg.dialog_graph.max_interval;
+ } else {
+ last_interval=user_data->dlg.dialog_graph.last_interval;
+ }
+
+
+
+
+/*XXX*/
+ /* plot the x-scale */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, left_x_border+0.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+1.5);
+ cairo_line_to(cr, user_data->dlg.dialog_graph.surface_width-right_x_border+1.5,user_data->dlg.dialog_graph.surface_height-bottom_y_border+1.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+ if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
+ first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
+ first_interval*=user_data->dlg.dialog_graph.interval;
+ } else {
+ first_interval=0;
+ }
+
+ interval_delta=1;
+ delta_multiplier=5;
+ while(interval_delta<((last_interval-first_interval)/10)){
+ interval_delta*=delta_multiplier;
+ if(delta_multiplier==5){
+ delta_multiplier=2;
+ } else {
+ delta_multiplier=5;
+ }
+ }
+
+ for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
+ int x, xlen;
+
+ /* if pixels_per_tick is <5, only draw every 10 ticks */
+ if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
+ continue;
+ }
+
+ if(current_interval%interval_delta){
+ xlen=5;
+ } else {
+ xlen=17;
+ }
+
+ x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, x-1-user_data->dlg.dialog_graph.pixels_per_tick/2+0.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+1.5);
+ cairo_line_to(cr, x-1-user_data->dlg.dialog_graph.pixels_per_tick/2+0.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+xlen+1.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+ if(xlen==17){
+ if(user_data->dlg.dialog_graph.interval>=1000){
+ g_snprintf(label_string, sizeof(label_string), "%ds", current_interval/1000);
+ } else if(user_data->dlg.dialog_graph.interval>=100){
+ g_snprintf(label_string, sizeof(label_string), "%d.%1ds", current_interval/1000,(current_interval/100)%10);
+ } else if(user_data->dlg.dialog_graph.interval>=10){
+ g_snprintf(label_string, sizeof(label_string), "%d.%2ds", current_interval/1000,(current_interval/10)%100);
+ } else {
+ g_snprintf(label_string, sizeof(label_string), "%d.%3ds", current_interval/1000,current_interval%1000);
+ }
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_move_to (cr,
+ x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
+ user_data->dlg.dialog_graph.surface_height-bottom_y_border+20);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+
+ }
+
+
+
+
+
+
+ /*
+ * Draw "x" for Sequence Errors and "m" for Marks
+ */
+ /* Draw the labels Fwd and Rev */
+ g_strlcpy(label_string, UTF8_LEFTWARDS_ARROW "Fwd",sizeof(label_string));
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_move_to (cr,
+ user_data->dlg.dialog_graph.surface_width-right_x_border+33-lwidth,
+ user_data->dlg.dialog_graph.surface_height-bottom_y_border+3);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+
+ g_strlcpy(label_string, UTF8_LEFTWARDS_ARROW "Rev",sizeof(label_string));
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_move_to (cr,
+ user_data->dlg.dialog_graph.surface_width-right_x_border+33-lwidth,
+ user_data->dlg.dialog_graph.surface_height-bottom_y_border+3+9);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+
+ /* Draw the marks */
+ for(i=MAX_GRAPHS-1;i>=0;i--){
+ guint32 interval;
+ guint32 x_pos/*, prev_x_pos*/;
+
+ /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
+ if (!user_data->dlg.dialog_graph.graph[i].display){
+ continue;
+ }
+ /* initialize prev x/y to the low left corner of the graph */
+ /*prev_x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-first_interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;*/
+
+ for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
+ x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
+
+ if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
+ if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
+ g_strlcpy(label_string,"x",sizeof(label_string));
+ } else {
+ g_strlcpy(label_string,"m",sizeof(label_string));
+ }
+
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_move_to (cr,
+ x_pos-1-lwidth/2,
+ user_data->dlg.dialog_graph.surface_height-bottom_y_border+3+7*(i/2));
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+
+ }
+
+ /*prev_x_pos=x_pos;*/
+ }
+ }
+
+ g_object_unref(G_OBJECT(layout));
+
+ /*
+ * Loop over all graphs and draw them
+ */
+ for(i=MAX_GRAPHS-1;i>=0;i--){
+ guint32 interval;
+ guint32 x_pos, y_pos, /*prev_x_pos,*/ prev_y_pos;
+ if (!user_data->dlg.dialog_graph.graph[i].display){
+ continue;
+ }
+ /* initialize prev x/y to the low left corner of the graph */
+ /*prev_x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-first_interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;*/
+ prev_y_pos=draw_height-1+top_y_border;
+
+ for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
+ guint32 val;
+ x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
+ val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
+ if(val>max_y){
+ y_pos=0;
+ } else {
+ y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
+ }
+
+ /* dont need to draw anything if the segment
+ * is entirely above the top of the graph
+ */
+ if( (prev_y_pos==0) && (y_pos==0) ){
+ prev_y_pos=y_pos;
+ /*prev_x_pos=x_pos;*/
+ continue;
+ }
+
+ if(val){
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ gdk_cairo_set_source_color (cr, &user_data->dlg.dialog_graph.graph[i].color);
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, x_pos+0.5, draw_height-1+top_y_border+0.5);
+ cairo_line_to(cr, x_pos+0.5, y_pos+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ }
+
+ prev_y_pos=y_pos;
+ /*prev_x_pos=x_pos;*/
+ }
+ }
+
+ cr = gdk_cairo_create (gtk_widget_get_window(user_data->dlg.dialog_graph.draw_area));
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, user_data->dlg.dialog_graph.pixmap, 0, 0);
+#endif
+ cairo_rectangle (cr, 0, 0, user_data->dlg.dialog_graph.surface_width, user_data->dlg.dialog_graph.surface_height);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+
+ /* update the scrollbar */
+ gtk_adjustment_set_upper(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) user_data->dlg.dialog_graph.max_interval);
+ gtk_adjustment_set_step_increment(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) ((last_interval-first_interval)/10));
+ gtk_adjustment_set_page_increment(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) (last_interval-first_interval));
+ if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
+ gtk_adjustment_set_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) (user_data->dlg.dialog_graph.max_interval/100));
+ } else {
+ gtk_adjustment_set_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) (last_interval-first_interval));
+ }
+ gtk_adjustment_set_value(user_data->dlg.dialog_graph.scrollbar_adjustment, last_interval - gtk_adjustment_get_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment));
+ gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
+ gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
+
+}
+
+/****************************************************************************/
+static void dialog_graph_redraw(user_data_t* user_data)
+{
+ user_data->dlg.dialog_graph.needs_redraw=TRUE;
+ dialog_graph_draw(user_data);
+}
+
+/****************************************************************************/
+static void quit(GtkWidget *widget _U_, user_data_t *user_data)
+{
+ GtkWidget *bt_save = g_object_get_data(G_OBJECT(user_data->dlg.dialog_graph.window), "bt_save");
+ surface_info_t *surface_info = g_object_get_data(G_OBJECT(bt_save), "surface-info");
+
+ g_free(surface_info);
+ user_data->dlg.dialog_graph.window = NULL;
+}
+
+/****************************************************************************/
+#if GTK_CHECK_VERSION(3,0,0)
+static gboolean draw_area_draw(GtkWidget *widget, cairo_t *cr, gpointer data)
+{
+ user_data_t *user_data = data;
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation (widget, &allocation);
+
+ cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
+ cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
+ cairo_fill (cr);
+
+ return FALSE;
+}
+#else
+static gint expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
+{
+ user_data_t *user_data = data;
+ cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(widget));
+
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, user_data->dlg.dialog_graph.pixmap, 0, 0);
+#endif
+ cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+#endif
+/****************************************************************************/
+static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
+{
+ user_data_t *user_data;
+ GtkWidget *bt_save;
+ GtkAllocation widget_alloc;
+ cairo_t *cr;
+#if GTK_CHECK_VERSION(2,22,0)
+ surface_info_t *surface_info = g_new(surface_info_t, 1);
+#endif
+
+ user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
+
+ if(!user_data){
+ exit(10);
+ }
+
+#if GTK_CHECK_VERSION(2,22,0)
+ if(user_data->dlg.dialog_graph.surface){
+ g_object_unref(user_data->dlg.dialog_graph.surface);
+ user_data->dlg.dialog_graph.surface=NULL;
+ }
+ gtk_widget_get_allocation(widget, &widget_alloc);
+ user_data->dlg.dialog_graph.surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
+ CAIRO_CONTENT_COLOR,
+ widget_alloc.width,
+ widget_alloc.height);
+#else
+ if(user_data->dlg.dialog_graph.pixmap){
+ g_object_unref(user_data->dlg.dialog_graph.pixmap);
+ user_data->dlg.dialog_graph.pixmap=NULL;
+ }
+
+ gtk_widget_get_allocation(widget, &widget_alloc);
+ user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(gtk_widget_get_window(widget),
+ widget_alloc.width,
+ widget_alloc.height,
+ -1);
+#endif
+ user_data->dlg.dialog_graph.surface_width=widget_alloc.width;
+ user_data->dlg.dialog_graph.surface_height=widget_alloc.height;
+
+ bt_save = g_object_get_data(G_OBJECT(user_data->dlg.dialog_graph.window), "bt_save");
+#if GTK_CHECK_VERSION(2,22,0)
+ surface_info->surface = user_data->dlg.dialog_graph.surface;
+ surface_info->width = widget_alloc.width;
+ surface_info->height = widget_alloc.height;
+ g_object_set_data(G_OBJECT(bt_save), "surface-info", surface_info);
+ gtk_widget_set_sensitive(bt_save, TRUE);
+
+ cr = cairo_create (user_data->dlg.dialog_graph.surface);
+#else
+ g_object_set_data(G_OBJECT(bt_save), "pixmap", user_data->dlg.dialog_graph.pixmap);
+ gtk_widget_set_sensitive(bt_save, TRUE);
+
+ cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
+#endif
+ cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+ dialog_graph_redraw(user_data);
+ return TRUE;
+}
+
+/****************************************************************************/
+static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
+{
+ user_data_t *user_data=(user_data_t *)data;
+ guint32 mi;
+
+ mi=(guint32) (gtk_adjustment_get_value(user_data->dlg.dialog_graph.scrollbar_adjustment) + gtk_adjustment_get_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment));
+ if(user_data->dlg.dialog_graph.last_interval==mi){
+ return TRUE;
+ }
+ if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
+ && (mi==user_data->dlg.dialog_graph.max_interval) ){
+ return TRUE;
+ }
+
+ user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
+
+ dialog_graph_redraw(user_data);
+ return TRUE;
+}
+
+/****************************************************************************/
+static void create_draw_area(user_data_t* user_data, GtkWidget *box)
+{
+ user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
+ g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(quit), user_data);
+
+ gtk_widget_set_size_request(user_data->dlg.dialog_graph.draw_area, user_data->dlg.dialog_graph.surface_width, user_data->dlg.dialog_graph.surface_height);
+
+ /* signals needed to handle backing pixmap */
+#if GTK_CHECK_VERSION(3,0,0)
+ g_signal_connect(user_data->dlg.dialog_graph.draw_area, "draw", G_CALLBACK(draw_area_draw), user_data);
+#else
+ g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(expose_event), user_data);
+#endif
+ g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(configure_event), user_data);
+
+ gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
+ gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
+
+ /* create the associated scrollbar */
+ user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
+ user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
+ gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
+ gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
+ g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), user_data);
+}
+
+/****************************************************************************/
+static void disable_graph(dialog_graph_graph_t *dgg)
+{
+ if (dgg->display) {
+ dgg->display=FALSE;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
+ FALSE);
+ }
+}
+
+/****************************************************************************/
+static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
+{
+ /* this graph is not active, just update display and redraw */
+ if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
+ disable_graph(dgg);
+ dialog_graph_redraw(dgg->ud);
+ return 0;
+ }
+
+ enable_graph(dgg);
+ cf_retap_packets(&cfile);
+ dialog_graph_redraw(dgg->ud);
+
+ return 0;
+}
+
+/****************************************************************************/
+static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
+{
+ GtkWidget *hbox;
+ GtkWidget *label;
+ char str[256];
+
+ hbox=gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(box), hbox);
+ gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
+ gtk_widget_show(hbox);
+
+ g_snprintf(str, sizeof(str), "Graph %d", num);
+ dgg->display_button=gtk_toggle_button_new_with_label(str);
+ gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
+ gtk_widget_show(dgg->display_button);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
+ g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_callback), dgg);
+
+ label=gtk_label_new(dgg->title);
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_color(label, GTK_STATE_NORMAL, &dgg->rgba_color);
+ gtk_widget_override_color(label, GTK_STATE_ACTIVE, &dgg->rgba_color);
+ gtk_widget_override_color(label, GTK_STATE_PRELIGHT, &dgg->rgba_color);
+ gtk_widget_override_color(label, GTK_STATE_SELECTED, &dgg->rgba_color);
+ gtk_widget_override_color(label, GTK_STATE_INSENSITIVE, &dgg->rgba_color);
+#else
+ gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
+ gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
+ gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
+ gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
+ gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
+#endif
+ return;
+}
+
+/****************************************************************************/
+static void create_filter_area(user_data_t* user_data, GtkWidget *box)
+{
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ int i;
+ GtkWidget *label;
+
+ frame=gtk_frame_new("Graphs");
+ gtk_container_add(GTK_CONTAINER(box), frame);
+ gtk_widget_show(frame);
+
+ vbox=gtk_vbox_new(FALSE, 1);
+ gtk_container_add(GTK_CONTAINER(frame), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
+ gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
+ gtk_widget_show(vbox);
+
+ for(i=0;i<MAX_GRAPHS;i++){
+ create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
+ }
+
+ label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ return;
+}
+
+/****************************************************************************/
+static void yscale_select(GtkWidget *item, gpointer key)
+{
+ int i;
+ user_data_t *user_data;
+
+ user_data=(user_data_t *)key;
+ i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
+
+ user_data->dlg.dialog_graph.max_y_units_index=i;
+ user_data->dlg.dialog_graph.max_y_units=yscale_max[i];
+ dialog_graph_redraw(user_data);
+}
+
+/****************************************************************************/
+static void pixels_per_tick_select(GtkWidget *item, gpointer key)
+{
+ int i;
+ user_data_t *user_data;
+
+ user_data=(user_data_t *)key;
+ i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
+
+ user_data->dlg.dialog_graph.pixels_per_tick_index=i;
+ user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[i];
+ dialog_graph_redraw(user_data);
+}
+
+/****************************************************************************/
+static void tick_interval_select(GtkWidget *item, gpointer key)
+{
+ int i;
+ user_data_t *user_data;
+
+ user_data=(user_data_t *)key;
+ i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
+
+ user_data->dlg.dialog_graph.interval_index=i;
+ user_data->dlg.dialog_graph.interval=tick_interval_values[i];
+ cf_retap_packets(&cfile);
+ dialog_graph_redraw(user_data);
+}
+
+/****************************************************************************/
+static GtkWidget *
+create_yscale_max_menu_items(user_data_t* user_data)
+{
+ char str[15];
+ GtkWidget *combo_box;
+ int i;
+
+ combo_box = gtk_combo_box_text_new();
+
+ for(i=0;i<MAX_YSCALE;i++){
+ if(yscale_max[i]==AUTO_MAX_YSCALE){
+ g_strlcpy(str,"Auto",sizeof(str));
+ } else if (yscale_max[i] < 1000000) {
+ g_snprintf(str, sizeof(str), "%u ms", yscale_max[i]/1000);
+ } else {
+ g_snprintf(str, sizeof(str), "%u s", yscale_max[i]/1000000);
+ }
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
+ }
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.max_y_units_index);
+ g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), (gpointer)user_data);
+
+ return combo_box;
+}
+
+/****************************************************************************/
+static GtkWidget *
+create_pixels_per_tick_menu_items(user_data_t *user_data)
+{
+ char str[5];
+ GtkWidget *combo_box;
+ int i;
+
+ combo_box = gtk_combo_box_text_new();
+
+ for(i=0;i<MAX_PIXELS_PER_TICK;i++){
+ g_snprintf(str, sizeof(str), "%u", pixels_per_tick[i]);
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
+ }
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.pixels_per_tick_index);
+
+ g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), (gpointer)user_data);
+
+ return combo_box;
+}
+
+/****************************************************************************/
+static GtkWidget *
+create_tick_interval_menu_items(user_data_t *user_data)
+{
+ GtkWidget *combo_box;
+ char str[15];
+ int i;
+
+ combo_box = gtk_combo_box_text_new();
+
+ for(i=0;i<MAX_TICK_VALUES;i++){
+ if(tick_interval_values[i]>=1000){
+ g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
+ } else if(tick_interval_values[i]>=100){
+ g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
+ } else if(tick_interval_values[i]>=10){
+ g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
+ } else {
+ g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
+ }
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
+ }
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.interval_index);
+ g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), (gpointer)user_data);
+
+ return combo_box;
+}
+
+/****************************************************************************/
+static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, GtkWidget *(*func)(user_data_t* user_data))
+{
+ GtkWidget *hbox;
+ GtkWidget *label;
+ GtkWidget *combo_box;
+
+ hbox=gtk_hbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(box), hbox);
+ gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
+ gtk_widget_show(hbox);
+
+ label=gtk_label_new(name);
+ gtk_widget_show(label);
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+
+ combo_box = (*func)(user_data);
+ gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
+ gtk_widget_show(combo_box);
+}
+
+/****************************************************************************/
+static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
+{
+ GtkWidget *frame_vbox;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+
+ frame_vbox=gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(box), frame_vbox);
+ gtk_widget_show(frame_vbox);
+
+ frame = gtk_frame_new("X Axis");
+ gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
+ gtk_widget_show(frame);
+
+ vbox=gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(frame), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
+ gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
+ gtk_widget_show(vbox);
+
+ create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
+ create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
+
+ frame = gtk_frame_new("Y Axis");
+ gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
+ gtk_widget_show(frame);
+
+ vbox=gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(frame), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
+ gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
+ gtk_widget_show(vbox);
+
+ create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
+
+ return;
+}
+
+/****************************************************************************/
+static void dialog_graph_init_window(user_data_t* user_data)
+{
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *bt_close;
+ GtkWidget *bt_save;
+
+ /* create the main window */
+ user_data->dlg.dialog_graph.window=dlg_window_new("I/O Graphs"); /* transient_for top_level */
+
+ vbox=gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
+ gtk_widget_show(vbox);
+
+ create_draw_area(user_data, vbox);
+
+ hbox=gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
+ gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
+ gtk_widget_show(hbox);
+
+ create_filter_area(user_data, hbox);
+ create_ctrl_area(user_data, hbox);
+
+ dialog_graph_set_title(user_data);
+
+ hbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_SAVE, NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ gtk_widget_show(hbox);
+
+ bt_close = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
+
+ bt_save = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_SAVE);
+ gtk_widget_set_sensitive(bt_save, FALSE);
+ gtk_widget_set_tooltip_text(bt_save, "Save the displayed graph to a file");
+ g_signal_connect(bt_save, "clicked", G_CALLBACK(pixmap_save_cb), NULL);
+ g_object_set_data(G_OBJECT(user_data->dlg.dialog_graph.window), "bt_save", bt_save);
+
+ g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+
+ gtk_widget_show(user_data->dlg.dialog_graph.window);
+ window_present(user_data->dlg.dialog_graph.window);
+
+}
+
+
+/****************************************************************************/
+static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
+{
+ if (user_data->dlg.dialog_graph.window != NULL) {
+ /* There's already a graph window; reactivate it. */
+ reactivate_window(user_data->dlg.dialog_graph.window);
+ return;
+ }
+
+ dialog_graph_init_window(user_data);
+
+}
+
+/****************************************************************************/
+
+static void on_goto_bt_clicked_lst(GtkWidget *bt _U_, user_data_t *user_data _U_)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreeSelection *selection;
+ guint fnumber;
+
+ selection = user_data->dlg.selected_list_sel;
+
+ if (selection==NULL)
+ return;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)){
+ gtk_tree_model_get (model, &iter, PACKET_COLUMN, &fnumber, -1);
+ cf_goto_frame(&cfile, fnumber);
+ }
+
+}
+
+static void draw_stat(user_data_t *user_data);
+
+/****************************************************************************/
+/* re-dissects all packets */
+static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
+{
+ GString *error_string;
+
+ /* remove tap listener */
+ protect_thread_critical_region();
+ remove_tap_listener(user_data);
+ unprotect_thread_critical_region();
+
+ /* register tap listener */
+ error_string = register_tap_listener("rtp", user_data, NULL, 0,
+ rtp_reset, rtp_packet, rtp_draw);
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ return;
+ }
+
+ /* retap all packets */
+ cf_retap_packets(&cfile);
+
+ /* draw statistics info */
+ draw_stat(user_data);
+
+}
+
+#ifdef HAVE_LIBPORTAUDIO
+/****************************************************************************/
+static void
+on_player_bt_clicked(GtkButton *button _U_, gpointer user_data _U_)
+{
+ /*rtp_player_init(voip_calls_get_info());*/
+ rtp_player_init(NULL);
+}
+#endif /* HAVE_LIBPORTAUDIO */
+
+static void on_next_bt_clicked_list(GtkWidget *bt _U_, user_data_t *user_data _U_)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ gchar *text;
+ GtkTreeSelection *selection;
+ GtkTreePath *path;
+
+ selection = user_data->dlg.selected_list_sel;
+
+ if (selection==NULL)
+ return;
+
+try_again:
+ if (gtk_tree_selection_get_selected (selection, &model, &iter)){
+ while (gtk_tree_model_iter_next (model,&iter)) {
+ gtk_tree_model_get (model, &iter, STATUS_COLUMN, &text, -1);
+ if (strcmp(text, OK_TEXT) != 0) {
+ gtk_tree_selection_select_iter (selection, &iter);
+ path = gtk_tree_model_get_path(model, &iter);
+ gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data->dlg.selected_list),
+ path,
+ NULL, FALSE, 0, 0);
+ gtk_tree_path_free(path);
+ g_free (text);
+ return;
+ }
+ g_free (text);
+ }
+ /* wrap around */
+ if (user_data->dlg.number_of_nok>1){
+ /* Get the first iter and select it before starting over */
+ gtk_tree_model_get_iter_first(model, &iter);
+ gtk_tree_selection_select_iter (selection, &iter);
+ goto try_again;
+ }
+ }
+}
+
+
+/****************************************************************************/
+/* when we want to save the information */
+static gboolean save_csv_as_ok_cb(GtkWidget *w _U_, gpointer fc /*user_data_t *user_data*/)
+{
+ gchar *g_dest;
+ GtkWidget *rev, *forw, *both;
+ user_data_t *user_data;
+
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ gboolean more_items = TRUE;
+
+ /* To Hold data from the list row */
+ guint32 packet; /* Packet */
+ guint16 sequence; /* Sequence */
+ guint32 timestamp; /* timestamp */
+ gfloat delta; /* Delta(ms) */
+ gfloat jitter; /* Jitter(ms) */
+ gfloat skew; /* Skew(ms) */
+ gfloat ipbw; /* IP BW(kbps) */
+ gboolean marker; /* Marker */
+ char * status_str; /* Status */
+ char * date_str; /* Date */
+ guint length; /* Length */
+
+
+ FILE *fp;
+ int j;
+
+ g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
+
+ /* Perhaps the user specified a directory instead of a file.
+ * Check whether they did.
+ */
+ if (test_for_directory(g_dest) == EISDIR) {
+ /* It's a directory - set the file selection box to display it. */
+ set_last_open_dir(g_dest);
+ g_free(g_dest);
+ file_selection_set_current_folder(fc, get_last_open_dir());
+ gtk_file_chooser_set_current_name(fc, "");
+ return FALSE; /* run the dialog again */
+ }
+
+ rev = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "reversed_rb");
+ forw = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "forward_rb");
+ both = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "both_rb");
+ user_data = (user_data_t*)g_object_get_data(G_OBJECT(fc), "user_data");
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(forw)) || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
+ fp = ws_fopen(g_dest, "w");
+ if (fp == NULL) {
+ open_failure_alert_box(g_dest, errno, TRUE);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
+ fprintf(fp, "Forward\n");
+ if (ferror(fp)) {
+ write_failure_alert_box(g_dest, errno);
+ fclose(fp);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ }
+
+ for(j = 0; j < NUM_COLS; j++) {
+ if (j == 0) {
+ fprintf(fp,"\"%s\"",titles[j]);
+ } else {
+ fprintf(fp,",\"%s\"",titles[j]);
+ }
+ }
+ fprintf(fp,"\n");
+ if (ferror(fp)) {
+ write_failure_alert_box(g_dest, errno);
+ fclose(fp);
+ g_free(g_dest);
+ return TRUE;
+ }
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd));
+ store = GTK_LIST_STORE(model);
+ if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
+
+ while (more_items){
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
+ PACKET_COLUMN, &packet,
+ SEQUENCE_COLUMN, &sequence,
+ TIMESTAMP_COLUMN, &timestamp,
+ DELTA_COLUMN, &delta,
+ JITTER_COLUMN, &jitter,
+ SKEW_COLUMN, &skew,
+ IPBW_COLUMN, &ipbw,
+ MARKER_COLUMN, &marker,
+ STATUS_COLUMN, &status_str,
+ DATE_COLUMN, &date_str,
+ LENGTH_COLUMN, &length,
+ -1);
+ fprintf(fp, "\"%u\"", packet);
+ fprintf(fp, ",\"%u\"", sequence);
+ fprintf(fp, ",\"%u\"", timestamp);
+ fprintf(fp, ",\"%.2f\"", delta);
+ fprintf(fp, ",\"%.2f\"", jitter);
+ fprintf(fp, ",\"%.2f\"", skew);
+ fprintf(fp, ",\"%.2f\"", ipbw);
+ fprintf(fp, ",\"%s\"", marker? "SET" : "");
+ fprintf(fp, ",\"%s\"", status_str);
+ fprintf(fp, ",\"%s\"", date_str);
+ fprintf(fp, ",\"%u\"", length);
+ fprintf(fp,"\n");
+ g_free(status_str);
+ g_free(date_str);
+ if (ferror(fp)) {
+ write_failure_alert_box(g_dest, errno);
+ fclose(fp);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+
+ more_items = gtk_tree_model_iter_next (model,&iter);
+ }
+ }
+
+ if (fclose(fp) == EOF) {
+ write_failure_alert_box(g_dest, errno);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ }
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rev)) || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
+ fp = ws_fopen(g_dest, "a");
+ if (fp == NULL) {
+ open_failure_alert_box(g_dest, errno, TRUE);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ fprintf(fp, "\nReverse\n");
+ if (ferror(fp)) {
+ write_failure_alert_box(g_dest, errno);
+ fclose(fp);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ } else {
+ fp = ws_fopen(g_dest, "w");
+ if (fp == NULL) {
+ open_failure_alert_box(g_dest, errno, TRUE);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ }
+ for(j = 0; j < NUM_COLS; j++) {
+ if (j == 0) {
+ fprintf(fp,"\"%s\"",titles[j]);
+ } else {
+ fprintf(fp,",\"%s\"",titles[j]);
+ }
+ }
+ fprintf(fp,"\n");
+ if (ferror(fp)) {
+ write_failure_alert_box(g_dest, errno);
+ fclose(fp);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev));
+ store = GTK_LIST_STORE(model);
+ if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
+
+ more_items = TRUE;
+
+ while (more_items){
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
+ PACKET_COLUMN, &packet,
+ SEQUENCE_COLUMN, &sequence,
+ TIMESTAMP_COLUMN, &timestamp,
+ DELTA_COLUMN, &delta,
+ JITTER_COLUMN, &jitter,
+ SKEW_COLUMN, &skew,
+ IPBW_COLUMN, &ipbw,
+ MARKER_COLUMN, &marker,
+ STATUS_COLUMN, &status_str,
+ DATE_COLUMN, &date_str,
+ LENGTH_COLUMN, &length,
+ -1);
+ fprintf(fp, "\"%u\"", packet);
+ fprintf(fp, ",\"%u\"", sequence);
+ fprintf(fp, ",\"%u\"", timestamp);
+ fprintf(fp, ",\"%.2f\"", delta);
+ fprintf(fp, ",\"%.2f\"", jitter);
+ fprintf(fp, ",\"%.2f\"", skew);
+ fprintf(fp, ",\"%.2f\"", ipbw);
+ fprintf(fp, ",\"%s\"", marker? "SET" : "");
+ fprintf(fp, ",\"%s\"", status_str);
+ fprintf(fp, ",\"%s\"", date_str);
+ fprintf(fp, ",\"%u\"", length);
+ fprintf(fp,"\n");
+ g_free(status_str);
+ g_free(date_str);
+ if (ferror(fp)) {
+ write_failure_alert_box(g_dest, errno);
+ fclose(fp);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+
+ more_items = gtk_tree_model_iter_next (model,&iter);
+ }
+ }
+ if (fclose(fp) == EOF) {
+ write_failure_alert_box(g_dest, errno);
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ }
+
+ g_free(g_dest);
+ return TRUE; /* we're done */
+}
+
+static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
+{
+ user_data->dlg.save_csv_as_w = NULL;
+}
+
+/* when the user wants to save the csv information in a file */
+static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data)
+{
+ GtkWidget *vertb;
+ GtkWidget *table1;
+ GtkWidget *label_format;
+ GtkWidget *channels_label;
+ GtkWidget *forward_rb;
+ GtkWidget *reversed_rb;
+ GtkWidget *both_rb;
+
+#if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
+ if (user_data->dlg.save_csv_as_w != NULL) {
+ /* There's already a Save CSV info dialog box; reactivate it. */
+ reactivate_window(user_data->dlg.save_csv_as_w);
+ return;
+ }
+#endif
+ user_data->dlg.save_csv_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Data As CSV",
+ GTK_WINDOW(user_data->dlg.notebook),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NULL);
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), TRUE);
+ gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_csv_as_w),GTK_WINDOW(user_data->dlg.window));
+
+ /* Container for each row of widgets */
+ vertb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
+ gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), vertb);
+ gtk_widget_show (vertb);
+
+ table1 = gtk_table_new (2, 4, FALSE);
+ gtk_widget_show (table1);
+ gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
+ gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
+
+ label_format = gtk_label_new ("Format: Comma Separated Values");
+ gtk_widget_show (label_format);
+ gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
+
+
+ channels_label = gtk_label_new ("Channels: ");
+ gtk_widget_show (channels_label);
+ gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
+
+ forward_rb = gtk_radio_button_new_with_label (NULL, "forward ");
+ gtk_widget_show (forward_rb);
+ gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ reversed_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "reversed ");
+ gtk_widget_show (reversed_rb);
+ gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ both_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "both");
+ gtk_widget_show (both_rb);
+ gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
+
+ g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "forward_rb", forward_rb);
+ g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "reversed_rb", reversed_rb);
+ g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "both_rb", both_rb);
+ g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "user_data", user_data);
+
+ g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event",
+ G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
+ G_CALLBACK(save_csv_as_destroy_cb), user_data);
+
+ gtk_widget_show(user_data->dlg.save_csv_as_w);
+ window_present(user_data->dlg.save_csv_as_w);
+
+ /* "Run" the GtkFileChooserDialog. */
+ /* Upon exit: If "Accept" run the OK callback. */
+ /* If the OK callback returns with a FALSE status, re-run the dialog.*/
+ /* Destroy the window. */
+ /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
+ /* return with a TRUE status so that the dialog window will be destroyed. */
+ /* Trying to re-run the dialog after popping up an alert box will not work */
+ /* since the user will not be able to dismiss the alert box. */
+ /* The (somewhat unfriendly) effect: the user must re-invoke the */
+ /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
+ /* */
+ /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
+ /* GtkFileChooserDialog. */
+ while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_csv_as_w)) == GTK_RESPONSE_ACCEPT) {
+ if (save_csv_as_ok_cb(NULL, user_data->dlg.save_csv_as_w)) {
+ break; /* we're done */
+ }
+ }
+ window_destroy(user_data->dlg.save_csv_as_w);
+}
+
+
+/****************************************************************************/
+static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
+{
+ /* Note that we no longer have a Save voice info dialog box. */
+ user_data->dlg.save_voice_as_w = NULL;
+}
+
+/****************************************************************************/
+/* here we save it into a file that user specified */
+/* XXX what about endians here? could go something wrong? */
+
+static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
+{
+ FILE *to_stream, *forw_stream, *rev_stream;
+ size_t fwritten, rwritten;
+ int f_rawvalue, r_rawvalue, rawvalue;
+ gint16 sample;
+ gchar pd[4];
+ guint32 f_write_silence = 0;
+ guint32 r_write_silence = 0;
+ progdlg_t *progbar;
+ guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
+ gboolean stop_flag = FALSE;
+ size_t nchars;
+ gboolean ret_val;
+
+ forw_stream = ws_fopen(user_data->f_tempname, "rb");
+ if (forw_stream == NULL)
+ return FALSE;
+ rev_stream = ws_fopen(user_data->r_tempname, "rb");
+ if (rev_stream == NULL) {
+ fclose(forw_stream);
+ return FALSE;
+ }
+
+ /* open file for saving */
+ to_stream = ws_fopen(dest, "wb");
+ if (to_stream == NULL) {
+ fclose(forw_stream);
+ fclose(rev_stream);
+ return FALSE;
+ }
+
+ progbar = create_progress_dlg("Saving voice in a file", dest, TRUE, &stop_flag);
+
+ if (format == SAVE_AU_FORMAT) /* au format */
+ {
+ /* First we write the .au header. XXX Hope this is endian independent */
+ /* the magic word 0x2e736e64 == .snd */
+ phtonl(pd, 0x2e736e64);
+ nchars = fwrite(pd, 1, 4, to_stream);
+ if (nchars != 4)
+ goto copy_file_err;
+ /* header offset == 24 bytes */
+ phtonl(pd, 24);
+ nchars = fwrite(pd, 1, 4, to_stream);
+ if (nchars != 4)
+ goto copy_file_err;
+ /* total length; it is permitted to set this to 0xffffffff */
+ phtonl(pd, -1);
+ nchars = fwrite(pd, 1, 4, to_stream);
+ if (nchars != 4)
+ goto copy_file_err;
+ /* encoding format == 16-bit linear PCM */
+ phtonl(pd, 3);
+ nchars = fwrite(pd, 1, 4, to_stream);
+ if (nchars != 4)
+ goto copy_file_err;
+ /* sample rate == 8000 Hz */
+ phtonl(pd, 8000);
+ nchars = fwrite(pd, 1, 4, to_stream);
+ if (nchars != 4)
+ goto copy_file_err;
+ /* channels == 1 */
+ phtonl(pd, 1);
+ nchars = fwrite(pd, 1, 4, to_stream);
+ if (nchars != 4)
+ goto copy_file_err;
+
+
+ switch (channels) {
+ /* only forward direction */
+ case SAVE_FORWARD_DIRECTION_MASK: {
+ progbar_count = user_data->forward.saveinfo.count;
+ progbar_quantum = user_data->forward.saveinfo.count/100;
+ while ((f_rawvalue = getc(forw_stream)) != EOF) {
+ if(stop_flag)
+ break;
+ if((count > progbar_nextstep) && (count <= progbar_count)) {
+ update_progress_dlg(progbar,
+ (gfloat) count/progbar_count, "Saving");
+ progbar_nextstep = progbar_nextstep + progbar_quantum;
+ }
+ count++;
+
+ if (user_data->forward.statinfo.pt == PT_PCMU){
+ sample = ulaw2linear((unsigned char)f_rawvalue);
+ phtons(pd, sample);
+ }
+ else if(user_data->forward.statinfo.pt == PT_PCMA){
+ sample = alaw2linear((unsigned char)f_rawvalue);
+ phtons(pd, sample);
+ }
+ else{
+ goto copy_file_err;
+ }
+
+ fwritten = fwrite(pd, 1, 2, to_stream);
+ if (fwritten < 2) {
+ goto copy_file_err;
+ }
+ }
+ break;
+ }
+ /* only reversed direction */
+ case SAVE_REVERSE_DIRECTION_MASK: {
+ progbar_count = user_data->reversed.saveinfo.count;
+ progbar_quantum = user_data->reversed.saveinfo.count/100;
+ while ((r_rawvalue = getc(rev_stream)) != EOF) {
+ if(stop_flag)
+ break;
+ if((count > progbar_nextstep) && (count <= progbar_count)) {
+ update_progress_dlg(progbar,
+ (gfloat) count/progbar_count, "Saving");
+ progbar_nextstep = progbar_nextstep + progbar_quantum;
+ }
+ count++;
+
+ if (user_data->reversed.statinfo.pt == PT_PCMU){
+ sample = ulaw2linear((unsigned char)r_rawvalue);
+ phtons(pd, sample);
+ }
+ else if(user_data->reversed.statinfo.pt == PT_PCMA){
+ sample = alaw2linear((unsigned char)r_rawvalue);
+ phtons(pd, sample);
+ }
+ else{
+ goto copy_file_err;
+ }
+
+ rwritten = fwrite(pd, 1, 2, to_stream);
+ if (rwritten < 2) {
+ goto copy_file_err;
+ }
+ }
+ break;
+ }
+ /* both directions */
+ case SAVE_BOTH_DIRECTION_MASK: {
+ (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
+ (progbar_count = user_data->forward.saveinfo.count) :
+ (progbar_count = user_data->reversed.saveinfo.count);
+ progbar_quantum = progbar_count/100;
+ /* since conversation in one way can start later than in the other one,
+ * we have to write some silence information for one channel */
+ if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
+ f_write_silence = (guint32)
+ ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*(8000/1000));
+ }
+ else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
+ r_write_silence = (guint32)
+ ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*(8000/1000));
+ }
+ for(;;) {
+ if(stop_flag)
+ break;
+ if((count > progbar_nextstep) && (count <= progbar_count)) {
+ update_progress_dlg(progbar,
+ (gfloat) count/progbar_count, "Saving");
+ progbar_nextstep = progbar_nextstep + progbar_quantum;
+ }
+ count++;
+ if(f_write_silence > 0) {
+ r_rawvalue = getc(rev_stream);
+ switch (user_data->forward.statinfo.reg_pt) {
+ case PT_PCMU:
+ f_rawvalue = SILENCE_PCMU;
+ break;
+ case PT_PCMA:
+ f_rawvalue = SILENCE_PCMA;
+ break;
+ default:
+ f_rawvalue = 0;
+ break;
+ }
+ f_write_silence--;
+ }
+ else if(r_write_silence > 0) {
+ f_rawvalue = getc(forw_stream);
+ switch (user_data->reversed.statinfo.reg_pt) {
+ case PT_PCMU:
+ r_rawvalue = SILENCE_PCMU;
+ break;
+ case PT_PCMA:
+ r_rawvalue = SILENCE_PCMA;
+ break;
+ default:
+ r_rawvalue = 0;
+ break;
+ }
+ r_write_silence--;
+ }
+ else {
+ f_rawvalue = getc(forw_stream);
+ r_rawvalue = getc(rev_stream);
+ }
+ if ((r_rawvalue == EOF) && (f_rawvalue == EOF))
+ break;
+ if ((user_data->forward.statinfo.pt == PT_PCMU) && (user_data->reversed.statinfo.pt == PT_PCMU)){
+ sample = (ulaw2linear((unsigned char)r_rawvalue) + ulaw2linear((unsigned char)f_rawvalue)) / 2;
+ phtons(pd, sample);
+ }
+ else if((user_data->forward.statinfo.pt == PT_PCMA) && (user_data->reversed.statinfo.pt == PT_PCMA)){
+ sample = (alaw2linear((unsigned char)r_rawvalue) + alaw2linear((unsigned char)f_rawvalue)) / 2;
+ phtons(pd, sample);
+ }
+ else
+ {
+ goto copy_file_err;
+ }
+
+
+ rwritten = fwrite(pd, 1, 2, to_stream);
+ if (rwritten < 2) {
+ goto copy_file_err;
+ }
+ }
+ }
+ }
+ }
+ else if (format == SAVE_RAW_FORMAT) /* raw format */
+ {
+ FILE *stream;
+ switch (channels) {
+ /* only forward direction */
+ case SAVE_FORWARD_DIRECTION_MASK: {
+ progbar_count = user_data->forward.saveinfo.count;
+ progbar_quantum = user_data->forward.saveinfo.count/100;
+ stream = forw_stream;
+ break;
+ }
+ /* only reversed direction */
+ case SAVE_REVERSE_DIRECTION_MASK: {
+ progbar_count = user_data->reversed.saveinfo.count;
+ progbar_quantum = user_data->reversed.saveinfo.count/100;
+ stream = rev_stream;
+ break;
+ }
+ default: {
+ goto copy_file_err;
+ }
+ }
+
+
+
+ /* XXX how do you just copy the file? */
+ while ((rawvalue = getc(stream)) != EOF) {
+ if(stop_flag)
+ break;
+ if((count > progbar_nextstep) && (count <= progbar_count)) {
+ update_progress_dlg(progbar,
+ (gfloat) count/progbar_count, "Saving");
+ progbar_nextstep = progbar_nextstep + progbar_quantum;
+ }
+ count++;
+
+ if (putc(rawvalue, to_stream) == EOF) {
+ goto copy_file_err;
+ }
+ }
+ }
+
+ ret_val = TRUE;
+ goto copy_file_xit;
+
+copy_file_err:
+ ret_val = FALSE;
+ goto copy_file_xit;
+
+copy_file_xit:
+ destroy_progress_dlg(progbar);
+ fclose(forw_stream);
+ fclose(rev_stream);
+ fclose(to_stream);
+ return ret_val;
+}
+
+
+/****************************************************************************/
+/* the user wants to save in a file */
+/* XXX support for different formats is currently commented out */
+static gboolean save_voice_as_ok_cb(GtkWidget *w _U_, gpointer fc)
+{
+ gchar *g_dest;
+ /*GtkWidget *wav, *sw;*/
+ GtkWidget *au, *raw;
+ GtkWidget *rev, *forw, *both;
+ user_data_t *user_data;
+ gint channels, format;
+
+ g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
+
+ /* Perhaps the user specified a directory instead of a file.
+ * Check whether they did.
+ */
+ if (test_for_directory(g_dest) == EISDIR) {
+ /* It's a directory - set the file selection box to display it. */
+ set_last_open_dir(g_dest);
+ g_free(g_dest);
+ file_selection_set_current_folder(fc, get_last_open_dir());
+ gtk_file_chooser_set_current_name(fc, "");
+ return FALSE; /* run the dialog again */
+ }
+
+#if 0
+ wav = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "wav_rb");
+ sw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "sw_rb");
+#endif
+ au = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "au_rb");
+ raw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "raw_rb");
+ rev = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "reversed_rb");
+ forw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "forward_rb");
+ both = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "both_rb");
+ user_data = (user_data_t *)g_object_get_data(G_OBJECT(fc), "user_data");
+
+ /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
+ * we don't support that codec. So we pop up a warning. Maybe it would be better to
+ * disable the ok button or disable the buttons for direction if only one is not ok. The
+ * problem is if we open the save voice dialog and then click the refresh button and maybe
+ * the state changes, so we can't save anymore. In this case we should be able to update
+ * the buttons. For now it is easier if we put the warning when the ok button is pressed.
+ */
+
+ /* we can not save in both directions */
+ if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both)))) {
+ /* there are many combinations here, we just exit when first matches */
+ if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
+ (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save in a file: Unsupported codec!");
+ else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
+ (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save in a file: Wrong length of captured packets!");
+ else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
+ (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save in a file: RTP data with padding!");
+ else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
+ (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save in a file: Not all data in all packets was captured!");
+ else
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save in a file: File I/O problem!");
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ /* we can not save forward direction */
+ else if ((user_data->forward.saveinfo.saved == FALSE) && ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (forw))) ||
+ (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both))))) {
+ if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save forward direction in a file: Unsupported codec!");
+ else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save forward direction in a file: Wrong length of captured packets!");
+ else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save forward direction in a file: RTP data with padding!");
+ else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save forward direction in a file: Not all data in all packets was captured!");
+ else
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save forward direction in a file: File I/O problem!");
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ /* we can not save reversed direction */
+ else if ((user_data->reversed.saveinfo.saved == FALSE) && ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rev))) ||
+ (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both))))) {
+ if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save reversed direction in a file: Unsupported codec!");
+ else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save reversed direction in a file: Wrong length of captured packets!");
+ else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save reversed direction in a file: RTP data with padding!");
+ else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save reversed direction in a file: Not all data in all packets was captured!");
+ else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save reversed direction in a file: No RTP data!");
+ else
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save reversed direction in a file: File I/O problem!");
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+
+#if 0
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (wav)))
+ format = SAVE_WAV_FORMAT;
+ else
+#endif
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (au)))
+ format = SAVE_AU_FORMAT;
+#if 0
+ else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (sw)))
+ format = SAVE_SW_FORMAT;
+#endif
+ else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (raw)))
+ format = SAVE_RAW_FORMAT;
+ else
+ format = SAVE_NONE_FORMAT;
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rev)))
+ channels = SAVE_REVERSE_DIRECTION_MASK;
+ else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both)))
+ channels = SAVE_BOTH_DIRECTION_MASK;
+ else
+ channels = SAVE_FORWARD_DIRECTION_MASK;
+
+ /* direction/format validity*/
+ if (format == SAVE_AU_FORMAT)
+ {
+ /* make sure streams are alaw/ulaw */
+ if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != PT_PCMA) && (user_data->forward.statinfo.pt != PT_PCMU)){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != PT_PCMA) && (user_data->reversed.statinfo.pt != PT_PCMU)){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ /* make sure pt's don't differ */
+ if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save in a file: Forward and reverse direction differ in type");
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ }
+ else if (format == SAVE_RAW_FORMAT)
+ {
+ /* can't save raw in both directions */
+ if (channels == SAVE_BOTH_DIRECTION_MASK){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save in a file: Unable to save raw data in both directions");
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+ }
+ else
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Can't save in a file: Invalid save format");
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+
+ if(!copy_file(g_dest, channels, format, user_data)) {
+ /* XXX - report the error type! */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "An error occurred while saving voice in a file!");
+ g_free(g_dest);
+ return TRUE; /* we're done */
+ }
+
+ g_free(g_dest);
+ return TRUE; /* we're done */
+}
+
+/****************************************************************************/
+/* when the user wants to save the voice information in a file */
+/* XXX support for different formats is currently commented out */
+static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
+{
+ GtkWidget *vertb;
+ GtkWidget *table1;
+ GtkWidget *label_format;
+ GtkWidget *channels_label;
+ GtkWidget *forward_rb;
+ GtkWidget *reversed_rb;
+ GtkWidget *both_rb;
+ /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
+ GtkWidget *au_rb;
+ GtkWidget *raw_rb;
+
+ /* if we can't save in a file: wrong codec, cut packets or other errors */
+ /* Should the error arise here or later when you click ok button ?
+ * if we do it here, then we must disable the refresh button, so we don't do it here
+ */
+
+#if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
+ if (user_data->dlg.save_voice_as_w != NULL) {
+ /* There's already a Save voice info dialog box; reactivate it. */
+ reactivate_window(user_data->dlg.save_voice_as_w);
+ return;
+ }
+#endif
+ /* XXX - use file_selection from dlg_utils instead! */
+ user_data->dlg.save_voice_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Payload As ...",
+ GTK_WINDOW(user_data->dlg.notebook),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NULL);
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), TRUE);
+ gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_voice_as_w),GTK_WINDOW(user_data->dlg.window));
+
+ /* Container for each row of widgets */
+ vertb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
+ gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), vertb);
+ gtk_widget_show (vertb);
+
+ table1 = gtk_table_new (2, 4, FALSE);
+ gtk_widget_show (table1);
+ gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
+ gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
+
+ /*label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
+ gtk_widget_show (label_format);
+ gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);*/
+
+ label_format = gtk_label_new ("Format: ");
+ gtk_widget_show (label_format);
+ gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
+
+ raw_rb = gtk_radio_button_new_with_label (NULL, ".raw");
+ gtk_widget_show (raw_rb);
+ gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+
+ au_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".au");
+ gtk_widget_show (au_rb);
+ gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+#if 0
+ /* we support .au - ulaw*/
+ wav_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".wav");
+ gtk_widget_show (wav_rb);
+ gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ sw_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), "8 kHz, 16 bit ");
+ gtk_widget_show (sw_rb);
+ gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ au_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".au");
+ gtk_widget_show (au_rb);
+ gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+#endif
+
+ channels_label = gtk_label_new ("Channels: ");
+ gtk_widget_show (channels_label);
+ gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
+
+ forward_rb = gtk_radio_button_new_with_label (NULL, "forward ");
+ gtk_widget_show (forward_rb);
+ gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ reversed_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "reversed ");
+ gtk_widget_show (reversed_rb);
+ gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+ both_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "both");
+ gtk_widget_show (both_rb);
+ gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(forward_rb), TRUE);
+
+#if 0
+ /* if one direction is nok we don't allow saving
+ XXX this is not ok since the user can click the refresh button and cause changes
+ but we can not update this window. So we move all the decision on the time the ok
+ button is clicked
+ */
+ if (user_data->forward.saved == FALSE) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
+ gtk_widget_set_sensitive(forward_rb, FALSE);
+ gtk_widget_set_sensitive(both_rb, FALSE);
+ }
+ else if (user_data->reversed.saved == FALSE) {
+ gtk_widget_set_sensitive(reversed_rb, FALSE);
+ gtk_widget_set_sensitive(both_rb, FALSE);
+ }
+ #endif
+
+ /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "wav_rb", wav_rb);*/
+ g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "au_rb", au_rb);
+ /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "sw_rb", sw_rb);*/
+ g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "raw_rb", raw_rb);
+ g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "forward_rb", forward_rb);
+ g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "reversed_rb", reversed_rb);
+ g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "both_rb", both_rb);
+ g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "user_data", user_data);
+
+ g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
+ G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
+ G_CALLBACK(save_voice_as_destroy_cb), user_data);
+
+ gtk_widget_show(user_data->dlg.save_voice_as_w);
+ window_present(user_data->dlg.save_voice_as_w);
+
+ /* "Run" the GtkFileChooserDialog. */
+ /* Upon exit: If "Accept" run the OK callback. */
+ /* If the OK callback returns with a FALSE status, re-run the dialog.*/
+ /* Destroy the window. */
+ /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
+ /* return with a TRUE status so that the dialog window will be destroyed. */
+ /* Trying to re-run the dialog after popping up an alert box will not work */
+ /* since the user will not be able to dismiss the alert box. */
+ /* The (somewhat unfriendly) effect: the user must re-invoke the */
+ /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
+ /* */
+ /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
+ /* GtkFileChooserDialog. */
+ while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_voice_as_w)) == GTK_RESPONSE_ACCEPT) {
+ if (save_voice_as_ok_cb(NULL, user_data->dlg.save_voice_as_w)) {
+ break; /* we're done */
+ }
+ }
+ window_destroy(user_data->dlg.save_voice_as_w);
+
+}
+
+
+/****************************************************************************/
+/* when we are finished with redisection, we add the label for the statistic */
+static void draw_stat(user_data_t *user_data)
+{
+ gchar label_max[300];
+ guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
+ - user_data->forward.statinfo.start_seq_nr + 1;
+ guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
+ - user_data->reversed.statinfo.start_seq_nr + 1;
+ guint32 f_total_nr = user_data->forward.statinfo.total_nr;
+ guint32 r_total_nr = user_data->reversed.statinfo.total_nr;
+ gint32 f_lost = f_expected - f_total_nr;
+ gint32 r_lost = r_expected - r_total_nr;
+ double f_sumt = user_data->forward.statinfo.sumt;
+ double f_sumTS = user_data->forward.statinfo.sumTS;
+ double f_sumt2 = user_data->forward.statinfo.sumt2;
+ double f_sumtTS = user_data->forward.statinfo.sumtTS;
+
+ double r_sumt = user_data->reversed.statinfo.sumt;
+ double r_sumTS = user_data->reversed.statinfo.sumTS;
+ double r_sumt2 = user_data->reversed.statinfo.sumt2;
+ double r_sumtTS = user_data->reversed.statinfo.sumtTS;
+ double f_perc, r_perc;
+ double f_clock_drift = 1.0;
+ double r_clock_drift = 1.0;
+ double f_duration = user_data->forward.statinfo.time - user_data->forward.statinfo.start_time;
+ double r_duration = user_data->reversed.statinfo.time - user_data->reversed.statinfo.start_time;
+ guint32 f_clock_rate = user_data->forward.statinfo.clock_rate;
+ guint32 r_clock_rate = user_data->reversed.statinfo.clock_rate;
+
+ if (f_clock_rate == 0){
+ f_clock_rate = 1;
+ }
+
+ if (r_clock_rate == 0){
+ r_clock_rate = 1;
+ }
+
+ if (f_expected){
+ f_perc = (double)(f_lost*100)/(double)f_expected;
+ } else {
+ f_perc = 0;
+ }
+ if (r_expected){
+ r_perc = (double)(r_lost*100)/(double)r_expected;
+ } else {
+ r_perc = 0;
+ }
+
+ if ((f_total_nr >0)&&(f_sumt2 > 0)){
+ f_clock_drift = (f_total_nr * f_sumtTS - f_sumt * f_sumTS) / (f_total_nr * f_sumt2 - f_sumt * f_sumt);
+ }
+ if ((r_total_nr >0)&&(r_sumt2 > 0)){
+ r_clock_drift = (r_total_nr * r_sumtTS - r_sumt * r_sumTS) / (r_total_nr * r_sumt2 - r_sumt * r_sumt);
+ }
+ g_snprintf(label_max, sizeof(label_max), "Max delta = %.2f ms at packet no. %u \n"
+ "Max jitter = %.2f ms. Mean jitter = %.2f ms.\n"
+ "Max skew = %.2f ms.\n"
+ "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
+ " Sequence errors = %u \n"
+ "Duration %.2f s (%.0f ms clock drift, corresponding to %.0f Hz (%+.2f%%)",
+ user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr,
+ user_data->forward.statinfo.max_jitter,user_data->forward.statinfo.mean_jitter,
+ user_data->forward.statinfo.max_skew,
+ f_expected, f_expected, f_lost, f_perc,
+ user_data->forward.statinfo.sequence,
+ f_duration/1000,f_duration*(f_clock_drift-1.0),f_clock_drift*f_clock_rate,100.0*(f_clock_drift-1.0));
+
+ gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
+ gtk_label_set_selectable (GTK_LABEL(user_data->dlg.label_stats_fwd),TRUE);
+
+ g_snprintf(label_max, sizeof(label_max), "Max delta = %.2f ms at packet no. %u \n"
+ "Max jitter = %.2f ms. Mean jitter = %.2f ms.\n"
+ "Max skew = %.2f ms.\n"
+ "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
+ " Sequence errors = %u \n"
+ "Duration %.2f s (%.0f ms clock drift, corresponding to %.0f Hz (%+.2f%%)",
+ user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr,
+ user_data->reversed.statinfo.max_jitter,user_data->reversed.statinfo.mean_jitter,
+ user_data->reversed.statinfo.max_skew,
+ r_expected, r_expected, r_lost, r_perc,
+ user_data->reversed.statinfo.sequence,
+ r_duration/1000,r_duration*(r_clock_drift-1.0),r_clock_drift*r_clock_rate,100.0*(r_clock_drift-1.0));
+
+ gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
+ gtk_label_set_selectable (GTK_LABEL(user_data->dlg.label_stats_rev),TRUE);
+
+ return ;
+}
+
+
+
+/****************************************************************************/
+/* append a line to list */
+static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num, guint32 timestamp,
+ double delta, double jitter,double skew, double bandwidth, gchar *status, gboolean marker,
+ gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags)
+{
+ GtkListStore *list_store;
+
+ if (strcmp(status, OK_TEXT) != 0) {
+ user_data->dlg.number_of_nok++;
+ }
+
+ list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (list))); /* Get store */
+
+ /* Creates a new row at position. iter will be changed to point to this new row.
+ * If position is larger than the number of rows on the list, then the new row will be appended to the list.
+ * The row will be filled with the values given to this function.
+ * :
+ * should generally be preferred when inserting rows in a sorted list store.
+ */
+ gtk_list_store_insert_with_values( list_store , &user_data->dlg.iter, G_MAXINT,
+ PACKET_COLUMN, number,
+ SEQUENCE_COLUMN, seq_num,
+ TIMESTAMP_COLUMN, timestamp,
+ DELTA_COLUMN, delta,
+ JITTER_COLUMN, jitter,
+ SKEW_COLUMN, skew,
+ IPBW_COLUMN, bandwidth,
+ MARKER_COLUMN, marker,
+ STATUS_COLUMN, (char *)status,
+ DATE_COLUMN, (char *)timeStr,
+ LENGTH_COLUMN, pkt_len,
+ FOREGROUND_COLOR_COL, NULL,
+ BACKGROUND_COLOR_COL, (char *)color_str,
+ -1);
+
+ if(flags & STAT_FLAG_FIRST){
+ /* Set first row as active */
+ gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), &user_data->dlg.iter);
+ }
+}
+
+/****************************************************************************
+* Functions needed to present values from the list
+*/
+
+
+/* Present boolean value */
+static void
+rtp_boolean_data_func (GtkTreeViewColumn *column _U_,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gboolean bool_val;
+ gchar buf[20];
+ /* the col to get data from is in userdata */
+ gint bool_col = GPOINTER_TO_INT(user_data);
+
+ gtk_tree_model_get(model, iter, bool_col, &bool_val, -1);
+
+ switch(bool_col){
+ case MARKER_COLUMN:
+ g_strlcpy(buf, bool_val ? "SET" : "", sizeof(buf));
+ break;
+ default:
+ g_assert_not_reached();
+ break;
+ }
+ g_object_set(renderer, "text", buf, NULL);
+}
+
+/* Create list */
+static
+GtkWidget* create_list(user_data_t* user_data)
+{
+
+ GtkListStore *list_store;
+ GtkWidget *list;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSortable *sortable;
+ GtkTreeView *list_view;
+ GtkTreeSelection *selection;
+
+ /* Create the store */
+ list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX */
+ G_TYPE_UINT, /* Packet */
+ G_TYPE_UINT, /* Sequence */
+ G_TYPE_UINT, /* Time stamp */
+ G_TYPE_FLOAT, /* Delta(ms) */
+ G_TYPE_FLOAT, /* Filtered Jitter(ms) */
+ G_TYPE_FLOAT, /* Skew(ms) */
+ G_TYPE_FLOAT, /* IP BW(kbps) */
+ G_TYPE_BOOLEAN, /* Marker */
+ G_TYPE_STRING, /* Status */
+ G_TYPE_STRING, /* Date */
+ G_TYPE_UINT, /* Length */
+ G_TYPE_STRING, /* Foreground color */
+ G_TYPE_STRING); /* Background color */
+
+ /* Create a view */
+ list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
+
+ list_view = GTK_TREE_VIEW(list);
+ sortable = GTK_TREE_SORTABLE(list_store);
+
+ /* Speed up the list display */
+ gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
+
+ /* Setup the sortable columns */
+ gtk_tree_sortable_set_sort_column_id(sortable, PACKET_COLUMN, GTK_SORT_ASCENDING);
+ gtk_tree_view_set_headers_clickable(list_view, FALSE);
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref (G_OBJECT (list_store));
+
+ /*
+ * Create the first column packet, associating the "text" attribute of the
+ * cell_renderer to the first column of the model
+ */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Packet", renderer,
+ "text", PACKET_COLUMN,
+ "foreground", FOREGROUND_COLOR_COL,
+ "background", BACKGROUND_COLOR_COL,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, PACKET_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 55);
+
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Sequence. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Sequence", renderer,
+ "text", SEQUENCE_COLUMN,
+ "foreground", FOREGROUND_COLOR_COL,
+ "background", BACKGROUND_COLOR_COL,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, SEQUENCE_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 75);
+ gtk_tree_view_append_column (list_view, column);
+
+#if 0
+ Currently not visible
+ /* Time stamp. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Time stamp", renderer,
+ "text", TIMESTAMP_COLUMN,
+ "foreground", FOREGROUND_COLOR_COL,
+ "background", BACKGROUND_COLOR_COL,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, TIMESTAMP_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 75);
+ gtk_tree_view_append_column (list_view, column);
+#endif
+ /* Delta(ms). */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Delta(ms)", renderer,
+ "text", DELTA_COLUMN,
+ "foreground", FOREGROUND_COLOR_COL,
+ "background", BACKGROUND_COLOR_COL,
+ NULL);
+
+ gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
+ GINT_TO_POINTER(DELTA_COLUMN), NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, DELTA_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 75);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Jitter(ms). */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Filtered Jitter(ms)", renderer,
+ "text", JITTER_COLUMN,
+ "foreground", FOREGROUND_COLOR_COL,
+ "background", BACKGROUND_COLOR_COL,
+ NULL);
+
+ gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
+ GINT_TO_POINTER(JITTER_COLUMN), NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, JITTER_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 110);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Skew(ms). */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Skew(ms)", renderer,
+ "text", SKEW_COLUMN,
+ "foreground", FOREGROUND_COLOR_COL,
+ "background", BACKGROUND_COLOR_COL,
+ NULL);
+
+ gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
+ GINT_TO_POINTER(SKEW_COLUMN), NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, SKEW_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 110);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* IP BW(kbps). */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer,
+ "text", IPBW_COLUMN,
+ "foreground", FOREGROUND_COLOR_COL,
+ "background", BACKGROUND_COLOR_COL,
+ NULL);
+
+ gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
+ GINT_TO_POINTER(IPBW_COLUMN), NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, IPBW_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Marker. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Marker", renderer,
+ "text", MARKER_COLUMN,
+ "foreground", FOREGROUND_COLOR_COL,
+ "background", BACKGROUND_COLOR_COL,
+ NULL);
+
+ gtk_tree_view_column_set_cell_data_func(column, renderer, rtp_boolean_data_func,
+ GINT_TO_POINTER(MARKER_COLUMN), NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, MARKER_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Status. */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ( "Status", renderer,
+ "text", STATUS_COLUMN,
+ "foreground", FOREGROUND_COLOR_COL,
+ "background", BACKGROUND_COLOR_COL,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, STATUS_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Now enable the sorting of each column */
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
+
+ /* Setup the selection handler */
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+
+ g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
+ G_CALLBACK (on_list_select_row),
+ user_data);
+ return list;
+}
+
+/****************************************************************************/
+/* Create the dialog box with all widgets */
+static void create_rtp_dialog(user_data_t* user_data)
+{
+ GtkWidget *window = NULL;
+ GtkWidget *list_fwd;
+ GtkWidget *list_rev;
+ GtkWidget *label_stats_fwd;
+ GtkWidget *label_stats_rev;
+ GtkWidget *notebook;
+
+ GtkWidget *main_vb, *page, *page_r;
+ GtkWidget *label;
+ GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
+ GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
+#ifdef HAVE_LIBPORTAUDIO
+ GtkWidget *player_bt = NULL;
+#endif /* HAVE_LIBPORTAUDIO */
+ GtkWidget *graph_bt;
+ gchar label_forward[150];
+ gchar label_forward_tree[150];
+ gchar label_reverse[150];
+
+ gchar str_ip_src[16];
+ gchar str_ip_dst[16];
+
+ window = dlg_window_new("Wireshark: RTP Stream Analysis"); /* transient_for top_level */
+ gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
+ gtk_container_add(GTK_CONTAINER(window), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* Notebooks... */
+ g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)), sizeof(str_ip_src));
+ g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)), sizeof(str_ip_dst));
+
+ g_snprintf(label_forward, sizeof(label_forward),
+ "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
+ str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
+
+ g_snprintf(label_forward_tree, sizeof(label_forward_tree),
+ "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
+ str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
+
+
+ g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)), sizeof(str_ip_src));
+ g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)), sizeof(str_ip_dst));
+
+ g_snprintf(label_reverse, sizeof(label_reverse),
+ "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
+ str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
+
+ /* Start a notebook for flipping between sets of changes */
+ notebook = gtk_notebook_new();
+ gtk_container_add(GTK_CONTAINER(main_vb), notebook);
+ g_object_set_data(G_OBJECT(window), "notebook", notebook);
+
+ user_data->dlg.notebook_signal_id =
+ g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
+
+ /* page for forward connection */
+ page = gtk_vbox_new(FALSE, 8);
+ gtk_container_set_border_width(GTK_CONTAINER(page), 8);
+
+ /* direction label */
+ label = gtk_label_new(label_forward);
+ gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
+
+ /* place for some statistics */
+ label_stats_fwd = gtk_label_new("\n");
+ gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
+
+ /* scrolled window */
+ scrolled_window = scrolled_window_new(NULL, NULL);
+
+ /* packet list */
+ list_fwd = create_list(user_data);
+ gtk_widget_show(list_fwd);
+ gtk_container_add(GTK_CONTAINER(scrolled_window), list_fwd);
+ gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
+ gtk_widget_show(scrolled_window);
+
+ /* tab */
+ label = gtk_label_new(" Forward Direction ");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
+
+ /* same page for reversed connection */
+ page_r = gtk_vbox_new(FALSE, 8);
+ gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
+ label = gtk_label_new(label_reverse);
+ gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
+ label_stats_rev = gtk_label_new("\n");
+ gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
+
+ scrolled_window_r = scrolled_window_new(NULL, NULL);
+
+ list_rev = create_list(user_data);
+ gtk_widget_show(list_rev);
+ gtk_container_add(GTK_CONTAINER(scrolled_window_r), list_rev);
+ gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
+ gtk_widget_show(scrolled_window_r);
+
+ label = gtk_label_new(" Reversed Direction ");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
+
+ /* page for help&about or future */
+#if 0
+ page_help = gtk_hbox_new(FALSE, 5);
+ label = gtk_label_new(" Future ");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
+ frame = gtk_frame_new("");
+ text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
+ gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
+ gtk_container_add(GTK_CONTAINER(frame), text);
+ gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
+ gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
+#endif
+
+ /* show all notebooks */
+ gtk_widget_show_all(notebook);
+
+ /* buttons */
+ box4 = gtk_hbutton_box_new();
+ gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
+ gtk_box_set_spacing(GTK_BOX (box4), 0);
+ gtk_widget_show(box4);
+
+ voice_bt = gtk_button_new_with_label("Save payload...");
+ gtk_container_add(GTK_CONTAINER(box4), voice_bt);
+ gtk_widget_show(voice_bt);
+ g_signal_connect(voice_bt, "clicked", G_CALLBACK(on_save_bt_clicked), user_data);
+
+ csv_bt = gtk_button_new_with_label("Save as CSV...");
+ gtk_container_add(GTK_CONTAINER(box4), csv_bt);
+ gtk_widget_show(csv_bt);
+ g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
+
+ refresh_bt = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
+ gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
+ gtk_widget_show(refresh_bt);
+ g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
+
+ goto_bt = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
+ gtk_container_add(GTK_CONTAINER(box4), goto_bt);
+ gtk_widget_show(goto_bt);
+ g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked_lst), user_data);
+
+ graph_bt = gtk_button_new_with_label("Graph");
+ gtk_container_add(GTK_CONTAINER(box4), graph_bt);
+ gtk_widget_show(graph_bt);
+ g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
+
+#ifdef HAVE_LIBPORTAUDIO
+ player_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_AUDIO_PLAYER);
+ gtk_container_add(GTK_CONTAINER(box4), player_bt);
+ gtk_widget_show(player_bt);
+ g_signal_connect(player_bt, "clicked", G_CALLBACK(on_player_bt_clicked), NULL);
+ /*gtk_widget_set_tooltip_text (player_bt, "Launch the RTP player to listen the audio stream");*/
+#endif /* HAVE_LIBPORTAUDIO */
+
+ next_bt = gtk_button_new_with_label("Next non-Ok");
+ gtk_container_add(GTK_CONTAINER(box4), next_bt);
+ gtk_widget_show(next_bt);
+ g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked_list), user_data);
+
+ close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_container_add(GTK_CONTAINER(box4), close_bt);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(close_bt, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
+#endif
+ gtk_widget_show(close_bt);
+ window_set_cancel_button(window, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), user_data);
+
+ gtk_widget_show(window);
+ window_present(window);
+
+
+ /* some widget references need to be saved for outside use */
+ user_data->dlg.window = window;
+ user_data->dlg.list_fwd = list_fwd;
+ user_data->dlg.list_rev = list_rev;
+ user_data->dlg.label_stats_fwd = label_stats_fwd;
+ user_data->dlg.label_stats_rev = label_stats_rev;
+ user_data->dlg.notebook = notebook;
+ user_data->dlg.selected_list = list_fwd;
+ user_data->dlg.number_of_nok = 0;
+
+ /*
+ * select the initial row
+ */
+ gtk_widget_grab_focus(list_fwd);
+
+}
+
+
+/****************************************************************************/
+static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
+ const gchar* proto_field, guint32* p_result)
+{
+ field_info *finfo;
+ proto_node *proto_sibling_node;
+ header_field_info *hfssrc;
+ ipv4_addr *ipv4;
+
+ finfo = PNODE_FINFO(ptree_node);
+
+ g_assert(finfo && "Caller passed top of the protocol tree. Expected child node");
+
+ if (hfinformation==(finfo->hfinfo)) {
+ hfssrc = proto_registrar_get_byname(proto_field);
+ if (hfssrc == NULL)
+ return FALSE;
+ for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
+ ptree_node=ptree_node->next) {
+ finfo=PNODE_FINFO(ptree_node);
+ if (hfssrc==finfo->hfinfo) {
+ if (hfinformation->type==FT_IPv4) {
+ ipv4 = fvalue_get(&finfo->value);
+ *p_result = ipv4_get_net_order_addr(ipv4);
+ }
+ else {
+ *p_result = fvalue_get_uinteger(&finfo->value);
+ }
+ return TRUE;
+ }
+ }
+ if(!ptree_node)
+ return FALSE;
+ }
+
+ proto_sibling_node = ptree_node->next;
+
+ if (proto_sibling_node) {
+ return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
+ }
+ else
+ return FALSE;
+}
+
+/****************************************************************************/
+static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
+ const gchar* proto_name,
+ const gchar* proto_field,
+ guint32* p_result)
+{
+ proto_node *ptree_node;
+ header_field_info *hfinformation;
+
+ hfinformation = proto_registrar_get_byname(proto_name);
+ if (hfinformation == NULL)
+ return FALSE;
+
+ ptree_node = ((proto_node *)protocol_tree)->first_child;
+ if (!ptree_node)
+ return FALSE;
+
+ return process_node(ptree_node, hfinformation, proto_field, p_result);
+}
+
+
+/****************************************************************************/
+void rtp_analysis(
+ address *ip_src_fwd,
+ guint16 port_src_fwd,
+ address *ip_dst_fwd,
+ guint16 port_dst_fwd,
+ guint32 ssrc_fwd,
+ address *ip_src_rev,
+ guint16 port_src_rev,
+ address *ip_dst_rev,
+ guint16 port_dst_rev,
+ guint32 ssrc_rev
+ )
+{
+ user_data_t *user_data;
+ int fd;
+ int i;
+ static GdkColor col[MAX_GRAPHS] = {
+ {0, 0x0000, 0x0000, 0x0000}, /* Black */
+ {0, 0xffff, 0x0000, 0x0000}, /* Red */
+ {0, 0x0000, 0xffff, 0x0000}, /* Green */
+ {0, 0xdddd, 0xcccc, 0x6666}, /* Light amber yellow */
+ {0, 0x6666, 0xcccc, 0xdddd}, /* Light bluish cyan */
+ {0, 0x0000, 0x0000, 0xffff} /* Blue */
+ };
+#if GTK_CHECK_VERSION(3,0,0)
+ static GdkRGBA rgba_col[MAX_GRAPHS] = {
+ {0.0, 0.0, 0.0, 1.0}, /* Black */
+ {1.0, 0.0, 0.1, 1.0}, /* Red */
+ {0.0, 1.0, 0.0, 1.0}, /* Green */
+ {0.867, 0.800, 0.400, 1.0}, /* Light amber yellow */
+ {0.400, 0.800, 0.867, 1.0}, /* Light bluish cyan */
+ {0.0, 0.0, 1.0, 1.0}, /* Blue */
+ };
+#endif
+
+ char *tempname;
+
+ /* init */
+ user_data = g_malloc(sizeof(user_data_t));
+
+ COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
+ user_data->port_src_fwd = port_src_fwd;
+ COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
+ user_data->port_dst_fwd = port_dst_fwd;
+ user_data->ssrc_fwd = ssrc_fwd;
+ COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
+ user_data->port_src_rev = port_src_rev;
+ COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
+ user_data->port_dst_rev = port_dst_rev;
+ user_data->ssrc_rev = ssrc_rev;
+
+
+ /* file names for storing sound data */
+ /*XXX: check for errors*/
+ fd = create_tempfile(&tempname, "wireshark_rtp_f");
+ user_data->f_tempname = g_strdup(tempname);
+ ws_close(fd);
+ fd = create_tempfile(&tempname, "wireshark_rtp_r");
+ user_data->r_tempname = g_strdup(tempname);
+ ws_close(fd);
+ user_data->forward.saveinfo.fp = NULL;
+ user_data->reversed.saveinfo.fp = NULL;
+ user_data->dlg.save_voice_as_w = NULL;
+ user_data->dlg.save_csv_as_w = NULL;
+ user_data->dlg.dialog_graph.window = NULL;
+
+ /* init dialog_graph */
+ user_data->dlg.dialog_graph.needs_redraw=TRUE;
+ user_data->dlg.dialog_graph.interval_index=DEFAULT_TICK_INTERVAL_VALUES_INDEX;
+ user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_INTERVAL_VALUES_INDEX];
+ user_data->dlg.dialog_graph.draw_area=NULL;
+#if GTK_CHECK_VERSION(2,22,0)
+ user_data->dlg.dialog_graph.surface=NULL;
+#else
+ user_data->dlg.dialog_graph.pixmap=NULL;
+#endif
+ user_data->dlg.dialog_graph.scrollbar=NULL;
+ user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
+ user_data->dlg.dialog_graph.surface_width=500;
+ user_data->dlg.dialog_graph.surface_height=200;
+ user_data->dlg.dialog_graph.pixels_per_tick_index=DEFAULT_PIXELS_PER_TICK_INDEX;
+ user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK_INDEX];
+ user_data->dlg.dialog_graph.max_y_units_index=AUTO_MAX_YSCALE_INDEX;
+ user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
+ user_data->dlg.dialog_graph.last_interval=0xffffffff;
+ user_data->dlg.dialog_graph.max_interval=0;
+ user_data->dlg.dialog_graph.num_items=0;
+ user_data->dlg.dialog_graph.start_time = -1;
+
+ for(i=0;i<MAX_GRAPHS;i++){
+ user_data->dlg.dialog_graph.graph[i].color.pixel=0;
+ user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
+ user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
+ user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
+#if GTK_CHECK_VERSION(3,0,0)
+ user_data->dlg.dialog_graph.graph[i].rgba_color.red=rgba_col[i].red;
+ user_data->dlg.dialog_graph.graph[i].rgba_color.green=rgba_col[i].green;
+ user_data->dlg.dialog_graph.graph[i].rgba_color.blue=rgba_col[i].blue;
+ user_data->dlg.dialog_graph.graph[i].rgba_color.alpha=rgba_col[i].alpha;
+#endif
+ user_data->dlg.dialog_graph.graph[i].display=TRUE;
+ user_data->dlg.dialog_graph.graph[i].display_button=NULL;
+ user_data->dlg.dialog_graph.graph[i].ud=user_data;
+ }
+
+ /* create the dialog box */
+ create_rtp_dialog(user_data);
+
+ /* proceed as if the Refresh button would have been pressed */
+ on_refresh_bt_clicked(NULL, user_data);
+}
+
+/****************************************************************************/
+/* entry point from main menu */
+void rtp_analysis_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ address ip_src_fwd;
+ guint16 port_src_fwd;
+ address ip_dst_fwd;
+ guint16 port_dst_fwd;
+ guint32 ssrc_fwd = 0;
+ address ip_src_rev;
+ guint16 port_src_rev;
+ address ip_dst_rev;
+ guint16 port_dst_rev;
+ guint32 ssrc_rev = 0;
+ unsigned int version_fwd;
+
+ gchar filter_text[256];
+ dfilter_t *sfcode;
+ capture_file *cf;
+ epan_dissect_t edt;
+ gboolean frame_matched;
+ frame_data *fdata;
+ GList *strinfo_list;
+ GList *filtered_list = NULL;
+ rtp_stream_info_t *strinfo;
+ guint nfound;
+
+ /* Try to compile the filter. */
+ g_strlcpy(filter_text,"rtp && rtp.version && rtp.ssrc && (ip || ipv6)",sizeof(filter_text));
+ if (!dfilter_compile(filter_text, &sfcode)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
+ return;
+ }
+ /* we load the current file into cf variable */
+ cf = &cfile;
+ fdata = cf->current_frame;
+
+ /* we are on the selected frame now */
+ if (fdata == NULL)
+ return; /* if we exit here it's an error */
+
+ /* dissect the current frame */
+ if (!cf_read_frame(cf, fdata))
+ return; /* error reading the frame */
+ epan_dissect_init(&edt, TRUE, FALSE);
+ epan_dissect_prime_dfilter(&edt, sfcode);
+ epan_dissect_run(&edt, &cf->pseudo_header, cf->pd, fdata, NULL);
+
+ /* if it is not an rtp frame, show the rtpstream dialog */
+ frame_matched = dfilter_apply_edt(sfcode, &edt);
+ if (frame_matched != TRUE) {
+ epan_dissect_cleanup(&edt);
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "You didn't choose a RTP packet!");
+ return;
+ }
+
+ /* ok, it is a RTP frame, so let's get the ip and port values */
+ COPY_ADDRESS(&(ip_src_fwd), &(edt.pi.src))
+ COPY_ADDRESS(&(ip_dst_fwd), &(edt.pi.dst))
+ port_src_fwd = edt.pi.srcport;
+ port_dst_fwd = edt.pi.destport;
+
+ /* assume the inverse ip/port combination for the reverse direction */
+ COPY_ADDRESS(&(ip_src_rev), &(edt.pi.dst))
+ COPY_ADDRESS(&(ip_dst_rev), &(edt.pi.src))
+ port_src_rev = edt.pi.destport;
+ port_dst_rev = edt.pi.srcport;
+
+ /* check if it is RTP Version 2 */
+ if (!get_int_value_from_proto_tree(edt.tree, "rtp", "rtp.version", &version_fwd) || version_fwd != 2) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "RTP Version != 2 isn't supported!");
+ return;
+ }
+
+ /* now we need the SSRC value of the current frame */
+ if (!get_int_value_from_proto_tree(edt.tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "SSRC value couldn't be found!");
+ return;
+ }
+
+ /* Scan for rtpstream */
+ rtpstream_scan();
+ /* search for reversed direction in the global rtp streams list */
+ nfound = 0;
+ strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
+ while (strinfo_list)
+ {
+ strinfo = (rtp_stream_info_t*)(strinfo_list->data);
+ if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
+ && strinfo->src_port==port_src_fwd
+ && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
+ && strinfo->dest_port==port_dst_fwd)
+ {
+ filtered_list = g_list_prepend(filtered_list, strinfo);
+ }
+
+ if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
+ && strinfo->src_port==port_src_rev
+ && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
+ && strinfo->dest_port==port_dst_rev)
+ {
+ ++nfound;
+ filtered_list = g_list_append(filtered_list, strinfo);
+ if (ssrc_rev==0)
+ ssrc_rev = strinfo->ssrc;
+ }
+
+ strinfo_list = g_list_next(strinfo_list);
+ }
+
+ /* if more than one reverse streams found, we let the user choose the right one */
+ if (nfound>1) {
+ rtpstream_dlg_show(filtered_list);
+ return;
+ }
+ else {
+ rtp_analysis(
+ &ip_src_fwd,
+ port_src_fwd,
+ &ip_dst_fwd,
+ port_dst_fwd,
+ ssrc_fwd,
+ &ip_src_rev,
+ port_src_rev,
+ &ip_dst_rev,
+ port_dst_rev,
+ ssrc_rev
+ );
+ }
+}
+
+/****************************************************************************/
+static void
+rtp_analysis_init(const char *dummy _U_,void* userdata _U_)
+{
+ rtp_analysis_cb(NULL, NULL);
+}
+
+/****************************************************************************/
+void
+register_tap_listener_rtp_analysis(void)
+{
+ register_stat_cmd_arg("rtp", rtp_analysis_init, NULL);
+}
+
diff --git a/ui/gtk/rtp_analysis.h b/ui/gtk/rtp_analysis.h
new file mode 100644
index 0000000000..28972b1e99
--- /dev/null
+++ b/ui/gtk/rtp_analysis.h
@@ -0,0 +1,131 @@
+/* rtp_analysis.h
+ * RTP analysis addition for Wireshark
+ *
+ * $Id$
+ *
+ * Copyright 2003, Alcatel Business Systems
+ * By Lars Ruoff <lars.ruoff@gmx.net>
+ *
+ * based on tap_rtp.c
+ * Copyright 2003, Iskratel, Ltd, Kranj
+ * By Miha Jemec <m.jemec@iskratel.si>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __RTP_ANALYSIS_H__
+#define __RTP_ANALYSIS_H__
+
+#include <glib.h>
+#include <epan/address.h>
+#include <epan/packet_info.h>
+
+/** @file
+ * ???
+ * @todo what's this?
+ */
+
+void rtp_analysis(
+ address *ip_src_fwd,
+ guint16 port_src_fwd,
+ address *ip_dst_fwd,
+ guint16 port_dst_fwd,
+ guint32 ssrc_fwd,
+ address *ip_src_rev,
+ guint16 port_src_rev,
+ address *ip_dst_rev,
+ guint16 port_dst_rev,
+ guint32 ssrc_rev
+ );
+
+/****************************************************************************/
+/* structure that holds the information about the forward and reversed direction */
+typedef struct _bw_history_item {
+ double time;
+ guint32 bytes;
+} bw_history_item;
+
+#define BUFF_BW 300
+
+typedef struct _tap_rtp_stat_t {
+ gboolean first_packet; /* do not use in code that is called after rtp_packet_analyse */
+ /* use (flags & STAT_FLAG_FIRST) instead */
+ /* all of the following fields will be initialized after
+ * rtp_packet_analyse has been called
+ */
+ guint32 flags; /* see STAT_FLAG-defines below */
+ guint16 seq_num;
+ guint32 timestamp;
+ guint32 first_timestamp;
+ guint32 delta_timestamp;
+ double bandwidth;
+ bw_history_item bw_history[BUFF_BW];
+ guint16 bw_start_index;
+ guint16 bw_index;
+ guint32 total_bytes;
+ guint32 clock_rate;
+ double delta;
+ double jitter;
+ double diff;
+ double skew;
+ double sumt;
+ double sumTS;
+ double sumt2;
+ double sumtTS;
+ double time; /* Unit is ms */
+ double start_time;
+ double lastnominaltime;
+ double max_delta;
+ double max_jitter;
+ double max_skew;
+ double mean_jitter;
+ guint32 max_nr;
+ guint16 start_seq_nr;
+ guint16 stop_seq_nr;
+ guint32 total_nr;
+ guint32 sequence;
+ gboolean under;
+ gint cycles;
+ guint16 pt;
+ int reg_pt;
+} tap_rtp_stat_t;
+
+#define PT_UNDEFINED -1
+
+/* status flags for the flags parameter in tap_rtp_stat_t */
+#define STAT_FLAG_FIRST 0x001
+#define STAT_FLAG_MARKER 0x002
+#define STAT_FLAG_WRONG_SEQ 0x004
+#define STAT_FLAG_PT_CHANGE 0x008
+#define STAT_FLAG_PT_CN 0x010
+#define STAT_FLAG_FOLLOW_PT_CN 0x020
+#define STAT_FLAG_REG_PT_CHANGE 0x040
+#define STAT_FLAG_WRONG_TIMESTAMP 0x080
+#define STAT_FLAG_PT_T_EVENT 0x100
+
+/* forward */
+struct _rtp_info;
+
+/* function for analysing an RTP packet. Called from rtp_analysis and rtp_streams */
+extern int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
+ packet_info *pinfo,
+ const struct _rtp_info *rtpinfo);
+
+
+#endif /* __RTP_ANALYSIS_H__ */
diff --git a/ui/gtk/rtp_player.c b/ui/gtk/rtp_player.c
new file mode 100644
index 0000000000..e0f3f76da4
--- /dev/null
+++ b/ui/gtk/rtp_player.c
@@ -0,0 +1,2530 @@
+ /* rtp_player.c
+ *
+ * $Id$
+ *
+ * Copyright 2006, Alejandro Vaquero <alejandrovaquero@yahoo.com>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1999 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Here is a summary on how this works:
+ * - The VoipCalls will call add_rtp_packet() every time there is an RTP
+ * packet
+ * - add_rtp_packet() will add the RTP packet in a RTP stream struct, and
+ * create the RTP stream if it is the first RTP packet in the stream.
+ * - Each new RTP stream will be added to a list of RTP streams, called
+ * rtp_streams_list
+ * - When the user clicks "Player" in the VoipCall dialogue,
+ * rtp_player_init() is called.
+ * - rtp_player_init() creates the main dialog, and it calls:
+ * + mark_rtp_stream_to_play() to mark the RTP streams that needs to be
+ * displayed. These are the RTP streams that match the selected calls in
+ * the VoipCall dlg.
+ * + decode_rtp_stream() this will decode the RTP packets in each RTP
+ * stream, and will also create the RTP channels. An RTP channel is a
+ * group of RTP streams that have in common the source and destination
+ * IP and UDP ports. The RTP channels is what the user will listen in
+ * one of the two Audio channels.
+ * The RTP channels are stored in the hash table rtp_channels_hash
+ * + add_channel_to_window() will create and add the Audio graphic
+ * representation in the main window
+ * - When the user clicks the check box to listen one of the Audio channels,
+ * the structure rtp_channels is filled to play one or two RTP channels
+ * (a max of two channels can be listened at a given moment)
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_LIBPORTAUDIO
+#include <math.h>
+#include <string.h>
+#include "portaudio.h"
+
+#include <gtk/gtk.h>
+
+#include <epan/stats_tree.h>
+#include <epan/addr_resolv.h>
+#include <epan/dissectors/packet-rtp.h>
+#include <epan/rtp_pt.h>
+#include <epan/codecs.h>
+#include <epan/prefs.h>
+
+#include "../globals.h"
+#include "../simple_dialog.h"
+#include "../codecs/G711a/G711adecode.h"
+#include "../codecs/G711u/G711udecode.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/graph_analysis.h"
+#include "ui/gtk/voip_calls_dlg.h"
+#include "ui/gtk/voip_calls.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/rtp_player.h"
+#include "ui/gtk/stock_icons.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+/*define this symbol to compile with G729 and G723 codecs*/
+/*#define HAVE_G729_G723 1*/
+
+#ifdef HAVE_G729_G723
+#include "codecs/G729/G729decode.h"
+#include "codecs/G723/G723decode.h"
+#endif /* HAVE_G729_G723 */
+
+static gboolean initialized = FALSE;
+
+static voip_calls_tapinfo_t *voip_calls = NULL;
+
+/* Hash table with all the RTP streams */
+static GHashTable* rtp_streams_hash = NULL;
+
+/* List with all the RTP streams (this is used to decode them as it is sorted)*/
+static GList* rtp_streams_list = NULL;
+
+/* the window */
+static GtkWidget *rtp_player_dlg_w;
+static GtkWidget *channels_vb;
+static GtkWidget *main_scrolled_window = NULL;
+static GtkWidget *jitter_spinner;
+static GtkWidget *cb_use_rtp_timestamp;
+static GtkWidget *cb_view_as_time_of_day;
+static GtkWidget *bt_decode;
+static GtkWidget *bt_play;
+static GtkWidget *bt_pause;
+static GtkWidget *bt_stop;
+static GtkWidget *progress_bar;
+static GtkWidget *info_bar;
+static GtkWidget *stat_hbox;
+
+static guint32 total_packets;
+static guint32 total_frames;
+static guint32 progbar_count;
+
+static int new_jitter_buff;
+
+/* a hash table with the RTP streams to play per audio channel */
+static GHashTable *rtp_channels_hash = NULL;
+
+/* Port Audio stuff */
+#define SAMPLE_RATE (8000)
+#define NUM_CHANNELS (2)
+
+#define PA_SAMPLE_TYPE paInt16
+typedef gint16 SAMPLE;
+#define SAMPLE_SILENCE (0)
+#define FRAMES_PER_BUFFER (512)
+
+typedef struct _sample_t {
+ SAMPLE val;
+ guint8 status;
+} sample_t;
+
+#define S_NORMAL 0
+#define S_DROP_BY_JITT 1
+#define S_WRONG_SEQ 2
+#define S_WRONG_TIMESTAMP 3 /* The timestamp does not reflect the number of samples - samples have been dropped or silence inserted to match timestamp */
+#define S_SILENCE 4 /* Silence inserted by Wireshark, rather than contained in a packet */
+
+/* Display channels constants */
+#define MULT 80
+#define CHANNEL_WIDTH 500
+#define CHANNEL_HEIGHT 100
+#define MAX_TIME_LABEL 10
+#define HEIGHT_TIME_LABEL 18
+#define MAX_NUM_COL_CONV 10
+
+#if PORTAUDIO_API_1
+static PortAudioStream *pa_stream;
+#else /* PORTAUDIO_API_1 */
+static PaStream *pa_stream;
+#endif /* PORTAUDIO_API_1 */
+
+/* defines a RTP stream */
+typedef struct _rtp_stream_info {
+ address src_addr;
+ guint16 src_port;
+ address dest_addr;
+ guint16 dest_port;
+ guint32 ssrc;
+ guint32 first_frame_number; /* first RTP frame for the stream */
+ double start_time; /* RTP stream start time in ms */
+ nstime_t start_time_abs;
+ gboolean play;
+ guint16 call_num;
+ GList* rtp_packets_list; /* List of RTP packets in the stream */
+ guint32 num_packets;
+} rtp_stream_info_t;
+
+
+/* defines the RTP streams to be played in an audio channel */
+typedef struct _rtp_channel_info {
+ double start_time; /* RTP stream start time in ms */
+ nstime_t start_time_abs;
+ double end_time; /* RTP stream end time in ms */
+ GArray *samples; /* the array with decoded audio */
+ guint16 call_num;
+ gboolean selected;
+ guint32 frame_index;
+ guint32 drop_by_jitter_buff;
+ guint32 out_of_seq;
+ guint32 wrong_timestamp;
+ guint32 max_frame_index;
+ GtkWidget *check_bt;
+ GtkWidget *separator;
+ GtkWidget *scroll_window;
+ GtkWidget *draw_area;
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_surface_t *surface;
+#else
+ GdkPixmap *pixmap;
+#endif
+ GtkAdjustment *h_scrollbar_adjustment;
+ GdkPixbuf* cursor_pixbuf;
+#if PORTAUDIO_API_1
+ PaTimestamp cursor_prev;
+#else /* PORTAUDIO_API_1 */
+ PaTime cursor_prev;
+#endif /* PORTAUDIO_API_1 */
+ GdkColor bg_color[MAX_NUM_COL_CONV+1];
+ gboolean cursor_catch;
+ rtp_stream_info_t *first_stream; /* This is the first RTP stream in the channel */
+ guint32 num_packets;
+} rtp_channel_info_t;
+
+/* defines a RTP packet */
+typedef struct _rtp_packet {
+ struct _rtp_info *info; /* the RTP dissected info */
+ double arrive_offset; /* arrive offset time since the begining of the stream in ms */
+ guint8* payload_data;
+} rtp_packet_t;
+
+/* defines the two RTP channels to be played */
+typedef struct _rtp_play_channles {
+ rtp_channel_info_t* rci[2]; /* Channels to be played */
+ guint32 start_index[2];
+ guint32 end_index[2];
+ int channel;
+ guint32 max_frame_index;
+ guint32 frame_index;
+ gboolean pause;
+ gboolean stop;
+ gint32 pause_duration;
+#if PORTAUDIO_API_1
+ PaTimestamp out_diff_time;
+#else /* PORTAUDIO_API_1 */
+ PaTime out_diff_time;
+ PaTime pa_start_time;
+#endif /* PORTAUDIO_API_1 */
+} rtp_play_channels_t;
+
+/* The two RTP channels to play */
+static rtp_play_channels_t *rtp_channels = NULL;
+
+typedef struct _rtp_decoder_t {
+ codec_handle_t handle;
+ void *context;
+} rtp_decoder_t;
+
+
+/****************************************************************************/
+static void
+rtp_key_destroy(gpointer key)
+{
+ g_free(key);
+ key = NULL;
+}
+
+/****************************************************************************/
+static void
+rtp_channel_value_destroy(gpointer rci_arg)
+{
+ rtp_channel_info_t *rci = rci_arg;
+
+ g_array_free(rci->samples, TRUE);
+ g_free(rci);
+ rci = NULL;
+}
+
+/****************************************************************************/
+static void
+rtp_stream_value_destroy(gpointer rsi_arg)
+{
+ rtp_stream_info_t *rsi = rsi_arg;
+ GList* rtp_packets_list;
+ rtp_packet_t *rp;
+
+ rtp_packets_list = g_list_first(rsi->rtp_packets_list);
+ while (rtp_packets_list)
+ {
+ rp = rtp_packets_list->data;
+
+ g_free(rp->info);
+ g_free(rp->payload_data);
+ g_free(rp);
+ rp = NULL;
+
+ rtp_packets_list = g_list_next(rtp_packets_list);
+ }
+ g_free((void *)(rsi->src_addr.data));
+ g_free((void *)(rsi->dest_addr.data));
+ g_free(rsi);
+ rsi = NULL;
+}
+
+/****************************************************************************/
+static void
+rtp_decoder_value_destroy(gpointer dec_arg)
+{
+ rtp_decoder_t *dec = dec_arg;
+
+ if (dec->handle)
+ codec_release(dec->handle, dec->context);
+ g_free(dec_arg);
+}
+
+/****************************************************************************/
+static void
+set_sensitive_check_bt(gchar *key _U_ , rtp_channel_info_t *rci, guint *stop _U_ )
+{
+ gtk_widget_set_sensitive(rci->check_bt, !(*stop));
+}
+
+/****************************************************************************/
+static void
+bt_state(gboolean decode, gboolean play, gboolean pause, gboolean stop)
+{
+ gboolean new_jitter_value = FALSE;
+ gboolean false_val = FALSE;
+
+ gtk_widget_set_sensitive(bt_decode, decode);
+ gtk_widget_set_sensitive(cb_use_rtp_timestamp, decode);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb_use_rtp_timestamp))) {
+ gtk_widget_set_sensitive(jitter_spinner, FALSE);
+ } else {
+ gtk_widget_set_sensitive(jitter_spinner, decode);
+ }
+
+ if (new_jitter_buff != (int) gtk_spin_button_get_value((GtkSpinButton * )jitter_spinner)) {
+ new_jitter_value = TRUE;
+ }
+
+ /* set the sensitive state of play only if there is a channel selected */
+ if ( play && (rtp_channels->rci[0] || rtp_channels->rci[1]) && !new_jitter_value) {
+ gtk_widget_set_sensitive(bt_play, TRUE);
+ } else {
+ gtk_widget_set_sensitive(bt_play, FALSE);
+ }
+
+ if (!new_jitter_value) {
+ gtk_widget_set_sensitive(bt_pause, pause);
+ gtk_widget_set_sensitive(bt_stop, stop);
+
+ /* Set sensitive to the check buttons based on the STOP state */
+ if (rtp_channels_hash)
+ g_hash_table_foreach( rtp_channels_hash, (GHFunc)set_sensitive_check_bt, &stop);
+ } else {
+ gtk_widget_set_sensitive(bt_pause, FALSE);
+ gtk_widget_set_sensitive(bt_stop, FALSE);
+
+ if (rtp_channels_hash)
+ g_hash_table_foreach( rtp_channels_hash, (GHFunc)set_sensitive_check_bt, &false_val);
+ }
+}
+
+/****************************************************************************/
+void
+add_rtp_packet(const struct _rtp_info *rtp_info, packet_info *pinfo)
+{
+ rtp_stream_info_t *stream_info = NULL;
+ rtp_packet_t *new_rtp_packet;
+ GString *key_str = NULL;
+
+ /* create the streams hash if it doen't exist */
+ if (!rtp_streams_hash)
+ rtp_streams_hash = g_hash_table_new_full( g_str_hash, g_str_equal, rtp_key_destroy, rtp_stream_value_destroy);
+
+ /* Create a hash key to lookup in the RTP streams hash table
+ * uses: src_ip:src_port dst_ip:dst_port ssrc
+ */
+ key_str = g_string_new("");
+ g_string_printf(key_str, "%s:%d %s:%d %d", get_addr_name(&(pinfo->src)),
+ pinfo->srcport, get_addr_name(&(pinfo->dst)),
+ pinfo->destport, rtp_info->info_sync_src );
+
+ /* lookup for this RTP packet in the stream hash table */
+ stream_info = g_hash_table_lookup( rtp_streams_hash, key_str->str);
+
+ /* if it is not in the hash table, create a new stream */
+ if (stream_info==NULL) {
+ stream_info = g_malloc(sizeof(rtp_stream_info_t));
+ COPY_ADDRESS(&(stream_info->src_addr), &(pinfo->src));
+ stream_info->src_port = pinfo->srcport;
+ COPY_ADDRESS(&(stream_info->dest_addr), &(pinfo->dst));
+ stream_info->dest_port = pinfo->destport;
+ stream_info->ssrc = rtp_info->info_sync_src;
+ stream_info->rtp_packets_list = NULL;
+ stream_info->first_frame_number = pinfo->fd->num;
+ stream_info->start_time = nstime_to_msec(&pinfo->fd->rel_ts);
+ stream_info->start_time_abs = pinfo->fd->abs_ts;
+ stream_info->call_num = 0;
+ stream_info->play = FALSE;
+ stream_info->num_packets = 0;
+
+ g_hash_table_insert(rtp_streams_hash, g_strdup(key_str->str), stream_info);
+
+ /* Add the element to the List too. The List is used to decode the packets because it is sorted */
+ rtp_streams_list = g_list_append(rtp_streams_list, stream_info);
+ }
+
+ /* increment the number of packets in this stream, this is used for the progress bar and statistics */
+ stream_info->num_packets++;
+
+ /* Add the RTP packet to the list */
+ new_rtp_packet = g_malloc(sizeof(rtp_packet_t));
+ new_rtp_packet->info = g_malloc(sizeof(struct _rtp_info));
+
+ memcpy(new_rtp_packet->info, rtp_info, sizeof(struct _rtp_info));
+ new_rtp_packet->arrive_offset = nstime_to_msec(&pinfo->fd->rel_ts) - stream_info->start_time;
+ /* copy the RTP payload to the rtp_packet to be decoded later */
+ if (rtp_info->info_all_data_present && (rtp_info->info_payload_len != 0)) {
+ new_rtp_packet->payload_data = g_malloc(rtp_info->info_payload_len);
+ memcpy(new_rtp_packet->payload_data, &(rtp_info->info_data[rtp_info->info_payload_offset]), rtp_info->info_payload_len);
+ } else {
+ new_rtp_packet->payload_data = NULL;
+ }
+
+ stream_info->rtp_packets_list = g_list_append(stream_info->rtp_packets_list, new_rtp_packet);
+
+ g_string_free(key_str, TRUE);
+}
+
+/****************************************************************************/
+/* Mark the RTP stream to be played. Use the voip_calls graph to see if the
+ * setup_frame is there and then if the associated voip_call is selected.
+ */
+static void
+mark_rtp_stream_to_play(gchar *key _U_ , rtp_stream_info_t *rsi, gpointer ptr _U_)
+{
+ GList* graph_list;
+ graph_analysis_item_t *graph_item;
+ GList* voip_calls_list;
+ voip_calls_info_t *tmp_voip_call;
+
+ /* Reset the "to be play" value because the user can close and reopen the RTP Player window
+ * and the streams are not reset in that case
+ */
+ rsi->play = FALSE;
+
+ /* and associate the RTP stream with a call using the first RTP packet in the stream */
+ graph_list = g_list_first(voip_calls->graph_analysis->list);
+ while (graph_list)
+ {
+ graph_item = graph_list->data;
+ if (rsi->first_frame_number == graph_item->fd->num) {
+ rsi->call_num = graph_item->conv_num;
+ /* if it is in the graph list, then check if the voip_call is selected */
+ voip_calls_list = g_list_first(voip_calls->callsinfo_list);
+ while (voip_calls_list)
+ {
+ tmp_voip_call = voip_calls_list->data;
+ if ( (tmp_voip_call->call_num == rsi->call_num) && (tmp_voip_call->selected == TRUE) ) {
+ rsi->play = TRUE;
+ total_packets += rsi->num_packets;
+ break;
+ }
+ voip_calls_list = g_list_next(voip_calls_list);
+ }
+ break;
+ }
+ graph_list = g_list_next(graph_list);
+ }
+}
+
+/****************************************************************************/
+/* Mark the ALL RTP stream to be played. This is called when calling the
+ * RTP player from the "RTP Analysis" window
+ */
+static void
+mark_all_rtp_stream_to_play(gchar *key _U_ , rtp_stream_info_t *rsi, gpointer ptr _U_)
+{
+ rsi->play = TRUE;
+ total_packets += rsi->num_packets;
+}
+
+/****************************************************************************/
+/* Decode a RTP packet
+ * Return the number of decoded bytes
+ */
+static int
+decode_rtp_packet(rtp_packet_t *rp, SAMPLE **out_buff, GHashTable *decoders_hash)
+{
+ unsigned int payload_type;
+ const gchar *p;
+ rtp_decoder_t *decoder;
+ SAMPLE *tmp_buff = NULL;
+ int tmp_buff_len;
+ int decoded_bytes = 0;
+
+ if ((rp->payload_data == NULL) || (rp->info->info_payload_len == 0) ) {
+ return 0;
+ }
+
+ payload_type = rp->info->info_payload_type;
+
+ /* Look for registered codecs */
+ decoder = g_hash_table_lookup(decoders_hash, GUINT_TO_POINTER(payload_type));
+ if (!decoder) { /* Put either valid or empty decoder into the hash table */
+ decoder = g_malloc(sizeof(rtp_decoder_t));
+ decoder->handle = NULL;
+ decoder->context = NULL;
+ p = match_strval_ext(payload_type, &rtp_payload_type_short_vals_ext);
+ if (p) {
+ decoder->handle = find_codec(p);
+ if (decoder->handle)
+ decoder->context = codec_init(decoder->handle);
+ }
+ g_hash_table_insert(decoders_hash, GUINT_TO_POINTER(payload_type), decoder);
+ }
+ if (decoder->handle) { /* Decode with registered codec */
+ tmp_buff_len = codec_decode(decoder->handle, decoder->context, rp->payload_data, rp->info->info_payload_len, NULL, NULL);
+ tmp_buff = g_malloc(tmp_buff_len);
+ decoded_bytes = codec_decode(decoder->handle, decoder->context, rp->payload_data, rp->info->info_payload_len, tmp_buff, &tmp_buff_len);
+ *out_buff = tmp_buff;
+ return decoded_bytes;
+ }
+
+ /* Try to decode with built-in codec */
+
+ switch (payload_type) {
+
+ case PT_PCMU: /* G.711 u-law */
+ tmp_buff = g_malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 1);
+ decodeG711u(rp->payload_data, rp->info->info_payload_len,
+ tmp_buff, &decoded_bytes);
+ break;
+
+ case PT_PCMA: /* G.711 A-law */
+ tmp_buff = g_malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 1);
+ decodeG711a(rp->payload_data, rp->info->info_payload_len,
+ tmp_buff, &decoded_bytes);
+ break;
+
+#ifdef HAVE_G729_G723
+ case PT_G729: /* G.729 */
+ /* G729 8kbps => 64kbps/8kbps = 8 */
+ /* Compensate for possible 2 octet SID frame (G.729B) */
+ tmp_buff = g_malloc(sizeof(SAMPLE) * ((rp->info->info_payload_len + 8) / 10) * 80);
+ decodeG729(rp->payload_data, rp->info->info_payload_len,
+ tmp_buff, &decoded_bytes);
+ break;
+
+ case PT_G723: /* G.723 */
+ if (rp->info->info_payload_len%24 == 0) /* G723 High 6.4kbps */
+ tmp_buff = g_malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 10); /* G723 High 64kbps/6.4kbps = 10 */
+ else if (rp->info->info_payload_len%20 == 0) /* G723 Low 5.3kbps */
+ tmp_buff = g_malloc(sizeof(SAMPLE) * rp->info->info_payload_len * 13); /* G723 High 64kbps/5.3kbps = 13 */
+ else {
+ return 0;
+ }
+ decodeG723(rp->payload_data, rp->info->info_payload_len,
+ tmp_buff, &decoded_bytes);
+ break;
+#endif /* HAVE_G729_G723 */
+
+ default:
+ /*
+ * XXX - return an error here, so the user gets told that
+ * we don't support this codec!
+ */
+ break;
+ }
+
+ *out_buff = tmp_buff;
+ return decoded_bytes;
+}
+
+/****************************************************************************/
+static void
+update_progress_bar(gfloat fraction)
+{
+
+ if GTK_IS_PROGRESS_BAR(progress_bar)
+ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar), fraction);
+
+ /* Force gtk to redraw the window before starting decoding the packet */
+ while (gtk_events_pending())
+ gtk_main_iteration();
+}
+
+/****************************************************************************/
+/* Decode the RTP streams and add them to the RTP channels struct
+ */
+static void
+decode_rtp_stream(rtp_stream_info_t *rsi, gpointer ptr _U_)
+{
+ GString *key_str = NULL;
+ rtp_channel_info_t *rci;
+ gboolean first = TRUE;
+ GList* rtp_packets_list;
+ rtp_packet_t *rp;
+
+ int i;
+ double rtp_time;
+ double rtp_time_prev;
+ double arrive_time;
+ double arrive_time_prev;
+ double start_time;
+ double start_rtp_time = 0;
+ double diff;
+ double pack_period;
+#ifdef DEBUG /* ?? */
+ double total_time;
+ double total_time_prev;
+#endif
+ gint32 silence_frames;
+ int seq;
+ double delay;
+ double prev_diff;
+#ifdef DEBUG /* ?? */
+ double mean_delay;
+ double variation;
+#endif
+ int decoded_bytes;
+ int decoded_bytes_prev;
+ int jitter_buff;
+ SAMPLE *out_buff = NULL;
+ sample_t silence;
+ sample_t sample;
+ guint8 status;
+ guint32 start_timestamp;
+ GHashTable *decoders_hash = NULL;
+
+ guint32 progbar_nextstep;
+ int progbar_quantum;
+ gfloat progbar_val;
+
+ silence.val = 0;
+ silence.status = S_NORMAL;
+
+ /* skip it if we are not going to play it */
+ if (rsi->play == FALSE) {
+ return;
+ }
+
+ /* get the static jitter buffer from the spinner gui */
+ jitter_buff = (int) gtk_spin_button_get_value((GtkSpinButton * )jitter_spinner);
+
+ /* Create a hash key to lookup in the RTP channels hash
+ * uses: src_ip:src_port dst_ip:dst_port call_num
+ */
+ key_str = g_string_new("");
+ g_string_printf(key_str, "%s:%d %s:%d %d", get_addr_name(&(rsi->src_addr)),
+ rsi->src_port, get_addr_name(&(rsi->dest_addr)),
+ rsi->dest_port, rsi->call_num );
+
+ /* create the rtp_channels_hash table if it doesn't exist */
+ if (!rtp_channels_hash) {
+ rtp_channels_hash = g_hash_table_new_full( g_str_hash, g_str_equal, rtp_key_destroy, rtp_channel_value_destroy);
+ }
+
+ /* lookup for this stream in the channel hash table */
+ rci = g_hash_table_lookup( rtp_channels_hash, key_str->str);
+
+ /* ..if it is not in the hash, create an entry */
+ if (rci == NULL) {
+ rci = g_malloc(sizeof(rtp_channel_info_t));
+ rci->call_num = rsi->call_num;
+ rci->start_time = rsi->start_time;
+ rci->start_time_abs = rsi->start_time_abs;
+ rci->end_time = rsi->start_time;
+ rci->selected = FALSE;
+ rci->frame_index = 0;
+ rci->drop_by_jitter_buff = 0;
+ rci->out_of_seq = 0;
+ rci->wrong_timestamp = 0;
+ rci->max_frame_index = 0;
+ rci->samples = g_array_new (FALSE, FALSE, sizeof(sample_t));
+ rci->check_bt = NULL;
+ rci->separator = NULL;
+ rci->draw_area = NULL;
+#if GTK_CHECK_VERSION(2,22,0)
+ rci->surface = NULL;
+#else
+ rci->pixmap = NULL;
+#endif
+ rci->h_scrollbar_adjustment = NULL;
+ rci->cursor_pixbuf = NULL;
+ rci->cursor_prev = 0;
+ rci->cursor_catch = FALSE;
+ rci->first_stream = rsi;
+ rci->num_packets = rsi->num_packets;
+ g_hash_table_insert(rtp_channels_hash, g_strdup(key_str->str), rci);
+ } else {
+ /* Add silence between the two streams if needed */
+ silence_frames = (gint32)( ((rsi->start_time - rci->end_time)/1000)*SAMPLE_RATE );
+ for (i = 0; i< silence_frames; i++) {
+ g_array_append_val(rci->samples, silence);
+ }
+ rci->num_packets += rsi->num_packets;
+ }
+
+ /* decode the RTP stream */
+ first = TRUE;
+ rtp_time = 0;
+ rtp_time_prev = 0;
+ decoded_bytes = 0;
+ decoded_bytes_prev = 0;
+ silence_frames = 0;
+ arrive_time = start_time = 0;
+ arrive_time_prev = 0;
+ pack_period = 0;
+#ifdef DEBUG /* ?? */
+ total_time = 0;
+ total_time_prev = 0;
+#endif
+ seq = 0;
+ delay = 0;
+ prev_diff = 0;
+#ifdef DEBUG /* ?? */
+ mean_delay = 0;
+ variation = 0;
+#endif
+ start_timestamp = 0;
+ decoders_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, rtp_decoder_value_destroy);
+
+ /* we update the progress bar 100 times */
+
+ /* Update the progress bar when it gets to this value. */
+ progbar_nextstep = 0;
+ /* When we reach the value that triggers a progress bar update,
+ bump that value by this amount. */
+ progbar_quantum = total_packets/100;
+
+ status = S_NORMAL;
+
+ rtp_packets_list = g_list_first(rsi->rtp_packets_list);
+ while (rtp_packets_list)
+ {
+
+ if (progbar_count >= progbar_nextstep) {
+ g_assert(total_packets > 0);
+
+ progbar_val = (gfloat) progbar_count / total_packets;
+
+ update_progress_bar(progbar_val);
+
+ progbar_nextstep += progbar_quantum;
+ }
+
+
+ rp = rtp_packets_list->data;
+ if (first == TRUE) {
+ start_timestamp = rp->info->info_timestamp; /* defined start_timestmp to avoid overflow in timestamp. TODO: handle the timestamp correctly */
+ start_rtp_time = 0;
+ rtp_time_prev = start_rtp_time;
+ first = FALSE;
+ seq = rp->info->info_seq_num - 1;
+ }
+
+ decoded_bytes = decode_rtp_packet(rp, &out_buff, decoders_hash);
+ if (decoded_bytes == 0) {
+ seq = rp->info->info_seq_num;
+ }
+
+ rtp_time = (double)(rp->info->info_timestamp-start_timestamp)/SAMPLE_RATE - start_rtp_time;
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb_use_rtp_timestamp))) {
+ arrive_time = rtp_time;
+ } else {
+ arrive_time = (double)rp->arrive_offset/1000 - start_time;
+ }
+
+ if (rp->info->info_seq_num != seq+1){
+ rci->out_of_seq++;
+ status = S_WRONG_SEQ;
+ }
+ seq = rp->info->info_seq_num;
+
+ diff = arrive_time - rtp_time;
+
+ delay = diff - prev_diff;
+ prev_diff = diff;
+ if (delay<0) delay = -delay;
+
+ if (diff<0) diff = -diff;
+
+#ifdef DEBUG
+ total_time = (double)rp->arrive_offset/1000;
+ printf("seq = %d arr = %f abs_diff = %f index = %d tim = %f ji=%d jb=%f\n",rp->info->info_seq_num,
+ total_time, diff, rci->samples->len, ((double)rci->samples->len/8000 - total_time)*1000, 0,
+ (mean_delay + 4*variation)*1000);
+ fflush(stdout);
+#endif
+ /* if the jitter buffer was exceeded */
+ if ( diff*1000 > jitter_buff ) {
+#ifdef DEBUG
+ printf("Packet drop by jitter buffer exceeded\n");
+#endif
+ rci->drop_by_jitter_buff++;
+ status = S_DROP_BY_JITT;
+
+ /* if there was a silence period (more than two packetization period) resync the source */
+ if ( (rtp_time - rtp_time_prev) > pack_period*2 ){
+#ifdef DEBUG
+ printf("Resync...\n");
+#endif
+ silence_frames = (gint32)((arrive_time - arrive_time_prev)*SAMPLE_RATE - decoded_bytes_prev/2);
+
+ /* Fix for bug 4119/5902: don't insert too many silence frames.
+ * XXX - is there a better thing to do here?
+ */
+#define MAX_SILENCE_FRAMES 240000
+ if (silence_frames > MAX_SILENCE_FRAMES)
+ silence_frames = MAX_SILENCE_FRAMES;
+
+ for (i = 0; i< silence_frames; i++) {
+ silence.status = status;
+ g_array_append_val(rci->samples, silence);
+
+ /* only mark the first in the silence that has the previous problem (S_DROP_BY_JITT or S_WRONG_SEQ) */
+ status = S_NORMAL;
+ }
+
+ decoded_bytes_prev = 0;
+ start_timestamp = rp->info->info_timestamp; /* defined start_timestamp to avoid overflow in timestamp. TODO: handle the timestamp correctly */
+ start_rtp_time = 0;
+ start_time = (double)rp->arrive_offset/1000;
+ rtp_time_prev = 0;
+ }
+ } else {
+ /* Add silence if it is necessary */
+ silence_frames = (gint32)((rtp_time - rtp_time_prev)*SAMPLE_RATE - decoded_bytes_prev/2);
+ if (silence_frames != 0) {
+ rci->wrong_timestamp++;
+ status = S_WRONG_TIMESTAMP;
+ }
+
+ /* Fix for bug 4119/5902: don't insert too many silence frames.
+ * XXX - is there a better thing to do here?
+ */
+ if (silence_frames > MAX_SILENCE_FRAMES)
+ silence_frames = MAX_SILENCE_FRAMES;
+
+ for (i = 0; i< silence_frames; i++) {
+ silence.status = status;
+ g_array_append_val(rci->samples, silence);
+
+ /* only mark the first in the silence that has the previous problem (S_DROP_BY_JITT or S_WRONG_SEQ) */
+ status = S_NORMAL;
+ }
+
+
+ if (silence_frames > 0) {
+ silence_frames = 0;
+ }
+ /* Add the audio */
+ for (i = - silence_frames; i< (decoded_bytes/2); i++) {
+ sample.val = out_buff[i];
+ sample.status = status;
+ g_array_append_val(rci->samples, sample);
+ status = S_NORMAL;
+ }
+
+ rtp_time_prev = rtp_time;
+ pack_period = (double)(decoded_bytes/2)/SAMPLE_RATE;
+ decoded_bytes_prev = decoded_bytes;
+ arrive_time_prev = arrive_time;
+ }
+
+ if (out_buff) {
+ g_free(out_buff);
+ out_buff = NULL;
+ }
+ rtp_packets_list = g_list_next (rtp_packets_list);
+ progbar_count++;
+ }
+ rci->max_frame_index = rci->samples->len;
+ rci->end_time = rci->start_time + ((double)rci->samples->len/SAMPLE_RATE)*1000;
+
+ g_string_free(key_str, TRUE);
+ g_hash_table_destroy(decoders_hash);
+}
+
+/****************************************************************************/
+static gint
+h_scrollbar_changed(GtkWidget *widget _U_, gpointer user_data)
+{
+ rtp_channel_info_t *rci = user_data;
+ rci->cursor_catch = TRUE;
+ return TRUE;
+}
+
+static gboolean draw_cursors(gpointer data);
+
+/****************************************************************************/
+static void
+stop_channels(void)
+{
+ PaError err;
+ GtkWidget *dialog;
+
+ /* we should never be here if we are already in STOP */
+ g_assert(rtp_channels->stop == FALSE);
+
+ rtp_channels->stop = TRUE;
+ /* force a draw_cursor to stop it */
+ draw_cursors(NULL);
+
+ err = Pa_StopStream(pa_stream);
+
+ if( err != paNoError ) {
+ dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
+ "Can not Stop Stream in PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ return;
+ }
+
+ err = Pa_CloseStream(pa_stream);
+ if( err != paNoError ) {
+ dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
+ "Can not Close Stream in PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ return;
+ }
+ pa_stream = NULL; /* to catch errors better */
+
+ rtp_channels->start_index[0] = 0;
+ rtp_channels->start_index[1] = 0;
+ rtp_channels->end_index[0] = 0;
+ rtp_channels->end_index[1] = 0;
+ rtp_channels->max_frame_index = 0;
+ rtp_channels->frame_index = 0;
+ rtp_channels->pause = FALSE;
+ rtp_channels->pause_duration = 0;
+ rtp_channels->stop = TRUE;
+ rtp_channels->out_diff_time = 10000;
+
+ if (rtp_channels->rci[0]) rtp_channels->rci[0]->frame_index = 0;
+ if (rtp_channels->rci[1]) rtp_channels->rci[1]->frame_index = 0;
+
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(TRUE, TRUE, FALSE, FALSE);
+
+}
+
+/****************************************************************************/
+/* Draw a cursor in a channel graph
+ */
+static void
+draw_channel_cursor(rtp_channel_info_t *rci, guint32 start_index)
+{
+#if PORTAUDIO_API_1
+ PaTimestamp idx;
+#else /* PORTAUDIO_API_1 */
+ PaTime idx;
+#endif /* PORTAUDIO_API_1 */
+ int i;
+ GtkAllocation widget_alloc;
+ cairo_t *cr;
+
+ if (!rci) return;
+
+#if PORTAUDIO_API_1
+ idx = Pa_StreamTime( pa_stream ) - rtp_channels->pause_duration - rtp_channels->out_diff_time - start_index;
+#else /* PORTAUDIO_API_1 */
+ idx = ((guint32)(SAMPLE_RATE) * (Pa_GetStreamTime(pa_stream)-rtp_channels->pa_start_time))- rtp_channels->pause_duration - rtp_channels->out_diff_time - start_index;
+#endif /* PORTAUDIO_API_1 */
+
+
+ /* If we finished playing both channels, then stop them */
+ if ( (rtp_channels && (!rtp_channels->stop) && (!rtp_channels->pause)) && (idx > rtp_channels->max_frame_index) ) {
+ stop_channels();
+ return;
+ }
+
+ /* If only this channel finished, then return */
+ if (idx > rci->max_frame_index) {
+ return;
+ }
+
+ gtk_widget_get_allocation(rci->draw_area, &widget_alloc);
+ /* draw the previous saved pixbuf line */
+ if (rci->cursor_pixbuf && (rci->cursor_prev>=0)) {
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (rci->surface);
+#else
+ cr = gdk_cairo_create (rci->pixmap);
+#endif
+ gdk_cairo_set_source_pixbuf (cr, rci->cursor_pixbuf, 0, 0);
+ cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+ cairo_rectangle (cr, rci->cursor_prev/MULT, 0, -1, -1);
+ cairo_fill (cr);
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, rci->surface, idx/MULT, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, rci->pixmap,idx/MULT, 0);
+#endif
+ cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+ cairo_rectangle (cr, rci->cursor_prev/MULT, 0, 1, widget_alloc.height-HEIGHT_TIME_LABEL);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+ g_object_unref(rci->cursor_pixbuf);
+ rci->cursor_pixbuf = NULL;
+ }
+
+ if (idx>0 && (rci->cursor_prev>=0)) {
+#if GTK_CHECK_VERSION(2,22,0)
+ rci->cursor_pixbuf = gdk_pixbuf_get_from_surface (rci->surface,0, 0, 1, widget_alloc.height-HEIGHT_TIME_LABEL);
+ cr = cairo_create (rci->surface);
+#else
+ rci->cursor_pixbuf = gdk_pixbuf_get_from_drawable(NULL, rci->pixmap, NULL, (int) (idx/MULT), 0, 0, 0, 1, widget_alloc.height-HEIGHT_TIME_LABEL);
+ cr = gdk_cairo_create (rci->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, idx/MULT, 0);
+ cairo_line_to(cr, idx/MULT, widget_alloc.height-HEIGHT_TIME_LABEL);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+ cr = gdk_cairo_create (gtk_widget_get_window(rci->draw_area));
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, rci->surface, idx/MULT, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, rci->pixmap, idx/MULT, 0);
+#endif
+ cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+ cairo_rectangle (cr, idx/MULT, 0, 1, widget_alloc.height-HEIGHT_TIME_LABEL);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ }
+
+ /* Disconnect the scroll bar "value" signal to not be called */
+ g_signal_handlers_disconnect_by_func(rci->h_scrollbar_adjustment, h_scrollbar_changed, rci);
+
+ /* Move the horizontal scroll bar */
+#if 0
+ if ( (rci->cursor_prev/MULT < (rci->h_scrollbar_adjustment->value+rci->h_scrollbar_adjustment->page_increment)) &&
+ (idx/MULT >= (rci->h_scrollbar_adjustment->value+rci->h_scrollbar_adjustment->page_increment)) ){
+ for (i=1; i<10; i++) {
+ rci->h_scrollbar_adjustment->value += rci->h_scrollbar_adjustment->page_size/10;
+ gtk_adjustment_value_changed(rci->h_scrollbar_adjustment);
+ }
+ }
+#endif
+ if (!rci->cursor_catch) {
+ if (idx/MULT < gtk_adjustment_get_page_size(rci->h_scrollbar_adjustment)/2) {
+ gtk_adjustment_set_value(rci->h_scrollbar_adjustment, gtk_adjustment_get_lower(rci->h_scrollbar_adjustment));
+ } else if (idx/MULT > (gtk_adjustment_get_upper(rci->h_scrollbar_adjustment) - gtk_adjustment_get_page_size(rci->h_scrollbar_adjustment)/2)) {
+ gtk_adjustment_set_value(rci->h_scrollbar_adjustment, gtk_adjustment_get_upper(rci->h_scrollbar_adjustment) - gtk_adjustment_get_page_size(rci->h_scrollbar_adjustment));
+ } else {
+ gtk_adjustment_set_value(rci->h_scrollbar_adjustment, idx/MULT - gtk_adjustment_get_page_size(rci->h_scrollbar_adjustment)/2);
+ }
+
+ gtk_adjustment_value_changed(rci->h_scrollbar_adjustment);
+ } else if ( (rci->cursor_prev/MULT < gtk_adjustment_get_value(rci->h_scrollbar_adjustment)+gtk_adjustment_get_page_increment(rci->h_scrollbar_adjustment)) &&
+ (idx/MULT >= gtk_adjustment_get_value(rci->h_scrollbar_adjustment) + gtk_adjustment_get_page_increment(rci->h_scrollbar_adjustment)) ){
+ rci->cursor_catch = FALSE;
+ for (i=1; i<10; i++) {
+ gtk_adjustment_set_value(rci->h_scrollbar_adjustment, MIN(gtk_adjustment_get_upper(rci->h_scrollbar_adjustment)-gtk_adjustment_get_page_size(rci->h_scrollbar_adjustment), gtk_adjustment_get_value(rci->h_scrollbar_adjustment) + gtk_adjustment_get_page_size(rci->h_scrollbar_adjustment)/20));
+ gtk_adjustment_value_changed(rci->h_scrollbar_adjustment);
+ }
+ }
+
+ /* Connect back the "value" scroll signal */
+ g_signal_connect(rci->h_scrollbar_adjustment, "value_changed", G_CALLBACK(h_scrollbar_changed), rci);
+
+#if 0
+ if (idx/MULT < rci->h_scrollbar_adjustment->page_increment) {
+ rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->lower;
+ } else if (idx/MULT > (rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size + rci->h_scrollbar_adjustment->page_increment)) {
+ rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size;
+ } else {
+ if ( (idx/MULT < rci->h_scrollbar_adjustment->value) || (idx/MULT > (rci->h_scrollbar_adjustment->value+rci->h_scrollbar_adjustment->page_increment)) ){
+ rci->h_scrollbar_adjustment->value = idx/MULT;
+ }
+ }
+#endif
+
+#if 0
+ if (idx/MULT < rci->h_scrollbar_adjustment->page_size/2) {
+ rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->lower;
+ } else if (idx/MULT > (rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size/2)) {
+ rci->h_scrollbar_adjustment->value = rci->h_scrollbar_adjustment->upper - rci->h_scrollbar_adjustment->page_size;
+ } else {
+ rci->h_scrollbar_adjustment->value = idx/MULT - rci->h_scrollbar_adjustment->page_size/2;
+ }
+#endif
+
+#if 0
+ gtk_adjustment_value_changed(rci->h_scrollbar_adjustment);
+#endif
+ rci->cursor_prev = idx;
+}
+
+/****************************************************************************/
+/* Move and draw the cursor in the graph
+ */
+static gboolean
+draw_cursors(gpointer data _U_)
+{
+ if (!rtp_channels) return FALSE;
+
+ /* Draw and move each of the two channels */
+ draw_channel_cursor(rtp_channels->rci[0], rtp_channels->start_index[0]);
+ draw_channel_cursor(rtp_channels->rci[1], rtp_channels->start_index[1]);
+
+ if ((rtp_channels->stop) || (rtp_channels->pause)) return FALSE;
+
+ return TRUE;
+}
+
+/****************************************************************************/
+static void
+init_rtp_channels_vals(void)
+{
+ rtp_play_channels_t *rpci = rtp_channels;
+
+ /* if we only have one channel to play, we just use the info from that channel */
+ if (rpci->rci[0] == NULL) {
+ rpci->max_frame_index = rpci->rci[1]->max_frame_index;
+ rpci->start_index[0] = rpci->max_frame_index;
+ rpci->start_index[1] = 0;
+ rpci->end_index[0] = rpci->max_frame_index;
+ rpci->end_index[1] = rpci->max_frame_index;
+ } else if (rpci->rci[1] == NULL) {
+ rpci->max_frame_index = rpci->rci[0]->max_frame_index;
+ rpci->start_index[1] = rpci->max_frame_index;
+ rpci->start_index[0] = 0;
+ rpci->end_index[0] = rpci->max_frame_index;
+ rpci->end_index[1] = rpci->max_frame_index;
+
+ /* if the two channels are to be played, then we need to sync both based on the start/end time of each one */
+ } else {
+ rpci->max_frame_index = (guint32)(SAMPLE_RATE/1000) * (guint32)(MAX(rpci->rci[0]->end_time, rpci->rci[1]->end_time) -
+ (guint32)MIN(rpci->rci[0]->start_time, rpci->rci[1]->start_time));
+
+ if (rpci->rci[0]->start_time < rpci->rci[1]->start_time) {
+ rpci->start_index[0] = 0;
+ rpci->start_index[1] = (guint32)(SAMPLE_RATE/1000) * (guint32)(rpci->rci[1]->start_time - rpci->rci[0]->start_time);
+ } else {
+ rpci->start_index[1] = 0;
+ rpci->start_index[0] = (guint32)(SAMPLE_RATE/1000) * (guint32)(rpci->rci[0]->start_time - rpci->rci[1]->start_time);
+ }
+
+ if (rpci->rci[0]->end_time < rpci->rci[1]->end_time) {
+ rpci->end_index[0] = rpci->max_frame_index - ((guint32)(SAMPLE_RATE/1000) * (guint32)(rpci->rci[1]->end_time - rpci->rci[0]->end_time));
+ rpci->end_index[1] = rpci->max_frame_index;
+ } else {
+ rpci->end_index[1] = rpci->max_frame_index - ((guint32)(SAMPLE_RATE/1000) * (guint32)(rpci->rci[0]->end_time - rpci->rci[1]->end_time));
+ rpci->end_index[0] = rpci->max_frame_index;
+ }
+ }
+}
+
+
+/****************************************************************************/
+/* This routine will be called by the PortAudio engine when audio is needed.
+ * It may called at interrupt level on some machines so don't do anything
+ * that could mess up the system like calling malloc() or free().
+ */
+#if PORTAUDIO_API_1
+
+static int paCallback( void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ PaTimestamp outTime, void *userData)
+{
+#else /* PORTAUDIO_API_1 */
+static int paCallback( const void *inputBuffer, void *outputBuffer,
+ unsigned long framesPerBuffer,
+ const PaStreamCallbackTimeInfo* outTime,
+ PaStreamCallbackFlags statusFlags _U_,
+ void *userData)
+{
+#endif /* PORTAUDIO_API_1 */
+ rtp_play_channels_t *rpci = (rtp_play_channels_t *)userData;
+ SAMPLE *wptr = (SAMPLE*)outputBuffer;
+ sample_t sample;
+ unsigned int i;
+ int finished;
+ unsigned int framesLeft;
+ int framesToPlay;
+
+ /* if it is pasued, we keep the stream running but with silence only */
+ if (rtp_channels->pause) {
+ for(i=0; i<framesPerBuffer; i++ ) {
+ *wptr++ = 0;
+ *wptr++ = 0;
+ }
+ rtp_channels->pause_duration += framesPerBuffer;
+ return 0;
+ }
+
+#if PORTAUDIO_API_1
+ rpci->out_diff_time = outTime - Pa_StreamTime(pa_stream) ;
+#else /* PORTAUDIO_API_1 */
+ rpci->out_diff_time = (guint32)(SAMPLE_RATE) * (outTime->outputBufferDacTime - Pa_GetStreamTime(pa_stream)) ;
+#endif /* PORTAUDIO_API_1 */
+
+
+ /* set the values if this is the first time */
+ if (rpci->max_frame_index == 0) {
+ init_rtp_channels_vals();
+
+ }
+
+ framesLeft = rpci->max_frame_index - rpci->frame_index;
+
+ (void) inputBuffer; /* Prevent unused variable warnings. */
+ (void) outTime;
+
+ if( framesLeft < framesPerBuffer )
+ {
+ framesToPlay = framesLeft;
+ finished = 1;
+ }
+ else
+ {
+ framesToPlay = framesPerBuffer;
+ finished = 0;
+ }
+
+ for( i=0; i<(unsigned int)framesToPlay; i++ )
+ {
+ if (rpci->rci[0] && ( (rpci->frame_index >= rpci->start_index[0]) && (rpci->frame_index <= rpci->end_index[0]) )) {
+ sample = g_array_index(rpci->rci[0]->samples, sample_t, rpci->rci[0]->frame_index++);
+ *wptr++ = sample.val;
+ } else {
+ *wptr++ = 0;
+ }
+
+ if (rpci->rci[1] && ( (rpci->frame_index >= rpci->start_index[1]) && (rpci->frame_index <= rpci->end_index[1]) )) {
+ sample = g_array_index(rpci->rci[1]->samples, sample_t, rpci->rci[1]->frame_index++);
+ *wptr++ = sample.val;
+ } else {
+ *wptr++ = 0;
+ }
+ }
+ for( ; i<framesPerBuffer; i++ )
+ {
+ *wptr++ = 0;
+ *wptr++ = 0;
+ }
+ rpci->frame_index += framesToPlay;
+
+ return finished;
+}
+
+/****************************************************************************/
+static void
+on_bt_check_clicked(GtkButton *button _U_, gpointer user_data)
+{
+ rtp_channel_info_t *rci = user_data;
+
+ if (rci->selected) {
+ if (rtp_channels->rci[0] == rci) {
+ rtp_channels->rci[0] = NULL;
+ rtp_channels->channel = 0;
+ } else {
+ rtp_channels->rci[1] = NULL;
+ rtp_channels->channel = 1;
+ }
+ } else {
+ /* if there are already both channels selected, unselect the old one */
+ if (rtp_channels->rci[rtp_channels->channel]) {
+ /* we disconnect the signal temporarly to avoid been called back */
+ g_signal_handlers_disconnect_by_func(rtp_channels->rci[rtp_channels->channel]->check_bt, on_bt_check_clicked, rtp_channels->rci[rtp_channels->channel]);
+ gtk_toggle_button_set_active((GtkToggleButton *)rtp_channels->rci[rtp_channels->channel]->check_bt, FALSE);
+ g_signal_connect(rtp_channels->rci[rtp_channels->channel]->check_bt, "clicked", G_CALLBACK(on_bt_check_clicked), rtp_channels->rci[rtp_channels->channel]);
+ rtp_channels->rci[rtp_channels->channel]->selected = FALSE;
+ }
+
+ rtp_channels->rci[rtp_channels->channel] = rci;
+ rtp_channels->channel = !(rtp_channels->channel);
+ }
+
+ rci->selected = !(rci->selected);
+
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(TRUE, TRUE, FALSE, FALSE);
+}
+
+/****************************************************************************/
+static void channel_draw(rtp_channel_info_t* rci)
+{
+ int i, imax;
+ int j;
+ sample_t sample;
+ SAMPLE min, max;
+ PangoLayout *small_layout;
+ guint32 label_width, label_height;
+ char label_string[MAX_TIME_LABEL];
+ double offset;
+ guint32 progbar_nextstep;
+ int progbar_quantum;
+ gfloat progbar_val;
+ guint status;
+ GdkColor red_color = {0, 65535, 0, 0};
+ GdkColor amber_color = {0, 65535, 49152, 0};
+ GdkColor white_color = {0, 65535, 65535, 65535};
+ GdkColor black_color = {0, 0, 0, 0};
+
+ GdkColor *draw_color_p;
+ time_t seconds;
+ struct tm *timestamp;
+ GtkAllocation widget_alloc;
+ cairo_t *cr;
+
+#if GTK_CHECK_VERSION(2,22,0)
+ gtk_widget_get_allocation(rci->draw_area, &widget_alloc);
+ /* Clear out old plot */
+ cr = cairo_create (rci->surface);
+ gdk_cairo_set_source_color (cr, &rci->bg_color[1+rci->call_num%MAX_NUM_COL_CONV]);
+ cairo_rectangle (cr, 0, 0, widget_alloc.width,widget_alloc.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ cr = NULL;
+
+ small_layout = gtk_widget_create_pango_layout(rci->draw_area, NULL);
+ pango_layout_set_font_description(small_layout, pango_font_description_from_string("Helvetica,Sans,Bold 7"));
+
+ /* calculated the pixel offset to display integer seconds */
+ offset = ((double)rci->start_time/1000 - floor((double)rci->start_time/1000))*SAMPLE_RATE/MULT;
+
+ cr = cairo_create (rci->surface);
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, 0, widget_alloc.height-HEIGHT_TIME_LABEL+0.5);
+ cairo_line_to(cr, widget_alloc.width, widget_alloc.height-HEIGHT_TIME_LABEL+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ cr = NULL;
+
+ imax = MIN(widget_alloc.width,(gint)(rci->samples->len/MULT));
+
+ /* we update the progress bar 100 times */
+
+ /* Update the progress bar when it gets to this value. */
+ progbar_nextstep = 0;
+ /* When we reach the value that triggers a progress bar update,
+ bump that value by this amount. */
+ progbar_quantum = imax/100;
+
+ for (i=0; i< imax; i++) {
+ sample.val = 0;
+ status = S_NORMAL;
+ max=(SAMPLE)0xFFFF;
+ min=(SAMPLE)0x7FFF;
+
+ if (progbar_count >= progbar_nextstep) {
+ g_assert(total_frames > 0);
+
+ progbar_val = (gfloat) i / imax;
+
+ update_progress_bar(progbar_val);
+
+ progbar_nextstep += progbar_quantum;
+ }
+
+ for (j=0; j<MULT; j++) {
+ sample = g_array_index(rci->samples, sample_t, i*MULT+j);
+ max = MAX(max, sample.val);
+ min = MIN(min, sample.val);
+ if (sample.status == S_DROP_BY_JITT) status = S_DROP_BY_JITT;
+ if (sample.status == S_WRONG_TIMESTAMP) status = S_WRONG_TIMESTAMP;
+ if (sample.status == S_SILENCE) status = S_SILENCE;
+ }
+
+ /* Set the line color, default is black */
+ if (status == S_DROP_BY_JITT) {
+ draw_color_p = &red_color;
+ } else if (status == S_WRONG_TIMESTAMP) {
+ draw_color_p = &amber_color;
+ } else if (status == S_SILENCE) {
+ draw_color_p = &white_color;
+ } else {
+ draw_color_p = &black_color;
+ }
+
+ /* if silence added by Wireshark, graphically show it with letter to indicate why */
+ if ((status == S_DROP_BY_JITT) || (status == S_WRONG_TIMESTAMP) || (status == S_SILENCE)) {
+ cr = cairo_create (rci->surface);
+ cairo_set_line_width (cr, 1.0);
+ gdk_cairo_set_source_color (cr, draw_color_p);
+ cairo_move_to(cr, i+0.5, 0);
+ cairo_line_to(cr, i+0.5, (widget_alloc.height-HEIGHT_TIME_LABEL)-1);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ cr=NULL;
+
+ if (status == S_DROP_BY_JITT) g_snprintf(label_string, MAX_TIME_LABEL,"D");
+ if (status == S_WRONG_TIMESTAMP) g_snprintf(label_string, MAX_TIME_LABEL, "W");
+ if (status == S_SILENCE) g_snprintf(label_string, MAX_TIME_LABEL, "S");
+
+ pango_layout_set_text(small_layout, label_string, -1);
+ pango_layout_get_pixel_size(small_layout, &label_width, &label_height);
+ cr = cairo_create (rci->surface);
+ gdk_cairo_set_source_color (cr, draw_color_p);
+ cairo_move_to (cr, i, 0);
+ pango_cairo_show_layout (cr, small_layout);
+ cairo_destroy (cr);
+ cr = NULL;
+ } else {
+ /* Draw a graphical representation of the sample */
+ cr = cairo_create (rci->surface);
+ cairo_set_line_width (cr, 1.0);
+ gdk_cairo_set_source_color (cr, draw_color_p);
+ cairo_move_to(cr, i+0.5, ( (0x7FFF+min) * (widget_alloc.height-HEIGHT_TIME_LABEL))/0xFFFF);
+ cairo_line_to(cr, i+0.5, ( (0x7FFF+max) * (widget_alloc.height-HEIGHT_TIME_LABEL))/0xFFFF);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ }
+
+ /* Draw the x-axis (seconds since beginning of packet flow for this call) */
+
+ /* Draw tick mark and put a number for each whole second */
+ if ( !((i*MULT)%(SAMPLE_RATE)) ) {
+ cr = cairo_create (rci->surface);
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, i - offset+0.5, widget_alloc.height-HEIGHT_TIME_LABEL);
+ cairo_line_to(cr, i+0.5, widget_alloc.height-HEIGHT_TIME_LABEL+4);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ cr=NULL;
+
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb_view_as_time_of_day))) {
+ seconds = rci->start_time_abs.secs + i * MULT / SAMPLE_RATE;
+ timestamp = localtime(&seconds);
+ g_snprintf(label_string, MAX_TIME_LABEL, "%02d:%02d:%02d", timestamp->tm_hour, timestamp->tm_min, timestamp->tm_sec);
+ } else {
+ g_snprintf(label_string, MAX_TIME_LABEL, "%.0f s", floor(rci->start_time/1000) + i*MULT/SAMPLE_RATE);
+ }
+
+ pango_layout_set_text(small_layout, label_string, -1);
+ pango_layout_get_pixel_size(small_layout, &label_width, &label_height);
+ cr = cairo_create (rci->surface);
+ cairo_move_to (cr, i - offset - label_width/2, widget_alloc.height - label_height);
+ pango_cairo_show_layout (cr, small_layout);
+ cairo_destroy (cr);
+ cr = NULL;
+
+
+ /* Draw only a tick mark for half second intervals */
+ } else if ( !((i*MULT)%(SAMPLE_RATE/2)) ) {
+ cr = cairo_create (rci->surface);
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr,i - offset+0.5, widget_alloc.height-HEIGHT_TIME_LABEL);
+ cairo_line_to(cr, (i - offset)+0.5, widget_alloc.height-HEIGHT_TIME_LABEL+2);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ cr=NULL;
+ }
+
+ progbar_count++;
+ }
+ g_object_unref(G_OBJECT(small_layout));
+#else
+ if (GDK_IS_DRAWABLE(rci->pixmap)) {
+ gtk_widget_get_allocation(rci->draw_area, &widget_alloc);
+ /* Clear out old plot */
+ cr = gdk_cairo_create (rci->pixmap);
+ gdk_cairo_set_source_color (cr, &rci->bg_color[1+rci->call_num%MAX_NUM_COL_CONV]);
+ cairo_rectangle (cr, 0, 0, widget_alloc.width,widget_alloc.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ cr = NULL;
+
+ small_layout = gtk_widget_create_pango_layout(rci->draw_area, NULL);
+ pango_layout_set_font_description(small_layout, pango_font_description_from_string("Helvetica,Sans,Bold 7"));
+
+ /* calculated the pixel offset to display integer seconds */
+ offset = ((double)rci->start_time/1000 - floor((double)rci->start_time/1000))*SAMPLE_RATE/MULT;
+
+ cr = gdk_cairo_create (rci->pixmap);
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, 0, widget_alloc.height-HEIGHT_TIME_LABEL+0.5);
+ cairo_line_to(cr, widget_alloc.width, widget_alloc.height-HEIGHT_TIME_LABEL+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ cr = NULL;
+
+ imax = MIN(widget_alloc.width,(gint)(rci->samples->len/MULT));
+
+ /* we update the progress bar 100 times */
+
+ /* Update the progress bar when it gets to this value. */
+ progbar_nextstep = 0;
+ /* When we reach the value that triggers a progress bar update,
+ bump that value by this amount. */
+ progbar_quantum = imax/100;
+
+ for (i=0; i< imax; i++) {
+ sample.val = 0;
+ status = S_NORMAL;
+ max=(SAMPLE)0xFFFF;
+ min=(SAMPLE)0x7FFF;
+
+ if (progbar_count >= progbar_nextstep) {
+ g_assert(total_frames > 0);
+
+ progbar_val = (gfloat) i / imax;
+
+ update_progress_bar(progbar_val);
+
+ progbar_nextstep += progbar_quantum;
+ }
+
+ for (j=0; j<MULT; j++) {
+ sample = g_array_index(rci->samples, sample_t, i*MULT+j);
+ max = MAX(max, sample.val);
+ min = MIN(min, sample.val);
+ if (sample.status == S_DROP_BY_JITT) status = S_DROP_BY_JITT;
+ if (sample.status == S_WRONG_TIMESTAMP) status = S_WRONG_TIMESTAMP;
+ if (sample.status == S_SILENCE) status = S_SILENCE;
+ }
+
+ /* Set the line color, default is black */
+ if (status == S_DROP_BY_JITT) {
+ draw_color_p = &red_color;
+ } else if (status == S_WRONG_TIMESTAMP) {
+ draw_color_p = &amber_color;
+ } else if (status == S_SILENCE) {
+ draw_color_p = &white_color;
+ } else {
+ draw_color_p = &black_color;
+ }
+
+ /* if silence added by Wireshark, graphically show it with letter to indicate why */
+ if ((status == S_DROP_BY_JITT) || (status == S_WRONG_TIMESTAMP) || (status == S_SILENCE)) {
+ cr = gdk_cairo_create (rci->pixmap);
+ cairo_set_line_width (cr, 1.0);
+ gdk_cairo_set_source_color (cr, draw_color_p);
+ cairo_move_to(cr, i+0.5, 0);
+ cairo_line_to(cr, i+0.5, (widget_alloc.height-HEIGHT_TIME_LABEL)-1);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ cr=NULL;
+
+ if (status == S_DROP_BY_JITT) g_snprintf(label_string, MAX_TIME_LABEL,"D");
+ if (status == S_WRONG_TIMESTAMP) g_snprintf(label_string, MAX_TIME_LABEL, "W");
+ if (status == S_SILENCE) g_snprintf(label_string, MAX_TIME_LABEL, "S");
+
+ pango_layout_set_text(small_layout, label_string, -1);
+ pango_layout_get_pixel_size(small_layout, &label_width, &label_height);
+ cr = gdk_cairo_create (rci->pixmap);
+ gdk_cairo_set_source_color (cr, draw_color_p);
+ cairo_move_to (cr, i, 0);
+ pango_cairo_show_layout (cr, small_layout);
+ cairo_destroy (cr);
+ cr = NULL;
+ } else {
+ /* Draw a graphical representation of the sample */
+ cr = gdk_cairo_create (rci->pixmap);
+ cairo_set_line_width (cr, 1.0);
+ gdk_cairo_set_source_color (cr, draw_color_p);
+ cairo_move_to(cr, i+0.5, ( (0x7FFF+min) * (widget_alloc.height-HEIGHT_TIME_LABEL))/0xFFFF);
+ cairo_line_to(cr, i+0.5, ( (0x7FFF+max) * (widget_alloc.height-HEIGHT_TIME_LABEL))/0xFFFF);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ }
+
+ /* Draw the x-axis (seconds since beginning of packet flow for this call) */
+
+ /* Draw tick mark and put a number for each whole second */
+ if ( !((i*MULT)%(SAMPLE_RATE)) ) {
+ cr = gdk_cairo_create (rci->pixmap);
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, i - offset+0.5, widget_alloc.height-HEIGHT_TIME_LABEL);
+ cairo_line_to(cr, i+0.5, widget_alloc.height-HEIGHT_TIME_LABEL+4);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ cr=NULL;
+
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb_view_as_time_of_day))) {
+ seconds = rci->start_time_abs.secs + i * MULT / SAMPLE_RATE;
+ timestamp = localtime(&seconds);
+ g_snprintf(label_string, MAX_TIME_LABEL, "%02d:%02d:%02d", timestamp->tm_hour, timestamp->tm_min, timestamp->tm_sec);
+ } else {
+ g_snprintf(label_string, MAX_TIME_LABEL, "%.0f s", floor(rci->start_time/1000) + i*MULT/SAMPLE_RATE);
+ }
+
+ pango_layout_set_text(small_layout, label_string, -1);
+ pango_layout_get_pixel_size(small_layout, &label_width, &label_height);
+ cr = gdk_cairo_create (rci->pixmap);
+ cairo_move_to (cr, i - offset - label_width/2, widget_alloc.height - label_height);
+ pango_cairo_show_layout (cr, small_layout);
+ cairo_destroy (cr);
+ cr = NULL;
+
+
+ /* Draw only a tick mark for half second intervals */
+ } else if ( !((i*MULT)%(SAMPLE_RATE/2)) ) {
+ cr = gdk_cairo_create (rci->pixmap);
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr,i - offset+0.5, widget_alloc.height-HEIGHT_TIME_LABEL);
+ cairo_line_to(cr, (i - offset)+0.5, widget_alloc.height-HEIGHT_TIME_LABEL+2);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ cr=NULL;
+ }
+
+ progbar_count++;
+ }
+ g_object_unref(G_OBJECT(small_layout));
+ }
+#endif
+
+}
+/****************************************************************************/
+static gboolean expose_event_channels(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
+{
+ rtp_channel_info_t *rci = user_data;
+ cairo_t *cr;
+
+ if (gtk_widget_is_drawable(widget)){
+ cr = gdk_cairo_create (gtk_widget_get_window(widget));
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, rci->surface, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, rci->pixmap, event->area.x, event->area.y);
+#endif
+ cairo_rectangle (cr, event->area.x,event->area.y, event->area.width, event->area.height);
+ cairo_fill (cr);
+ cairo_destroy(cr);
+ cr = NULL;
+ }
+
+ return FALSE;
+}
+
+/****************************************************************************/
+static gboolean
+configure_event_channels(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer user_data)
+{
+ rtp_channel_info_t *rci = user_data;
+ int i;
+ GtkAllocation widget_alloc;
+ cairo_t *cr;
+
+ /* the first color is blue to highlight the selected item
+ * the other collors are the same as in the Voip Graph analysys
+ * to match the same calls
+ */
+ static GdkColor col[MAX_NUM_COL_CONV+1] = {
+ {0, 0x00FF, 0x00FF, 0xFFFF},
+ {0, 0x90FF, 0xEEFF, 0x90FF},
+ {0, 0xFFFF, 0xA0FF, 0x7AFF},
+ {0, 0xFFFF, 0xB6FF, 0xC1FF},
+ {0, 0xFAFF, 0xFAFF, 0xD2FF},
+ {0, 0xFFFF, 0xFFFF, 0x33FF},
+ {0, 0x66FF, 0xCDFF, 0xAAFF},
+ {0, 0xE0FF, 0xFFFF, 0xFFFF},
+ {0, 0xB0FF, 0xC4FF, 0xDEFF},
+ {0, 0x87FF, 0xCEFF, 0xFAFF},
+ {0, 0xD3FF, 0xD3FF, 0xD3FF}
+ };
+
+#if GTK_CHECK_VERSION(2,22,0)
+ if(rci->surface){
+ cairo_surface_destroy (rci->surface);
+ rci->surface=NULL;
+ }
+ gtk_widget_get_allocation(widget, &widget_alloc);
+ rci->surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
+ CAIRO_CONTENT_COLOR,
+ widget_alloc.width,
+ widget_alloc.height);
+
+ cr = cairo_create (rci->surface);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_rectangle (cr, 0, 0, widget_alloc.width,widget_alloc.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+#else
+ if(rci->pixmap){
+ g_object_unref(rci->pixmap);
+ rci->pixmap=NULL;
+ }
+ gtk_widget_get_allocation(widget, &widget_alloc);
+ rci->pixmap = gdk_pixmap_new(gtk_widget_get_window(widget),
+ widget_alloc.width,
+ widget_alloc.height,
+ -1);
+ if ( GDK_IS_DRAWABLE(rci->pixmap) ){
+ cr = gdk_cairo_create (rci->pixmap);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_rectangle (cr, 0, 0, widget_alloc.width,widget_alloc.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ }
+#endif
+
+ /* create gc's for the background color of each channel */
+ for (i=0; i<MAX_NUM_COL_CONV+1; i++){
+ rci->bg_color[i].pixel=col[i].pixel;
+ rci->bg_color[i].red=col[i].red;
+ rci->bg_color[i].green=col[i].green;
+ rci->bg_color[i].blue=col[i].blue;
+
+ }
+
+ channel_draw(rci);
+
+ return TRUE;
+}
+
+/****************************************************************************/
+static gboolean
+button_press_event_channel(GtkWidget *widget _U_, GdkEventButton *event _U_, gpointer user_data)
+{
+ rtp_channel_info_t *rci = user_data;
+ int this_channel;
+ guint32 prev_index;
+
+ if (!rci->selected) {
+
+ /* only select a new channels if we are in STOP */
+ if (!rtp_channels->stop) return 0;
+
+ /* if there are already both channels selected, unselect the old one */
+ if (rtp_channels->rci[rtp_channels->channel]) {
+ /* we disconnect the signal temporarly to avoid been called back */
+ g_signal_handlers_disconnect_by_func(rtp_channels->rci[rtp_channels->channel]->check_bt, on_bt_check_clicked, rtp_channels->rci[rtp_channels->channel]);
+ gtk_toggle_button_set_active((GtkToggleButton *) rtp_channels->rci[rtp_channels->channel]->check_bt, FALSE);
+ g_signal_connect(rtp_channels->rci[rtp_channels->channel]->check_bt, "clicked", G_CALLBACK(on_bt_check_clicked), rtp_channels->rci[rtp_channels->channel]);
+ rtp_channels->rci[rtp_channels->channel]->selected = FALSE;
+ }
+
+ /* we disconnect the signal temporarly to avoid been called back */
+ g_signal_handlers_disconnect_by_func(rci->check_bt, on_bt_check_clicked, rci);
+ gtk_toggle_button_set_active((GtkToggleButton *) rci->check_bt, TRUE);
+ g_signal_connect(rci->check_bt, "clicked", G_CALLBACK(on_bt_check_clicked), rci);
+
+ rtp_channels->rci[rtp_channels->channel] = rci;
+ rtp_channels->channel = !(rtp_channels->channel);
+ rci->selected = TRUE;
+
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(TRUE, TRUE, FALSE, FALSE);
+ }
+
+ if (rci == rtp_channels->rci[0]) {
+ this_channel = 0;
+ } else {
+ this_channel = 1;
+ }
+
+ rci->frame_index = (unsigned int) (event->x * MULT);
+
+ prev_index = rtp_channels->frame_index;
+ rtp_channels->frame_index = rtp_channels->start_index[this_channel] + rci->frame_index;
+ rtp_channels->pause_duration += prev_index - rtp_channels->frame_index;
+
+
+
+ /* change the index in the other channel if selected, according with the index position */
+ if (rtp_channels->rci[!this_channel]) {
+ init_rtp_channels_vals();
+
+ if (rtp_channels->frame_index < rtp_channels->start_index[!this_channel]) {
+ rtp_channels->rci[!this_channel]->frame_index = 0;
+ } else if (rtp_channels->frame_index > rtp_channels->end_index[!this_channel]) {
+ rtp_channels->rci[!this_channel]->frame_index = rtp_channels->rci[!this_channel]->max_frame_index;
+ } else {
+ rtp_channels->rci[!this_channel]->frame_index = rtp_channels->frame_index - rtp_channels->start_index[!this_channel];
+ }
+ } else {
+ init_rtp_channels_vals();
+ }
+
+ rtp_channels->out_diff_time = 0;
+
+ rci->cursor_catch = TRUE;
+
+ /* redraw the cusrsor */
+ draw_cursors(NULL);
+
+ return TRUE;
+}
+
+/****************************************************************************/
+static void
+add_channel_to_window(gchar *key _U_ , rtp_channel_info_t *rci, guint *counter _U_ )
+{
+ GString *label = NULL;
+ GtkWidget *viewport;
+
+
+ /* create the channel draw area */
+ rci->draw_area=gtk_drawing_area_new();
+
+ rci->scroll_window=gtk_scrolled_window_new(NULL, NULL);
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (rci->scroll_window), GTK_POLICY_ALWAYS, GTK_POLICY_NEVER);
+ rci->h_scrollbar_adjustment = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(rci->scroll_window));
+
+
+ gtk_widget_set_size_request(rci->draw_area, (gint)(rci->samples->len/MULT), CHANNEL_HEIGHT);
+
+
+ viewport = gtk_viewport_new(rci->h_scrollbar_adjustment, gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(rci->scroll_window)));
+ gtk_container_add(GTK_CONTAINER(viewport), rci->draw_area);
+ gtk_container_add(GTK_CONTAINER(rci->scroll_window), viewport);
+ gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
+ gtk_widget_add_events (rci->draw_area, GDK_BUTTON_PRESS_MASK);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_focus(rci->draw_area, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(rci->draw_area, GTK_CAN_FOCUS);
+#endif
+ gtk_widget_grab_focus(rci->draw_area);
+
+ gtk_box_pack_start(GTK_BOX (channels_vb), rci->scroll_window, FALSE, FALSE, 0);
+
+ /* signals needed to handle backing pixmap */
+ g_signal_connect(rci->draw_area, "expose_event", G_CALLBACK(expose_event_channels), rci);
+ g_signal_connect(rci->draw_area, "configure_event", G_CALLBACK(configure_event_channels), rci);
+ gtk_widget_add_events (rci->draw_area, GDK_BUTTON_PRESS_MASK);
+ g_signal_connect(rci->draw_area, "button_press_event", G_CALLBACK(button_press_event_channel), rci);
+ g_signal_connect(rci->h_scrollbar_adjustment, "value_changed", G_CALLBACK(h_scrollbar_changed), rci);
+
+
+ label = g_string_new("");
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb_use_rtp_timestamp))) {
+ g_string_printf(label, "From %s:%d to %s:%d Duration:%.2f Out of Seq: %d(%.1f%%) Wrong Timestamp: %d(%.1f%%)",
+ get_addr_name(&(rci->first_stream->src_addr)), rci->first_stream->src_port,
+ get_addr_name(&(rci->first_stream->dest_addr)), rci->first_stream->dest_port,
+ (double)rci->samples->len/SAMPLE_RATE,
+ rci->out_of_seq, (double)rci->out_of_seq * 100 / (double)rci->num_packets,
+ rci->wrong_timestamp, (double)rci->wrong_timestamp * 100 / (double)rci->num_packets);
+ } else {
+ g_string_printf(label, "From %s:%d to %s:%d Duration:%.2f Drop by Jitter Buff:%d(%.1f%%) Out of Seq: %d(%.1f%%) Wrong Timestamp: %d(%.1f%%)",
+ get_addr_name(&(rci->first_stream->src_addr)), rci->first_stream->src_port,
+ get_addr_name(&(rci->first_stream->dest_addr)), rci->first_stream->dest_port,
+ (double)rci->samples->len/SAMPLE_RATE,
+ rci->drop_by_jitter_buff, (double)rci->drop_by_jitter_buff * 100 / (double)rci->num_packets,
+ rci->out_of_seq, (double)rci->out_of_seq * 100 / (double)rci->num_packets,
+ rci->wrong_timestamp, (double)rci->wrong_timestamp * 100 / (double)rci->num_packets);
+ }
+
+ rci->check_bt = gtk_check_button_new_with_label(label->str);
+ gtk_box_pack_start(GTK_BOX (channels_vb), rci->check_bt, FALSE, FALSE, 1);
+
+ /* Create the Separator if it is not the last one */
+ (*counter)++;
+ if (*counter < g_hash_table_size(rtp_channels_hash)) {
+ rci->separator = gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX (channels_vb), rci->separator, FALSE, FALSE, 5);
+ }
+
+ g_signal_connect(rci->check_bt, "clicked", G_CALLBACK(on_bt_check_clicked), rci);
+
+ g_string_free(label, TRUE);
+}
+
+/****************************************************************************/
+static void
+count_channel_frames(gchar *key _U_ , rtp_channel_info_t *rci, gpointer ptr _U_ )
+{
+ total_frames += rci->samples->len;
+}
+
+/****************************************************************************/
+static void
+play_channels(void)
+{
+ PaError err;
+ GtkWidget *dialog;
+
+ /* we should never be here if we are in PLAY and !PAUSE */
+ g_assert(!rtp_channels->stop && !rtp_channels->pause);
+
+ /* if we are in PAUSE change the state */
+ if (rtp_channels->pause) {
+ rtp_channels->pause = FALSE;
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(FALSE, FALSE, TRUE, TRUE);
+
+ /* if not PAUSE, then start to PLAY */
+ } else {
+#if PORTAUDIO_API_1
+ err = Pa_OpenStream(
+ &pa_stream,
+ paNoDevice, /* default input device */
+ 0, /* no input */
+ PA_SAMPLE_TYPE, /* 16 bit Integer input */
+ NULL,
+ Pa_GetDefaultOutputDeviceID(),
+ NUM_CHANNELS, /* Stereo output */
+ PA_SAMPLE_TYPE, /* 16 bit Integer output */
+ NULL,
+ SAMPLE_RATE, /* 8 kHz */
+ FRAMES_PER_BUFFER,
+ 0, /* number of buffers, if zero then use default minimum */
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ paCallback,
+ rtp_channels );
+
+ if( err != paNoError ) {
+ const char *deviceName = "No Device";
+
+ PaDeviceID device = Pa_GetDefaultOutputDeviceID();
+
+ if (device != paNoDevice)
+ {
+ const PaDeviceInfo* deviceInfo = Pa_GetDeviceInfo( device );
+ if (deviceInfo)
+ deviceName = deviceInfo->name;
+ else
+ deviceName = "(No device info)";
+ }
+
+ dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
+ "Got this info from PortAudio Library:\n"
+ " Default deviceName: %s (%d)", deviceName, device);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+
+ dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
+ "Can not Open Stream in PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ return;
+ }
+#else /* PORTAUDIO_API_1 */
+ if (Pa_GetDefaultOutputDevice() != paNoDevice) {
+ err = Pa_OpenDefaultStream(
+ &pa_stream,
+ 0,
+ NUM_CHANNELS, /* Stereo output */
+ PA_SAMPLE_TYPE, /* 16 bit Integer output */
+ SAMPLE_RATE, /* 8 kHz */
+ FRAMES_PER_BUFFER,
+ paCallback,
+ rtp_channels );
+ } else {
+ /* If the Default Host API doesn't even provide a device
+ * we might as well go look for another.
+ */
+ PaHostApiIndex host_api_count = Pa_GetHostApiCount();
+ PaHostApiIndex default_host_api_index = Pa_GetDefaultHostApi();
+
+ PaHostApiIndex host_api_index;
+ const PaHostApiInfo *host_api_info;
+
+ for (host_api_index=0; host_api_index<host_api_count; host_api_index++)
+ {
+ /* Skip the default host API, that didn't work before */
+ if (host_api_index == default_host_api_index)
+ continue;
+
+ /* If we find a host API with a device, then take it. */
+ host_api_info = Pa_GetHostApiInfo(host_api_index);
+ if (host_api_info->deviceCount > 0)
+ break;
+ }
+
+ if (host_api_index<host_api_count)
+ {
+ PaStreamParameters stream_parameters;
+ stream_parameters.device = host_api_info->defaultOutputDevice;
+ stream_parameters.channelCount = NUM_CHANNELS; /* Stereo output */
+ stream_parameters.sampleFormat = PA_SAMPLE_TYPE; /* 16 bit Integer output */
+ stream_parameters.suggestedLatency = 0;
+ stream_parameters.hostApiSpecificStreamInfo = NULL;
+#ifdef DEBUG
+ g_print("Trying Host API: %s\n", host_api_info->name);
+#endif
+ err = Pa_OpenStream(
+ &pa_stream,
+ NULL, /* no input */
+ &stream_parameters,
+ SAMPLE_RATE, /* 8 kHz */
+ FRAMES_PER_BUFFER,
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */
+ paCallback,
+ rtp_channels );
+ }
+ else
+ {
+ err = paNoDevice;
+ }
+ }
+
+ if( err != paNoError ) {
+ PaHostApiIndex hostApi = Pa_GetDefaultHostApi();
+ if (hostApi < 0)
+ {
+ dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
+ "Can not even get the default host API from PortAudio Library.\n Error: %s",
+ Pa_GetErrorText( hostApi ));
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ }
+ else
+ {
+ const PaHostApiInfo *hostApiInfo = Pa_GetHostApiInfo( hostApi );
+
+ if ( !hostApiInfo )
+ {
+ dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
+ "Can not even get the host API info from PortAudio Library.");
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ }
+ else
+ {
+ const char *hostApiName = hostApiInfo->name;
+ const char *deviceName = "No Device";
+
+ PaDeviceIndex device = hostApiInfo->defaultOutputDevice;
+
+ if (device != paNoDevice)
+ {
+ const PaDeviceInfo* deviceInfo = Pa_GetDeviceInfo( device );
+ if (deviceInfo)
+ deviceName = deviceInfo->name;
+ else
+ deviceName = "(No device info)";
+ }
+
+ dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
+ "Got this info from PortAudio Library:\n"
+ " Default hostApiName: %s\n"
+ " Default deviceName: %s (%d)", hostApiName, deviceName, device);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ }
+ }
+
+ dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
+ "Can not Open Stream in PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ return;
+ }
+#endif
+
+ err = Pa_StartStream( pa_stream );
+ if( err != paNoError ) {
+ dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
+ "Can not Start Stream in PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ return;
+ }
+#if !PORTAUDIO_API_1
+ rtp_channels->pa_start_time = Pa_GetStreamTime(pa_stream);
+#endif /* PORTAUDIO_API_1 */
+
+ rtp_channels->stop = FALSE;
+
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(FALSE, FALSE, TRUE, TRUE);
+ }
+
+ /* Draw the cursor in the graph */
+ g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, MULT*1000/SAMPLE_RATE, draw_cursors, NULL, NULL);
+
+}
+
+/****************************************************************************/
+static void
+pause_channels(void)
+{
+ rtp_channels->pause = !(rtp_channels->pause);
+
+ /* reactivate the cusrosr display if no in pause */
+ if (!rtp_channels->pause) {
+ /* Draw the cursor in the graph */
+ g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, MULT*1000/SAMPLE_RATE, draw_cursors, NULL, NULL);
+ }
+
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(FALSE, TRUE, FALSE, TRUE);
+}
+
+/****************************************************************************/
+static void
+reset_rtp_channels(void)
+{
+ rtp_channels->channel = 0;
+ rtp_channels->rci[0] = NULL;
+ rtp_channels->rci[1] = NULL;
+ rtp_channels->start_index[0] = 0;
+ rtp_channels->start_index[1] = 0;
+ rtp_channels->end_index[0] = 0;
+ rtp_channels->end_index[1] = 0;
+ rtp_channels->max_frame_index = 0;
+ rtp_channels->frame_index = 0;
+ rtp_channels->pause = FALSE;
+ rtp_channels->pause_duration = 0;
+ rtp_channels->stop = TRUE;
+ rtp_channels->out_diff_time = 10000;
+}
+
+/****************************************************************************/
+static void
+remove_channel_to_window(gchar *key _U_ , rtp_channel_info_t *rci, gpointer ptr _U_ )
+{
+#if GTK_CHECK_VERSION(2,22,0)
+ if(rci->surface){
+ cairo_surface_destroy (rci->surface);
+ rci->surface=NULL;
+ }
+#else
+ g_object_unref(rci->pixmap);
+#endif
+ gtk_widget_destroy(rci->draw_area);
+ gtk_widget_destroy(rci->scroll_window);
+ gtk_widget_destroy(rci->check_bt);
+ if (rci->separator)
+ gtk_widget_destroy(rci->separator);
+}
+
+/****************************************************************************/
+static void
+reset_channels(void)
+{
+
+ if (rtp_channels_hash) {
+ /* Remove the channels from the main window if there are there */
+ g_hash_table_foreach( rtp_channels_hash, (GHFunc)remove_channel_to_window, NULL);
+
+
+ /* destroy the rtp channels hash table */
+ g_hash_table_destroy(rtp_channels_hash);
+ rtp_channels_hash = NULL;
+ }
+
+ if (rtp_channels) {
+ reset_rtp_channels();
+ }
+}
+
+/****************************************************************************/
+void
+reset_rtp_player(void)
+{
+ /* Destroy the rtp channels */
+ reset_channels();
+
+ /* destroy the rtp streams hash table */
+ if (rtp_streams_hash) {
+ g_hash_table_destroy(rtp_streams_hash);
+ rtp_streams_hash = NULL;
+ }
+
+ /* destroy the rtp streams list */
+ if (rtp_streams_list) {
+ g_list_free (rtp_streams_list);
+ rtp_streams_list = NULL;
+ }
+
+}
+
+/****************************************************************************/
+static void
+decode_streams(void)
+{
+ guint statusbar_context;
+ guint counter;
+
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(FALSE, FALSE, FALSE, FALSE);
+
+ reset_channels();
+
+ progress_bar = gtk_progress_bar_new();
+ gtk_widget_set_size_request(progress_bar, 100, -1);
+ gtk_box_pack_start(GTK_BOX (stat_hbox), progress_bar, FALSE, FALSE, 2);
+ gtk_widget_show(progress_bar);
+ statusbar_context = gtk_statusbar_get_context_id((GtkStatusbar *) info_bar, "main");
+ gtk_statusbar_push((GtkStatusbar *) info_bar, statusbar_context, " Decoding RTP packets...");
+
+#if !GTK_CHECK_VERSION(3,0,0)
+ gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(info_bar), FALSE);
+#endif
+ /* reset the number of packet to be decoded, this is used for the progress bar */
+ total_packets = 0;
+ /* reset the Progress Bar count */
+ progbar_count = 0;
+
+ /* Mark the RTP streams to be played using the selected VoipCalls. If voip_calls is NULL
+ then this was called from "RTP Analysis" so mark all strams */
+ if (rtp_streams_hash) {
+ if (voip_calls)
+ g_hash_table_foreach( rtp_streams_hash, (GHFunc)mark_rtp_stream_to_play, NULL);
+ else
+ g_hash_table_foreach( rtp_streams_hash, (GHFunc)mark_all_rtp_stream_to_play, NULL);
+ }
+
+ /* Decode the RTP streams and add them to the RTP channels to be played */
+ g_list_foreach( rtp_streams_list, (GFunc)decode_rtp_stream, NULL);
+
+ /* reset the number of frames to be displayed, this is used for the progress bar */
+ total_frames = 0;
+ /* Count the frames in all the RTP channels */
+ if (rtp_channels_hash)
+ g_hash_table_foreach( rtp_channels_hash, (GHFunc)count_channel_frames, NULL);
+
+ /* reset the Progress Bar count again for the progress of creating the channels view */
+ progbar_count = 0;
+ gtk_statusbar_pop((GtkStatusbar *) info_bar, statusbar_context);
+ gtk_statusbar_push((GtkStatusbar *) info_bar, statusbar_context, " Creating channels view...");
+
+ /* Display the RTP channels in the window */
+ counter = 0;
+ if (rtp_channels_hash)
+ g_hash_table_foreach( rtp_channels_hash, (GHFunc)add_channel_to_window, &counter);
+
+ /* Resize the main scroll window to display no more than preferred (or default) max channels, scroll bar will be used if needed */
+
+ if (prefs.rtp_player_max_visible < 1 || prefs.rtp_player_max_visible > 10)
+ prefs.rtp_player_max_visible = RTP_PLAYER_DEFAULT_VISIBLE;
+
+ gtk_widget_set_size_request(main_scrolled_window, CHANNEL_WIDTH,
+ MIN(counter, prefs.rtp_player_max_visible) * (CHANNEL_HEIGHT+60));
+
+ gtk_widget_show_all(main_scrolled_window);
+
+ gtk_widget_destroy(progress_bar);
+#if !GTK_CHECK_VERSION(3,0,0)
+ gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(info_bar), TRUE);
+#endif
+ gtk_statusbar_pop((GtkStatusbar *) info_bar, statusbar_context);
+
+ /* blank the status label */
+ gtk_statusbar_pop((GtkStatusbar *) info_bar, statusbar_context);
+
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(TRUE, FALSE, FALSE, FALSE);
+
+ /* get the static jitter buffer from the spinner gui */
+ new_jitter_buff = (int) gtk_spin_button_get_value((GtkSpinButton * )jitter_spinner);
+
+}
+
+/****************************************************************************/
+static void
+on_cb_view_as_time_of_day_clicked(GtkButton *button _U_, gpointer user_data _U_)
+{
+ /* Decode the streams again as if the decode button was pushed to update the time display */
+ decode_streams();
+}
+
+/****************************************************************************/
+static void
+on_cb_use_rtp_clicked(GtkToggleButton *button _U_, gpointer user_data _U_)
+{
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(TRUE, FALSE, FALSE, FALSE);
+}
+
+/****************************************************************************/
+static void
+on_bt_decode_clicked(GtkButton *button _U_, gpointer user_data _U_)
+{
+ decode_streams();
+}
+
+/****************************************************************************/
+static void
+on_bt_play_clicked(GtkButton *button _U_, gpointer user_data _U_)
+{
+ play_channels();
+}
+
+/****************************************************************************/
+static void
+on_bt_pause_clicked(GtkButton *button _U_, gpointer user_data _U_)
+{
+ pause_channels();
+}
+
+/****************************************************************************/
+static void
+on_bt_stop_clicked(GtkButton *button _U_, gpointer user_data _U_)
+{
+ stop_channels();
+}
+
+/****************************************************************************/
+static void
+rtp_player_on_destroy(GObject *object _U_, gpointer user_data _U_)
+{
+ PaError err;
+ GtkWidget *dialog;
+
+ /* Stop the channels if necesary */
+ if(rtp_channels && (!rtp_channels->stop)){
+ stop_channels();
+ }
+
+ /* Destroy the rtp channels */
+ reset_channels();
+
+ g_free(rtp_channels);
+ rtp_channels = NULL;
+
+ /* Terminate the use of PortAudio library */
+ err = Pa_Terminate();
+ initialized = FALSE;
+ if( err != paNoError ) {
+ dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
+ "Can not terminate the PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ }
+
+ gtk_widget_destroy(rtp_player_dlg_w);
+ main_scrolled_window = NULL;
+ rtp_player_dlg_w = NULL;
+}
+
+/****************************************************************************/
+static void
+jitter_spinner_value_changed (GtkSpinButton *spinner _U_, gpointer user_data _U_)
+{
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(TRUE, TRUE, FALSE, FALSE);
+}
+
+/****************************************************************************/
+static void
+rtp_player_dlg_create(void)
+{
+ GtkWidget *main_vb;
+ GtkWidget *hbuttonbox;
+ GtkWidget *timestamp_hb;
+ GtkWidget *h_jitter_buttons_box;
+ GtkWidget *bt_close;
+ GtkAdjustment *jitter_spinner_adj;
+ GtkWidget *label;
+ const gchar *title_name_ptr;
+ gchar *win_name;
+
+ title_name_ptr = cf_get_display_name(&cfile);
+ win_name = g_strdup_printf("%s - VoIP - RTP Player", title_name_ptr);
+
+ rtp_player_dlg_w = dlg_window_new(win_name); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(rtp_player_dlg_w), TRUE);
+ gtk_window_set_position(GTK_WINDOW(rtp_player_dlg_w), GTK_WIN_POS_NONE);
+
+ gtk_window_set_default_size(GTK_WINDOW(rtp_player_dlg_w), 400, 50);
+
+ main_vb = gtk_vbox_new (FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(rtp_player_dlg_w), main_vb);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vb), 2);
+
+ main_scrolled_window=gtk_scrolled_window_new(NULL, NULL);
+ gtk_container_set_border_width (GTK_CONTAINER (main_scrolled_window), 4);
+ gtk_widget_set_size_request(main_scrolled_window, CHANNEL_WIDTH, 0);
+
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (main_scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_container_add(GTK_CONTAINER(main_vb), main_scrolled_window);
+
+ channels_vb = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (channels_vb), 2);
+ gtk_scrolled_window_add_with_viewport((GtkScrolledWindow *) main_scrolled_window, channels_vb);
+
+ timestamp_hb = gtk_hbox_new(FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(main_vb), timestamp_hb, FALSE, FALSE, 0);
+ cb_view_as_time_of_day = gtk_check_button_new_with_label("View as time of day");
+ gtk_box_pack_start(GTK_BOX(timestamp_hb), cb_view_as_time_of_day, TRUE, FALSE, 0); /* Centered */
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb_view_as_time_of_day), FALSE);
+ gtk_widget_set_tooltip_text(cb_view_as_time_of_day, "View the timestamps as time of day instead of seconds since beginning of capture");
+ g_signal_connect(cb_view_as_time_of_day, "toggled", G_CALLBACK(on_cb_view_as_time_of_day_clicked), NULL);
+
+ h_jitter_buttons_box = gtk_hbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (h_jitter_buttons_box), 10);
+ gtk_box_pack_start (GTK_BOX(main_vb), h_jitter_buttons_box, FALSE, FALSE, 0);
+ label = gtk_label_new("Jitter buffer [ms] ");
+ gtk_box_pack_start(GTK_BOX(h_jitter_buttons_box), label, FALSE, FALSE, 0);
+
+ jitter_spinner_adj = (GtkAdjustment *) gtk_adjustment_new (50, 0, 500, 5, 10, 0);
+ jitter_spinner = gtk_spin_button_new (jitter_spinner_adj, 5, 0);
+ gtk_box_pack_start(GTK_BOX(h_jitter_buttons_box), jitter_spinner, FALSE, FALSE, 0);
+ gtk_widget_set_tooltip_text (jitter_spinner, "The simulated jitter buffer in [ms]");
+ g_signal_connect(G_OBJECT (jitter_spinner_adj), "value_changed", G_CALLBACK(jitter_spinner_value_changed), NULL);
+
+ cb_use_rtp_timestamp = gtk_check_button_new_with_label("Use RTP timestamp");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb_use_rtp_timestamp), FALSE);
+ gtk_box_pack_start(GTK_BOX(h_jitter_buttons_box), cb_use_rtp_timestamp, FALSE, FALSE, 10);
+ g_signal_connect(cb_use_rtp_timestamp, "toggled", G_CALLBACK(on_cb_use_rtp_clicked), NULL);
+ gtk_widget_set_tooltip_text (cb_use_rtp_timestamp, "Use RTP Timestamp instead of the arriving packet time. This will not reproduce the RTP stream as the user heard it, but is useful when the RTP is being tunneled and the original packet timing is missing");
+
+ /* button row */
+ hbuttonbox = gtk_hbutton_box_new ();
+ gtk_box_pack_start (GTK_BOX (h_jitter_buttons_box), hbuttonbox, TRUE, TRUE, 0);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_SPREAD);
+ gtk_box_set_spacing (GTK_BOX (hbuttonbox), 10);
+
+ bt_decode = gtk_button_new_from_stock(WIRESHARK_STOCK_DECODE);
+ gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_decode);
+ g_signal_connect(bt_decode, "clicked", G_CALLBACK(on_bt_decode_clicked), NULL);
+ gtk_widget_set_tooltip_text (bt_decode, "Decode the RTP stream(s)");
+
+ bt_play = gtk_button_new_from_stock(GTK_STOCK_MEDIA_PLAY);
+ gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_play);
+ g_signal_connect(bt_play, "clicked", G_CALLBACK(on_bt_play_clicked), NULL);
+ gtk_widget_set_tooltip_text (bt_play, "Play the RTP channel(s)");
+
+ bt_pause = gtk_button_new_from_stock(GTK_STOCK_MEDIA_PAUSE);
+ gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_pause);
+ g_signal_connect(bt_pause, "clicked", G_CALLBACK(on_bt_pause_clicked), NULL);
+ gtk_widget_set_tooltip_text (bt_pause, "Pause the RTP channel(s)");
+
+ bt_stop = gtk_button_new_from_stock(GTK_STOCK_MEDIA_STOP);
+ gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_stop);
+ g_signal_connect(bt_stop, "clicked", G_CALLBACK(on_bt_stop_clicked), NULL);
+ gtk_widget_set_tooltip_text (bt_stop, "Stop the RTP channel(s)");
+
+ bt_close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_close);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(bt_close, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(bt_close, GTK_CAN_DEFAULT);
+#endif
+ gtk_widget_set_tooltip_text (bt_close, "Close this dialog");
+ window_set_cancel_button(rtp_player_dlg_w, bt_close, window_cancel_button_cb);
+
+ g_signal_connect(rtp_player_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(rtp_player_dlg_w, "destroy", G_CALLBACK(rtp_player_on_destroy), NULL);
+
+ /* button row */
+ hbuttonbox = gtk_hbutton_box_new ();
+
+ /* Filter/status hbox */
+ stat_hbox = gtk_hbox_new(FALSE, 1);
+ gtk_container_set_border_width(GTK_CONTAINER(stat_hbox), 0);
+
+ /* statusbar */
+ info_bar = gtk_statusbar_new();
+#if !GTK_CHECK_VERSION(3,0,0)
+ gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(info_bar), TRUE);
+#endif
+ gtk_box_pack_start(GTK_BOX(stat_hbox), info_bar, TRUE, TRUE, 0);
+
+ /* statusbar hbox */
+ gtk_box_pack_start(GTK_BOX(main_vb), stat_hbox, FALSE, TRUE, 0);
+
+ /* set the sensitive state of the buttons (decode, play, pause, stop) */
+ bt_state(TRUE, FALSE, FALSE, FALSE);
+
+ gtk_widget_show_all(rtp_player_dlg_w);
+
+ /* Force gtk to redraw the window before starting decoding the packet */
+ while (g_main_context_iteration(NULL, FALSE));
+
+ g_free(win_name);
+}
+
+/****************************************************************************/
+void
+rtp_player_init(voip_calls_tapinfo_t *voip_calls_tap)
+{
+ PaError err;
+ GtkWidget *dialog;
+
+ if (initialized) return;
+ initialized = TRUE;
+
+ voip_calls = voip_calls_tap;
+ err = Pa_Initialize();
+ if( err != paNoError ) {
+ dialog = gtk_message_dialog_new ((GtkWindow *) rtp_player_dlg_w,
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,GTK_BUTTONS_CLOSE,
+ "Can not Initialize the PortAudio Library.\n Error: %s", Pa_GetErrorText( err ));
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ initialized = FALSE;
+ return;
+ }
+
+ new_jitter_buff = -1;
+
+#ifdef HAVE_G729_G723
+ /* Initialize the G729 and G723 decoders */
+ initG723();
+ initG729();
+#endif /* HAVE_G729_G723 */
+
+ if (!rtp_channels) {
+ rtp_channels = g_malloc(sizeof(rtp_play_channels_t));
+ }
+
+ reset_rtp_channels();
+
+#ifdef DEBUG
+ g_print("Pa_GetHostApiCount() = %d\n", Pa_GetHostApiCount());
+ g_print("Pa_GetDefaultHostApi() = %d\n", Pa_GetDefaultHostApi());
+
+ if ((Pa_GetHostApiCount() >= 0) && (Pa_GetDefaultHostApi() >= 0))
+ {
+ unsigned int i;
+ PaHostApiIndex api_index;
+ const PaHostApiInfo *api_info = Pa_GetHostApiInfo( (unsigned int)Pa_GetDefaultHostApi() );
+ g_print("Default PaHostApiInfo.type = %d (%s)\n", api_info->type, api_info->name);
+
+ for (i=0; i<(unsigned int)Pa_GetHostApiCount(); i++)
+ {
+ api_info = Pa_GetHostApiInfo( i );
+ g_print("PaHostApiInfo[%u].type = %d (%s)\n", i, api_info->type, api_info->name);
+ }
+
+ api_index = Pa_HostApiTypeIdToHostApiIndex( paALSA );
+ if (api_index < 0)
+ {
+ g_print("api_index for paALSA not found (%d)\n", api_index);
+ }
+ else
+ {
+ api_info = Pa_GetHostApiInfo( (unsigned int)api_index );
+ g_print("This should be ALSA: %s\n", api_info->name);
+ }
+ }
+#endif
+
+ /* create the dialog window */
+ rtp_player_dlg_create();
+
+}
+
+#endif /* HAVE_LIBPORTAUDIO */
diff --git a/ui/gtk/rtp_player.h b/ui/gtk/rtp_player.h
new file mode 100644
index 0000000000..b7edd1991b
--- /dev/null
+++ b/ui/gtk/rtp_player.h
@@ -0,0 +1,47 @@
+/* player_rtp.h
+ * RTP Player for Wireshark
+ *
+ * $Id$
+ *
+ * Copyright 2006, Alejandro Vaquero
+ * By Alejandro Vaquero <alejandro.vaquero@yahoo.com>
+ *
+ * based on h323_calls.h
+ * Copyright 2004, Iskratel, Ltd, Kranj
+ * By Miha Jemec <m.jemec@iskratel.si>
+ *
+ * H323, RTP and Graph Support
+ * By Alejandro Vaquero, alejandro.vaquero@verso.com
+ * Copyright 2005, Verso Technologies Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __RTP_PLAYER_H__
+#define __RTP_PLAYER_H__
+
+#ifdef HAVE_LIBPORTAUDIO
+
+void rtp_player_init(voip_calls_tapinfo_t *voip_calls_tap);
+void add_rtp_packet(const struct _rtp_info *rtp_info, packet_info *pinfo);
+void reset_rtp_player(void);
+
+#endif /* HAVE_LIBPORTAUDIO */
+
+#endif /* __RTP_PLAYER_H__ */
diff --git a/ui/gtk/rtp_stream.c b/ui/gtk/rtp_stream.c
new file mode 100644
index 0000000000..2d96a10d93
--- /dev/null
+++ b/ui/gtk/rtp_stream.c
@@ -0,0 +1,202 @@
+/* rtp_stream.c
+ * RTP streams summary addition for Wireshark
+ *
+ * $Id$
+ *
+ * Copyright 2003, Alcatel Business Systems
+ * By Lars Ruoff <lars.ruoff@gmx.net>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+
+#include <epan/epan.h>
+#include <epan/packet.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-rtp.h>
+#include <epan/addr_resolv.h>
+
+#include "../globals.h"
+#include "../alert_box.h"
+#include "../simple_dialog.h"
+#include "../tap-rtp-common.h"
+#include <wsutil/file_util.h>
+
+#include "ui/gtk/rtp_stream.h"
+#include "ui/gtk/rtp_stream_dlg.h"
+#include "ui/gtk/main.h"
+
+/* The one and only global rtpstream_tapinfo_t structure for tshark and wireshark.
+ */
+static rtpstream_tapinfo_t the_tapinfo_struct =
+ {0, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, 0, FALSE};
+
+
+/****************************************************************************/
+/* redraw the output */
+static void rtpstream_draw(void *arg _U_)
+{
+/* XXX: see rtpstream_on_update in rtp_streams_dlg.c for comments
+ g_signal_emit_by_name(top_level, "signal_rtpstream_update");
+*/
+ rtpstream_dlg_update(the_tapinfo_struct.strinfo_list);
+ return;
+}
+
+
+/****************************************************************************/
+/* scan for RTP streams */
+void rtpstream_scan(void)
+{
+ gboolean was_registered = the_tapinfo_struct.is_registered;
+ if (!the_tapinfo_struct.is_registered)
+ register_tap_listener_rtp_stream();
+
+ the_tapinfo_struct.mode = TAP_ANALYSE;
+ cf_retap_packets(&cfile);
+
+ if (!was_registered)
+ remove_tap_listener_rtp_stream();
+}
+
+
+/****************************************************************************/
+/* save rtp dump of stream_fwd */
+gboolean rtpstream_save(rtp_stream_info_t* stream, const gchar *filename)
+{
+ gboolean was_registered = the_tapinfo_struct.is_registered;
+ /* open file for saving */
+ the_tapinfo_struct.save_file = ws_fopen(filename, "wb");
+ if (the_tapinfo_struct.save_file==NULL) {
+ open_failure_alert_box(filename, errno, TRUE);
+ return FALSE;
+ }
+
+ rtp_write_header(stream, the_tapinfo_struct.save_file);
+ if (ferror(the_tapinfo_struct.save_file)) {
+ write_failure_alert_box(filename, errno);
+ fclose(the_tapinfo_struct.save_file);
+ return FALSE;
+ }
+
+ if (!the_tapinfo_struct.is_registered)
+ register_tap_listener_rtp_stream();
+
+ the_tapinfo_struct.mode = TAP_SAVE;
+ the_tapinfo_struct.filter_stream_fwd = stream;
+ cf_retap_packets(&cfile);
+ the_tapinfo_struct.mode = TAP_ANALYSE;
+
+ if (!was_registered)
+ remove_tap_listener_rtp_stream();
+
+ if (ferror(the_tapinfo_struct.save_file)) {
+ write_failure_alert_box(filename, errno);
+ fclose(the_tapinfo_struct.save_file);
+ return FALSE;
+ }
+
+ if (fclose(the_tapinfo_struct.save_file) == EOF) {
+ write_failure_alert_box(filename, errno);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/****************************************************************************/
+/* mark packets in stream_fwd or stream_rev */
+void rtpstream_mark(rtp_stream_info_t* stream_fwd, rtp_stream_info_t* stream_rev)
+{
+ gboolean was_registered = the_tapinfo_struct.is_registered;
+ if (!the_tapinfo_struct.is_registered)
+ register_tap_listener_rtp_stream();
+
+ the_tapinfo_struct.mode = TAP_MARK;
+ the_tapinfo_struct.filter_stream_fwd = stream_fwd;
+ the_tapinfo_struct.filter_stream_rev = stream_rev;
+ cf_retap_packets(&cfile);
+ the_tapinfo_struct.mode = TAP_ANALYSE;
+
+ if (!was_registered)
+ remove_tap_listener_rtp_stream();
+}
+
+
+/****************************************************************************/
+const rtpstream_tapinfo_t* rtpstream_get_info(void)
+{
+ return &the_tapinfo_struct;
+}
+
+
+/****************************************************************************/
+/* TAP INTERFACE */
+/****************************************************************************/
+
+/****************************************************************************/
+void
+remove_tap_listener_rtp_stream(void)
+{
+ if (the_tapinfo_struct.is_registered) {
+ protect_thread_critical_region();
+ remove_tap_listener(&the_tapinfo_struct);
+ unprotect_thread_critical_region();
+
+ the_tapinfo_struct.is_registered = FALSE;
+ }
+}
+
+
+/****************************************************************************/
+void
+register_tap_listener_rtp_stream(void)
+{
+ GString *error_string;
+
+ if (!the_tapinfo_struct.is_registered) {
+ error_string = register_tap_listener("rtp", &the_tapinfo_struct,
+ NULL, 0, rtpstream_reset_cb, rtpstream_packet,
+ rtpstream_draw);
+
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+
+ the_tapinfo_struct.is_registered = TRUE;
+ }
+}
diff --git a/ui/gtk/rtp_stream.h b/ui/gtk/rtp_stream.h
new file mode 100644
index 0000000000..e539b6ee4b
--- /dev/null
+++ b/ui/gtk/rtp_stream.h
@@ -0,0 +1,155 @@
+/* rtp_stream.h
+ * RTP streams summary addition for Wireshark
+ *
+ * $Id$
+ *
+ * Copyright 2003, Alcatel Business Systems
+ * By Lars Ruoff <lars.ruoff@gmx.net>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __RTP_STREAM_H__
+#define __RTP_STREAM_H__
+
+#include "rtp_analysis.h"
+#include <glib.h>
+#include <stdio.h>
+#include <epan/address.h>
+
+
+/****************************************************************************/
+/* type for storing rtp frame information */
+typedef struct st_rtp_sample_header {
+ guint32 rec_time; /* milliseconds since start of recording */
+ guint16 frame_length; /* number of bytes in *frame */
+} rtp_sample_header_t;
+
+/* type for storing rtp frame information */
+typedef struct st_rtp_sample {
+ rtp_sample_header_t header; /* date and size */
+ const guint8 *frame; /* data bytes */
+} rtp_sample_t;
+
+typedef rtp_sample_t* rtp_sample_p;
+
+
+/* defines an rtp stream */
+typedef struct _rtp_stream_info {
+ address src_addr;
+ guint16 src_port;
+ address dest_addr;
+ guint16 dest_port;
+ guint32 ssrc;
+ guint8 pt;
+ gchar *info_payload_type_str;
+ guint32 npackets;
+
+ guint32 first_frame_num; /* frame number of first frame */
+ guint32 setup_frame_number; /* frame number of setup message */
+ /* start of recording (GMT) of this stream */
+ guint32 start_sec; /* seconds */
+ guint32 start_usec; /* microseconds */
+ gboolean tag_vlan_error;
+ guint32 start_rel_sec; /* start stream rel seconds */
+ guint32 start_rel_usec; /* start stream rel microseconds */
+ guint32 stop_rel_sec; /* stop stream rel seconds */
+ guint32 stop_rel_usec; /* stop stream rel microseconds */
+ gboolean tag_diffserv_error;
+ guint16 vlan_id;
+
+ tap_rtp_stat_t rtp_stats; /* here goes the RTP statistics info */
+ gboolean problem; /* if the streams had wrong sequence numbers or wrong timerstamps */
+} rtp_stream_info_t;
+
+
+/* tapping modes */
+typedef enum
+{
+ TAP_ANALYSE,
+ TAP_SAVE,
+ TAP_MARK
+} tap_mode_t;
+
+
+/* structure that holds the information about all detected streams */
+/* struct holding all information of the tap */
+typedef struct _rtpstream_tapinfo {
+ int nstreams; /* number of streams in the list */
+ GList* strinfo_list; /* list with all streams */
+ int npackets; /* total number of rtp packets of all streams */
+ /* used while tapping. user shouldnt modify these */
+ tap_mode_t mode;
+ rtp_stream_info_t* filter_stream_fwd; /* used as filter in some tap modes */
+ rtp_stream_info_t* filter_stream_rev; /* used as filter in some tap modes */
+ FILE* save_file;
+ guint32 launch_count; /* number of times the tap has been run */
+ gboolean is_registered; /* if the tap listener is currently registered or not */
+} rtpstream_tapinfo_t;
+
+/****************************************************************************/
+/* INTERFACE */
+
+/**
+* Registers the rtp_streams tap listener (if not already done).
+* From that point on, the RTP streams list will be updated with every redissection.
+* This function is also the entry point for the initialization routine of the tap system.
+* So whenever rtp_stream.c is added to the list of WIRESHARK_TAP_SRCs, the tap will be registered on startup.
+* If not, it will be registered on demand by the rtp_streams and rtp_analysis functions that need it.
+*/
+void register_tap_listener_rtp_stream(void);
+
+/**
+* Removes the rtp_streams tap listener (if not already done)
+* From that point on, the RTP streams list won't be updated any more.
+*/
+void remove_tap_listener_rtp_stream(void);
+
+/**
+* Retrieves a constant reference to the unique info structure of the rtp_streams tap listener.
+* The user should not modify the data pointed to.
+*/
+const rtpstream_tapinfo_t* rtpstream_get_info(void);
+
+/**
+* Cleans up memory of rtp streams tap.
+*/
+void rtpstream_reset(rtpstream_tapinfo_t *tapinfo);
+
+/**
+* Scans all packets for RTP streams and updates the RTP streams list.
+* (redissects all packets)
+*/
+void rtpstream_scan(void);
+
+/**
+* Saves an RTP stream as raw data stream with timestamp information for later RTP playback.
+* (redissects all packets)
+*/
+gboolean rtpstream_save(rtp_stream_info_t* stream, const gchar *filename);
+
+/**
+* Marks all packets belonging to either of stream_fwd or stream_rev.
+* (both can be NULL)
+* (redissects all packets)
+*/
+void rtpstream_mark(rtp_stream_info_t* stream_fwd, rtp_stream_info_t* stream_rev);
+
+
+#endif /* __RTP_STREAM_H__ */
diff --git a/ui/gtk/rtp_stream_dlg.c b/ui/gtk/rtp_stream_dlg.c
new file mode 100644
index 0000000000..977e616ae0
--- /dev/null
+++ b/ui/gtk/rtp_stream_dlg.c
@@ -0,0 +1,1140 @@
+/* rtp_stream_dlg.c
+ * RTP streams summary addition for Wireshark
+ *
+ * $Id$
+ *
+ * Copyright 2003, Alcatel Business Systems
+ * By Lars Ruoff <lars.ruoff@gmx.net>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <locale.h>
+
+#include <epan/rtp_pt.h>
+#include <epan/address.h>
+#include <epan/addr_resolv.h>
+#include <epan/strutil.h>
+#include "epan/filesystem.h"
+
+#include "../globals.h"
+#include "../stat_menu.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/rtp_stream_dlg.h"
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/file_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/rtp_stream.h"
+#include "ui/gtk/rtp_analysis.h"
+#include "ui/gtk/stock_icons.h"
+
+static const gchar FWD_LABEL_TEXT[] = "Select a forward stream with left mouse button, and then";
+static const gchar FWD_ONLY_LABEL_TEXT[] = "Select a forward stream with Ctrl + left mouse button";
+static const gchar REV_LABEL_TEXT[] = "Select a reverse stream with Ctrl + left mouse button";
+
+/****************************************************************************/
+/* pointer to the one and only dialog window */
+static GtkWidget *rtp_stream_dlg = NULL;
+
+/* save as dialog box */
+static GtkWidget *rtpstream_save_dlg = NULL;
+static GtkListStore *list_store = NULL;
+static GtkTreeIter list_iter;
+static GtkWidget *list = NULL;
+static GtkWidget *top_label = NULL;
+static GtkWidget *label_fwd = NULL;
+static GtkWidget *label_rev = NULL;
+
+static rtp_stream_info_t* selected_stream_fwd = NULL; /* current selection */
+static rtp_stream_info_t* selected_stream_rev = NULL; /* current selection for reversed */
+static GList *last_list = NULL;
+
+static guint32 streams_nb = 0; /* number of displayed streams */
+
+enum
+{
+ RTP_COL_SRC_ADDR,
+ RTP_COL_SRC_PORT,
+ RTP_COL_DST_ADDR,
+ RTP_COL_DST_PORT,
+ RTP_COL_SSRC,
+ RTP_COL_PAYLOAD,
+ RTP_COL_PACKETS,
+ RTP_COL_LOST,
+ RTP_COL_MAX_DELTA,
+ RTP_COL_MAX_JITTER,
+ RTP_COL_MEAN_JITTER,
+ RTP_COL_PROBLEM,
+ RTP_COL_DATA,
+ NUM_COLS /* The number of columns */
+};
+
+
+/****************************************************************************/
+static void save_stream_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+ /* Note that we no longer have a Save voice info dialog box. */
+ rtpstream_save_dlg = NULL;
+}
+
+/****************************************************************************/
+/* save in a file */
+static gboolean save_stream_ok_cb(GtkWidget *ok_bt _U_, gpointer fs)
+{
+ gchar *g_dest;
+
+ if (!selected_stream_fwd) {
+ return TRUE;
+ }
+
+ g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
+
+ /* Perhaps the user specified a directory instead of a file.
+ Check whether they did. */
+ if (test_for_directory(g_dest) == EISDIR) {
+ /* It's a directory - set the file selection box to display it. */
+ set_last_open_dir(g_dest);
+ g_free(g_dest);
+ file_selection_set_current_folder(fs, get_last_open_dir());
+ gtk_file_chooser_set_current_name(fs, "");
+ return FALSE;
+ }
+
+#if 0 /* GtkFileChooser/gtk_dialog_run currently being used. */
+ /* So: Leaving the dialog box displayed after popping-up an */
+ /* alert box won't work. */
+ /*
+ * Don't dismiss the dialog box if the save operation fails.
+ */
+ if (!rtpstream_save(selected_stream_fwd, g_dest)) {
+ g_free(g_dest);
+ return;
+ }
+ g_free(g_dest);
+ window_destroy(GTK_WIDGET(rtpstream_save_dlg));
+ return;
+#else
+ /* Dialog box needs to be always destroyed. Return TRUE */
+ /* so that caller will destroy the dialog box. */
+ /* See comment under rtpstream_on_save. */
+ rtpstream_save(selected_stream_fwd, g_dest);
+ g_free(g_dest);
+ return TRUE;
+#endif
+}
+
+
+/****************************************************************************/
+/* CALLBACKS */
+/****************************************************************************/
+static void
+rtpstream_on_destroy(GObject *object _U_, gpointer user_data _U_)
+{
+ /* Remove the stream tap listener */
+ remove_tap_listener_rtp_stream();
+
+ /* Is there a save voice window open? */
+ if (rtpstream_save_dlg != NULL)
+ window_destroy(rtpstream_save_dlg);
+
+ /* Clean up memory used by stream tap */
+ rtpstream_reset((rtpstream_tapinfo_t *)rtpstream_get_info());
+
+ /* Note that we no longer have a "RTP Streams" dialog box. */
+ rtp_stream_dlg = NULL;
+}
+
+
+/****************************************************************************/
+static void
+rtpstream_on_unselect(GtkButton *button _U_, gpointer user_data _U_)
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+ gtk_tree_selection_unselect_all(selection);
+
+ selected_stream_fwd = NULL;
+ selected_stream_rev = NULL;
+ gtk_label_set_text(GTK_LABEL(label_fwd), FWD_LABEL_TEXT);
+ gtk_label_set_text(GTK_LABEL(label_rev), REV_LABEL_TEXT);
+}
+
+
+/****************************************************************************/
+static gint rtp_stream_info_cmp_reverse(gconstpointer aa, gconstpointer bb)
+{
+ const struct _rtp_stream_info* a = aa;
+ const struct _rtp_stream_info* b = bb;
+
+ if (a==NULL || b==NULL)
+ return 1;
+ if ((ADDRESSES_EQUAL(&(a->src_addr), &(b->dest_addr)))
+ && (a->src_port == b->dest_port)
+ && (ADDRESSES_EQUAL(&(a->dest_addr), &(b->src_addr)))
+ && (a->dest_port == b->src_port))
+ return 0;
+ else
+ return 1;
+}
+
+/****************************************************************************/
+static void
+rtpstream_on_findrev(GtkButton *button _U_, gpointer user_data _U_)
+{
+ GtkTreeSelection *selection;
+ GList *path_list;
+ GList *path_list_item = NULL;
+ GtkTreePath *path = NULL;
+ GtkTreePath *path_fwd = NULL;
+ GtkTreePath *path_rev = NULL;
+ GtkTreeIter iter;
+ rtp_stream_info_t *stream = NULL;
+ gboolean found_it = FALSE;
+
+ if (selected_stream_fwd==NULL)
+ return;
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+ path_list = gtk_tree_selection_get_selected_rows(selection, NULL);
+
+ if (path_list) {
+ path_list_item = g_list_first(path_list);
+ path = (GtkTreePath *)(path_list_item->data);
+ }
+
+ if (path && gtk_tree_model_get_iter(GTK_TREE_MODEL(list_store), &iter, path)) {
+ gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, RTP_COL_DATA, &stream, -1);
+ if (stream == selected_stream_fwd) {
+ path_fwd = path;
+ }
+ if (stream == selected_stream_rev) {
+ path_rev = path;
+ }
+ }
+
+ path = NULL;
+ if (path_list_item) {
+ path_list_item = g_list_next(path_list_item);
+ if (path_list_item)
+ path = (GtkTreePath *)(path_list_item->data);
+ }
+
+ if (path && gtk_tree_model_get_iter(GTK_TREE_MODEL(list_store), &iter, path)) {
+ gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, RTP_COL_DATA, &stream, -1);
+ if (stream == selected_stream_fwd) {
+ path_fwd = path;
+ }
+ if (stream == selected_stream_rev) {
+ path_rev = path;
+ }
+ }
+
+ /* Find it from the forward stream on */
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(list_store), &iter, path_fwd);
+ while (gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store), &iter)) {
+ gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, RTP_COL_DATA, &stream, -1);
+ if (rtp_stream_info_cmp_reverse(selected_stream_fwd, stream) == 0) {
+ found_it = TRUE;
+ break;
+ }
+ };
+
+ if (!found_it) {
+ /* If we're not done yet, restart at the beginning */
+ gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list_store), &iter);
+ do {
+ gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, RTP_COL_DATA, &stream, -1);
+ if (rtp_stream_info_cmp_reverse(selected_stream_fwd, stream) == 0) {
+ found_it = TRUE;
+ break;
+ }
+ if (stream == selected_stream_fwd)
+ break;
+ } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store), &iter));
+ }
+
+ if (found_it) {
+ if (path_rev)
+ gtk_tree_selection_unselect_path(selection, path_rev);
+ gtk_tree_selection_select_iter(selection, &iter);
+ }
+
+ g_list_foreach(path_list, (GFunc)gtk_tree_path_free, NULL);
+ g_list_free(path_list);
+}
+
+
+/****************************************************************************/
+/*
+static void
+rtpstream_on_goto (GtkButton *button _U_,
+ gpointer user_data _U_)
+{
+ if (selected_stream_fwd)
+ {
+ cf_goto_frame(&cfile, selected_stream_fwd->first_frame_num);
+ }
+}
+*/
+
+
+/****************************************************************************/
+static void
+rtpstream_on_save(GtkButton *button _U_, gpointer data _U_)
+{
+/* XX - not needed?
+ rtpstream_tapinfo_t* tapinfo = data;
+*/
+
+ if (!selected_stream_fwd) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Please select a forward stream");
+ return;
+ }
+
+#if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
+ if (rtpstream_save_dlg != NULL) {
+ /* There's already a Save dialog box; reactivate it. */
+ reactivate_window(rtpstream_save_dlg);
+ return;
+ }
+#endif
+
+ rtpstream_save_dlg = gtk_file_chooser_dialog_new(
+ "Wireshark: Save selected stream in rtpdump ('-F dump') format",
+ GTK_WINDOW(rtp_stream_dlg), GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ NULL);
+ gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(rtpstream_save_dlg), TRUE);
+
+ g_signal_connect(rtpstream_save_dlg, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(rtpstream_save_dlg, "destroy", G_CALLBACK(save_stream_destroy_cb), NULL);
+
+ gtk_widget_show(rtpstream_save_dlg);
+ window_present(rtpstream_save_dlg);
+#if 0
+ if (gtk_dialog_run(GTK_DIALOG(rtpstream_save_dlg)) == GTK_RESPONSE_ACCEPT){
+ save_stream_ok_cb(rtpstream_save_dlg, rtpstream_save_dlg);
+ }else{
+ window_destroy(rtpstream_save_dlg);
+ }
+#endif
+ /* "Run" the GtkFileChooserDialog. */
+ /* Upon exit: If "Accept" run the OK callback. */
+ /* If the OK callback returns with a FALSE status, re-run the dialog.*/
+ /* If not accept (ie: cancel) destroy the window. */
+ /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
+ /* return with a TRUE status so that the dialog window will be destroyed. */
+ /* Trying to re-run the dialog after popping up an alert box will not work */
+ /* since the user will not be able to dismiss the alert box. */
+ /* The (somewhat unfriendly) effect: the user must re-invoke the */
+ /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
+ /* */
+ /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
+ /* GtkFileChooserDialog. */
+ while (gtk_dialog_run(GTK_DIALOG(rtpstream_save_dlg)) == GTK_RESPONSE_ACCEPT) {
+ if (save_stream_ok_cb(NULL, rtpstream_save_dlg)) {
+ break; /* we're done */
+ }
+ }
+ window_destroy(rtpstream_save_dlg);
+}
+
+
+/****************************************************************************/
+static void
+rtpstream_on_mark(GtkButton *button _U_, gpointer user_data _U_)
+{
+ if (selected_stream_fwd==NULL && selected_stream_rev==NULL)
+ return;
+ rtpstream_mark(selected_stream_fwd, selected_stream_rev);
+}
+
+
+/****************************************************************************/
+static void
+rtpstream_on_filter(GtkButton *button _U_, gpointer user_data _U_)
+{
+ gchar *filter_string = NULL;
+ gchar *filter_string_fwd = NULL;
+ gchar *filter_string_rev = NULL;
+ gchar ip_version[3];
+
+ if (selected_stream_fwd==NULL && selected_stream_rev==NULL)
+ return;
+
+ if (selected_stream_fwd)
+ {
+ if (selected_stream_fwd->src_addr.type==AT_IPv6) {
+ g_strlcpy(ip_version,"v6",sizeof(ip_version));
+ } else {
+ ip_version[0] = '\0';
+ }
+ filter_string_fwd = g_strdup_printf(
+ "(ip%s.src==%s && udp.srcport==%u && ip%s.dst==%s && udp.dstport==%u && rtp.ssrc==0x%X)",
+ ip_version,
+ ep_address_to_str(&(selected_stream_fwd->src_addr)),
+ selected_stream_fwd->src_port,
+ ip_version,
+ ep_address_to_str(&(selected_stream_fwd->dest_addr)),
+ selected_stream_fwd->dest_port,
+ selected_stream_fwd->ssrc);
+
+ filter_string = filter_string_fwd;
+ }
+
+ if (selected_stream_rev)
+ {
+ if (selected_stream_rev->src_addr.type==AT_IPv6) {
+ g_strlcpy(ip_version,"v6",sizeof(ip_version));
+ } else {
+ ip_version[0] = '\0';
+ }
+ filter_string_rev = g_strdup_printf(
+ "(ip%s.src==%s && udp.srcport==%u && ip%s.dst==%s && udp.dstport==%u && rtp.ssrc==0x%X)",
+ ip_version,
+ ep_address_to_str(&(selected_stream_rev->src_addr)),
+ selected_stream_rev->src_port,
+ ip_version,
+ ep_address_to_str(&(selected_stream_rev->dest_addr)),
+ selected_stream_rev->dest_port,
+ selected_stream_rev->ssrc);
+
+ filter_string = filter_string_rev;
+ }
+
+ if ((selected_stream_fwd) && (selected_stream_rev))
+ {
+ filter_string = g_strdup_printf("%s || %s", filter_string_fwd, filter_string_rev);
+ g_free(filter_string_fwd);
+ g_free(filter_string_rev);
+ }
+
+ gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), filter_string);
+ g_free(filter_string);
+
+/*
+ main_filter_packets(&cfile, filter_string, FALSE);
+ rtpstream_dlg_update(rtpstream_get_info()->strinfo_list);
+*/
+}
+
+
+/****************************************************************************/
+static void
+rtpstream_on_copy_as_csv(GtkWindow *win _U_, gpointer data _U_)
+{
+ GtkTreeViewColumn *column;
+ const gchar *title;
+ GtkTreeIter iter;
+ guint i,j;
+ gchar *table_entry;
+ guint table_entry_uint;
+
+ GString *CSV_str;
+ GtkClipboard *cb;
+
+ CSV_str = g_string_sized_new(240*(1+streams_nb));
+ /* Add the column headers to the CSV data */
+ for (j=0; j<NUM_COLS-1; j++) {
+ column = gtk_tree_view_get_column(GTK_TREE_VIEW(list), j);
+ title = gtk_tree_view_column_get_title(column);
+ g_string_append_printf(CSV_str, "\"%s\"", title);
+ if (j<NUM_COLS-2) g_string_append(CSV_str, ",");
+ }
+ g_string_append(CSV_str,"\n");
+
+ /* Add the column values to the CSV data */
+ if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list_store), &iter)) {
+ for (i=0; i<streams_nb; i++) {
+ for (j=0; j<NUM_COLS-1; j++) {
+ if (j == RTP_COL_SRC_PORT || j == RTP_COL_DST_PORT || j == RTP_COL_PACKETS) {
+ gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, j, &table_entry_uint, -1);
+ g_string_append_printf(CSV_str, "\"%u\"", table_entry_uint);
+ } else {
+ gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, j, &table_entry, -1);
+ g_string_append_printf(CSV_str, "\"%s\"", table_entry);
+ g_free(table_entry);
+ }
+ if (j<NUM_COLS-2) g_string_append(CSV_str,",");
+ }
+ g_string_append(CSV_str,"\n");
+ gtk_tree_model_iter_next (GTK_TREE_MODEL(list_store),&iter);
+ }
+ }
+
+ /* Now that we have the CSV data, copy it into the default clipboard */
+ cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+ gtk_clipboard_set_text(cb, CSV_str->str, (gint)CSV_str->len);
+ g_string_free(CSV_str, TRUE);
+}
+
+/****************************************************************************/
+static void
+rtpstream_on_analyse(GtkButton *button _U_, gpointer user_data _U_)
+{
+ address ip_src_fwd;
+ guint16 port_src_fwd = 0;
+ address ip_dst_fwd;
+ guint16 port_dst_fwd = 0;
+ guint32 ssrc_fwd = 0;
+ address ip_src_rev;
+ guint16 port_src_rev = 0;
+ address ip_dst_rev;
+ guint16 port_dst_rev = 0;
+ guint32 ssrc_rev = 0;
+
+ if (!(selected_stream_fwd || selected_stream_rev))
+ {
+ return;
+ }
+
+ SET_ADDRESS(&ip_src_fwd,AT_NONE,0,NULL);
+ SET_ADDRESS(&ip_dst_fwd,AT_NONE,0,NULL);
+ SET_ADDRESS(&ip_src_rev,AT_NONE,0,NULL);
+ SET_ADDRESS(&ip_dst_rev,AT_NONE,0,NULL);
+
+ if (selected_stream_fwd) {
+ COPY_ADDRESS(&(ip_src_fwd), &(selected_stream_fwd->src_addr));
+ port_src_fwd = selected_stream_fwd->src_port;
+ COPY_ADDRESS(&(ip_dst_fwd), &(selected_stream_fwd->dest_addr));
+ port_dst_fwd = selected_stream_fwd->dest_port;
+ ssrc_fwd = selected_stream_fwd->ssrc;
+ }
+
+ if (selected_stream_rev) {
+ COPY_ADDRESS(&(ip_src_rev), &(selected_stream_rev->src_addr));
+ port_src_rev = selected_stream_rev->src_port;
+ COPY_ADDRESS(&(ip_dst_rev), &(selected_stream_rev->dest_addr));
+ port_dst_rev = selected_stream_rev->dest_port;
+ ssrc_rev = selected_stream_rev->ssrc;
+ }
+
+ rtp_analysis(
+ &ip_src_fwd,
+ port_src_fwd,
+ &ip_dst_fwd,
+ port_dst_fwd,
+ ssrc_fwd,
+ &ip_src_rev,
+ port_src_rev,
+ &ip_dst_rev,
+ port_dst_rev,
+ ssrc_rev
+ );
+
+}
+
+
+/****************************************************************************/
+/* when the user selects a row in the stream list */
+static gboolean
+rtpstream_view_selection_func(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean path_currently_selected, gpointer userdata _U_)
+{
+ GtkTreeIter iter;
+ gint nb_selected;
+ rtp_stream_info_t* selected_stream;
+ gboolean result = TRUE;
+ gchar label_text[80];
+
+ /* Logic
+ * nb_selected path_currently_selected forward reverse action result
+ * 0 must be false any any assign forward true
+ * 1 true match any delete forward true
+ * 1 true other any delete reverse true
+ * 1 false match any invalid true
+ * 1 false other none assign reverse true
+ * 1 false other any assign forward true
+ * 2 true match any delete forward path_currently_selected
+ * 2 true other match delete reverse path_currently_selected
+ * 2 true other other invalid path_currently_selected
+ * 2 false match any invalid path_currently_selected
+ * 2 false any match invalid path_currently_selected
+ * 2 false other other assign reverse path_currently_selected
+ * >2 any any any invalid path_currently_selected
+ */
+
+ nb_selected = gtk_tree_selection_count_selected_rows(selection);
+ if (gtk_tree_model_get_iter(model, &iter, path)) {
+ gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, RTP_COL_DATA, &selected_stream, -1);
+
+ switch (nb_selected)
+ {
+ case 0:
+ {
+ if (path_currently_selected)
+ g_print("Select: He, we've got a selected path while none is selected?\n");
+ else
+ selected_stream_fwd = selected_stream;
+ break;
+ }
+ case 1:
+ {
+ if (path_currently_selected)
+ if (selected_stream == selected_stream_fwd)
+ selected_stream_fwd = NULL;
+ else
+ selected_stream_rev = NULL;
+ else
+ if (selected_stream == selected_stream_fwd)
+ g_print("Select: He, this can't be. 1 not selected but equal to fwd\n");
+ else
+ if (selected_stream_rev)
+ selected_stream_fwd = selected_stream;
+ else
+ selected_stream_rev = selected_stream;
+ break;
+ }
+ case 2:
+ {
+ if (path_currently_selected) {
+ if (selected_stream == selected_stream_fwd)
+ selected_stream_fwd = NULL;
+ else if (selected_stream == selected_stream_rev)
+ selected_stream_rev = NULL;
+ else
+ g_print("Select: He, this can't be. 2 selected but not equal to fwd or rev\n");
+ }
+ result = path_currently_selected;
+ break;
+ }
+ default:
+ {
+ g_print("Select: He, we're getting a too high selection count\n");
+ result = path_currently_selected;
+ }
+ }
+ }
+
+ if (selected_stream_fwd) {
+ g_snprintf(label_text, sizeof(label_text), "Forward: %s:%u -> %s:%u, SSRC=0x%X",
+ get_addr_name(&(selected_stream_fwd->src_addr)),
+ selected_stream_fwd->src_port,
+ get_addr_name(&(selected_stream_fwd->dest_addr)),
+ selected_stream_fwd->dest_port,
+ selected_stream_fwd->ssrc
+ );
+ gtk_label_set_text(GTK_LABEL(label_fwd), label_text);
+ } else {
+ if (selected_stream_rev)
+ gtk_label_set_text(GTK_LABEL(label_fwd), FWD_ONLY_LABEL_TEXT);
+ else
+ gtk_label_set_text(GTK_LABEL(label_fwd), FWD_LABEL_TEXT);
+ }
+
+ if (selected_stream_rev) {
+ g_snprintf(label_text, sizeof(label_text), "Reverse: %s:%u -> %s:%u, SSRC=0x%X",
+ get_addr_name(&(selected_stream_rev->src_addr)),
+ selected_stream_rev->src_port,
+ get_addr_name(&(selected_stream_rev->dest_addr)),
+ selected_stream_rev->dest_port,
+ selected_stream_rev->ssrc
+ );
+ gtk_label_set_text(GTK_LABEL(label_rev), label_text);
+ } else {
+ gtk_label_set_text(GTK_LABEL(label_rev), REV_LABEL_TEXT);
+ }
+
+ return result;
+}
+
+/****************************************************************************/
+/* INTERFACE */
+/****************************************************************************/
+/* append a line to list */
+static void
+add_to_list_store(rtp_stream_info_t* strinfo)
+{
+ gchar label_text[256];
+ gchar *data[NUM_COLS];
+ guint32 expected;
+ gint32 lost;
+ double perc;
+ int i;
+ char *savelocale;
+
+ /* save the current locale */
+ savelocale = setlocale(LC_NUMERIC, NULL);
+ /* switch to "C" locale to avoid problems with localized decimal separators
+ in g_snprintf("%f") functions */
+ setlocale(LC_NUMERIC, "C");
+
+ data[0] = g_strdup(get_addr_name(&(strinfo->src_addr)));
+ data[1] = NULL;
+ data[2] = g_strdup(get_addr_name(&(strinfo->dest_addr)));
+ data[3] = NULL;
+ data[4] = g_strdup_printf("0x%X", strinfo->ssrc);
+ if ((strinfo->pt > 95) && (strinfo->info_payload_type_str != NULL)) {
+ data[5] = g_strdup(strinfo->info_payload_type_str);
+ } else {
+ data[5] = g_strdup(val_to_str_ext(strinfo->pt, &rtp_payload_type_short_vals_ext,
+ "Unknown (%u)"));
+ }
+ data[6] = NULL;
+
+ expected = (strinfo->rtp_stats.stop_seq_nr + strinfo->rtp_stats.cycles*65536)
+ - strinfo->rtp_stats.start_seq_nr + 1;
+ lost = expected - strinfo->rtp_stats.total_nr;
+ if (expected) {
+ perc = (double)(lost*100)/(double)expected;
+ } else {
+ perc = 0;
+ }
+ data[7] = g_strdup_printf("%d (%.1f%%)", lost, perc);
+ data[8] = g_strdup_printf("%.2f", strinfo->rtp_stats.max_delta);
+ data[9] = g_strdup_printf("%.2f", strinfo->rtp_stats.max_jitter);
+ data[10] = g_strdup_printf("%.2f", strinfo->rtp_stats.mean_jitter);
+ if (strinfo->problem)
+ data[11] = g_strdup("X");
+ else
+ data[11] = g_strdup("");
+
+ /* restore previous locale setting */
+ setlocale(LC_NUMERIC, savelocale);
+
+ /* Acquire an iterator */
+ gtk_list_store_append(list_store, &list_iter);
+
+ /* Fill the new row */
+ gtk_list_store_set(list_store, &list_iter,
+ RTP_COL_SRC_ADDR, data[0],
+ RTP_COL_SRC_PORT, strinfo->src_port,
+ RTP_COL_DST_ADDR, data[2],
+ RTP_COL_DST_PORT, strinfo->dest_port,
+ RTP_COL_SSRC, data[4],
+ RTP_COL_PAYLOAD, data[5],
+ RTP_COL_PACKETS, strinfo->npackets,
+ RTP_COL_LOST, data[7],
+ RTP_COL_MAX_DELTA, data[8],
+ RTP_COL_MAX_JITTER, data[9],
+ RTP_COL_MEAN_JITTER, data[10],
+ RTP_COL_PROBLEM, data[11],
+ RTP_COL_DATA, strinfo,
+ -1);
+
+ for (i = 0; i < NUM_COLS-1; i++)
+ g_free(data[i]);
+
+ /* Update the top label with the number of detected streams */
+ g_snprintf(label_text, sizeof(label_text),
+ "Detected %d RTP streams. Choose one for forward and reverse direction for analysis",
+ ++streams_nb);
+ gtk_label_set_text(GTK_LABEL(top_label), label_text);
+}
+
+/****************************************************************************/
+/* Create list view */
+static void
+create_list_view(void)
+{
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSortable *sortable;
+ GtkTreeView *list_view;
+ GtkTreeSelection *selection;
+
+ /* Create the store */
+ list_store = gtk_list_store_new(NUM_COLS, /* Total number of columns */
+ G_TYPE_STRING, /* Source address */
+ G_TYPE_UINT, /* Source port */
+ G_TYPE_STRING, /* Destination address */
+ G_TYPE_UINT, /* Destination port */
+ G_TYPE_STRING, /* SSRC */
+ G_TYPE_STRING, /* Payload */
+ G_TYPE_UINT, /* Packets */
+ G_TYPE_STRING, /* Lost */
+ G_TYPE_STRING, /* Max. delta */
+ G_TYPE_STRING, /* Max. jitter */
+ G_TYPE_STRING, /* Mean jitter */
+ G_TYPE_STRING, /* Problem */
+ G_TYPE_POINTER /* Data */
+ );
+
+ /* Create a view */
+ list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store));
+
+ list_view = GTK_TREE_VIEW(list);
+ sortable = GTK_TREE_SORTABLE(list_store);
+
+ /* Speed up the list display */
+ gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
+
+ /* Setup the sortable columns */
+ gtk_tree_sortable_set_sort_column_id(sortable, RTP_COL_SRC_ADDR, GTK_SORT_ASCENDING);
+ gtk_tree_view_set_headers_clickable(list_view, FALSE);
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref(G_OBJECT(list_store));
+
+ /*
+ * Create the first column packet, associating the "text" attribute of the
+ * cell_renderer to the first column of the model
+ */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Src IP addr", renderer,
+ "text", RTP_COL_SRC_ADDR,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, RTP_COL_SRC_ADDR);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+ gtk_tree_view_column_set_fixed_width(column, 100);
+ /* Add the column to the view. */
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Source port */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Src port", renderer,
+ "text", RTP_COL_SRC_PORT,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, RTP_COL_SRC_PORT);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+ gtk_tree_view_column_set_fixed_width(column, 80);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Destination address */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Dst IP addr", renderer,
+ "text", RTP_COL_DST_ADDR,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, RTP_COL_DST_ADDR);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+ gtk_tree_view_column_set_fixed_width(column, 100);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Destination port */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Dst port", renderer,
+ "text", RTP_COL_DST_PORT,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, RTP_COL_DST_PORT);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+ gtk_tree_view_column_set_fixed_width(column, 80);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* SSRC */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("SSRC", renderer,
+ "text", RTP_COL_SSRC,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, RTP_COL_SSRC);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 70);
+ gtk_tree_view_column_set_fixed_width(column, 90);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Payload */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Payload", renderer,
+ "text", RTP_COL_PAYLOAD,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, RTP_COL_PAYLOAD);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_column_set_fixed_width(column, 100);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Packets */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Packets", renderer,
+ "text", RTP_COL_PACKETS,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, RTP_COL_PACKETS);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+ gtk_tree_view_column_set_fixed_width(column, 70);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Lost */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Lost", renderer,
+ "text", RTP_COL_LOST,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, RTP_COL_LOST);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+ gtk_tree_view_column_set_fixed_width(column, 90);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Max Delta (ms) */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Max Delta (ms)", renderer,
+ "text", RTP_COL_MAX_DELTA,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, RTP_COL_MAX_DELTA);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 90);
+ gtk_tree_view_column_set_fixed_width(column, 130);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Max Jitter (ms) */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Max Jitter (ms)", renderer,
+ "text", RTP_COL_MAX_JITTER,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, RTP_COL_MAX_JITTER);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 50);
+ gtk_tree_view_column_set_fixed_width(column, 120);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Mean Jitter (ms) */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Mean Jitter (ms)", renderer,
+ "text", RTP_COL_MEAN_JITTER,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, RTP_COL_MEAN_JITTER);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 50);
+ gtk_tree_view_column_set_fixed_width(column, 130);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Problems? */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Pb?", renderer,
+ "text", RTP_COL_PROBLEM,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, RTP_COL_PROBLEM);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 30);
+ gtk_tree_view_column_set_fixed_width(column, 50);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Now enable the sorting of each column */
+ gtk_tree_view_set_rules_hint(list_view, TRUE);
+ gtk_tree_view_set_headers_clickable(list_view, TRUE);
+
+ /* Setup the selection handler */
+ selection = gtk_tree_view_get_selection(list_view);
+
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
+ gtk_tree_selection_set_select_function(selection, rtpstream_view_selection_func, NULL, NULL);
+}
+
+
+/****************************************************************************/
+/* Create dialog */
+static void
+rtpstream_dlg_create (void)
+{
+ GtkWidget *rtpstream_dlg_w;
+ GtkWidget *main_vb;
+ GtkWidget *scrolledwindow;
+ GtkWidget *hbuttonbox;
+/* GtkWidget *bt_goto;*/
+ GtkWidget *bt_unselect;
+ GtkWidget *bt_findrev;
+ GtkWidget *bt_save;
+ GtkWidget *bt_mark;
+ GtkWidget *bt_filter;
+ GtkWidget *bt_analyze;
+ GtkWidget *bt_close;
+ GtkWidget *bt_copy;
+
+ rtpstream_dlg_w = dlg_window_new("Wireshark: RTP Streams");
+ gtk_window_set_default_size(GTK_WINDOW(rtpstream_dlg_w), 620, 400);
+
+ main_vb = gtk_vbox_new (FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(rtpstream_dlg_w), main_vb);
+ gtk_container_set_border_width (GTK_CONTAINER (main_vb), 12);
+
+ top_label = gtk_label_new ("Detected 0 RTP streams. Choose one for forward and reverse direction for analysis");
+ gtk_box_pack_start (GTK_BOX (main_vb), top_label, FALSE, FALSE, 8);
+
+ scrolledwindow = scrolled_window_new (NULL, NULL);
+ gtk_box_pack_start (GTK_BOX (main_vb), scrolledwindow, TRUE, TRUE, 0);
+
+ create_list_view();
+ gtk_container_add(GTK_CONTAINER(scrolledwindow), list);
+
+ gtk_widget_show(rtpstream_dlg_w);
+
+ label_fwd = gtk_label_new (FWD_LABEL_TEXT);
+ gtk_box_pack_start (GTK_BOX (main_vb), label_fwd, FALSE, FALSE, 0);
+
+ label_rev = gtk_label_new (REV_LABEL_TEXT);
+ gtk_box_pack_start (GTK_BOX (main_vb), label_rev, FALSE, FALSE, 0);
+
+ /* button row */
+ hbuttonbox = gtk_hbutton_box_new ();
+ gtk_box_pack_start (GTK_BOX (main_vb), hbuttonbox, FALSE, FALSE, 0);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_END);
+ gtk_box_set_spacing (GTK_BOX (hbuttonbox), 0);
+
+ bt_unselect = gtk_button_new_with_label ("Unselect");
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_unselect);
+ gtk_widget_set_tooltip_text (bt_unselect, "Undo stream selection");
+
+ bt_findrev = gtk_button_new_with_label ("Find Reverse");
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_findrev);
+ gtk_widget_set_tooltip_text (bt_findrev, "Find the reverse stream matching the selected forward stream");
+/*
+ bt_goto = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_goto);
+*/
+ bt_save = gtk_button_new_from_stock(GTK_STOCK_SAVE_AS);
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_save);
+ gtk_widget_set_tooltip_text (bt_save, "Save stream payload in rtpdump format");
+
+ bt_mark = gtk_button_new_with_label ("Mark Packets");
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_mark);
+ gtk_widget_set_tooltip_text (bt_mark, "Mark packets of the selected stream(s)");
+
+ bt_filter = gtk_button_new_from_stock(WIRESHARK_STOCK_PREPARE_FILTER);
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_filter);
+ gtk_widget_set_tooltip_text (bt_filter, "Prepare a display filter of the selected stream(s)");
+
+ /* XXX - maybe we want to have a "Copy as CSV" stock button here? */
+ /*bt_copy = gtk_button_new_with_label ("Copy content to clipboard as CSV");*/
+ bt_copy = gtk_button_new_from_stock(GTK_STOCK_COPY);
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_copy);
+ gtk_widget_set_tooltip_text(bt_copy,
+ "Copy all statistical values of this page to the clipboard in CSV (Comma Separated Values) format.");
+
+ bt_analyze = gtk_button_new_from_stock(WIRESHARK_STOCK_ANALYZE);
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_analyze);
+ gtk_widget_set_tooltip_text (bt_analyze, "Open an analyze window of the selected stream(s)");
+
+ bt_close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_close);
+ gtk_widget_set_tooltip_text (bt_close, "Close this dialog");
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(bt_close, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(bt_close, GTK_CAN_DEFAULT);
+#endif
+
+ g_signal_connect(bt_unselect, "clicked", G_CALLBACK(rtpstream_on_unselect), NULL);
+ g_signal_connect(bt_findrev, "clicked", G_CALLBACK(rtpstream_on_findrev), NULL);
+/*
+ g_signal_connect(bt_goto, "clicked", G_CALLBACK(rtpstream_on_goto), NULL);
+*/
+ g_signal_connect(bt_save, "clicked", G_CALLBACK(rtpstream_on_save), NULL);
+ g_signal_connect(bt_mark, "clicked", G_CALLBACK(rtpstream_on_mark), NULL);
+ g_signal_connect(bt_filter, "clicked", G_CALLBACK(rtpstream_on_filter), NULL);
+ g_signal_connect(bt_copy, "clicked", G_CALLBACK(rtpstream_on_copy_as_csv), NULL);
+ g_signal_connect(bt_analyze, "clicked", G_CALLBACK(rtpstream_on_analyse), NULL);
+
+ window_set_cancel_button(rtpstream_dlg_w, bt_close, window_cancel_button_cb);
+
+ g_signal_connect(rtpstream_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(rtpstream_dlg_w, "destroy", G_CALLBACK(rtpstream_on_destroy), NULL);
+
+ gtk_widget_show_all(rtpstream_dlg_w);
+ window_present(rtpstream_dlg_w);
+
+ rtpstream_on_unselect(NULL, NULL);
+
+ rtp_stream_dlg = rtpstream_dlg_w;
+}
+
+
+/****************************************************************************/
+/* PUBLIC */
+/****************************************************************************/
+
+/****************************************************************************/
+/* update the contents of the dialog box list_store */
+/* list: pointer to list of rtp_stream_info_t* */
+void rtpstream_dlg_update(GList *list_lcl)
+{
+ if (rtp_stream_dlg != NULL) {
+ gtk_list_store_clear(list_store);
+ streams_nb = 0;
+
+ list_lcl = g_list_first(list_lcl);
+ while (list_lcl)
+ {
+ add_to_list_store((rtp_stream_info_t*)(list_lcl->data));
+ list_lcl = g_list_next(list_lcl);
+ }
+
+ rtpstream_on_unselect(NULL, NULL);
+ }
+
+ last_list = list_lcl;
+}
+
+
+/****************************************************************************/
+/* update the contents of the dialog box list_store */
+/* list: pointer to list of rtp_stream_info_t* */
+void rtpstream_dlg_show(GList *list_lcl)
+{
+ if (rtp_stream_dlg != NULL) {
+ /* There's already a dialog box; reactivate it. */
+ reactivate_window(rtp_stream_dlg);
+ /* Another list since last call? */
+ if (list_lcl != last_list) {
+ rtpstream_dlg_update(list_lcl);
+ }
+ }
+ else {
+ /* Create and show the dialog box */
+ rtpstream_dlg_create();
+ rtpstream_dlg_update(list_lcl);
+ }
+}
+
+
+/****************************************************************************/
+/* entry point when called via the GTK menu */
+void rtpstream_launch(GtkAction *action _U_, gpointer user_data _U_)
+{
+ /* Register the tap listener */
+ register_tap_listener_rtp_stream();
+
+ /* Scan for RTP streams (redissect all packets) */
+ rtpstream_scan();
+
+ /* Show the dialog box with the list of streams */
+ rtpstream_dlg_show(rtpstream_get_info()->strinfo_list);
+
+ /* Tap listener will be removed and cleaned up in rtpstream_on_destroy */
+}
+
+/****************************************************************************/
+void
+register_tap_listener_rtp_stream_dlg(void)
+{
+}
+
diff --git a/ui/gtk/rtp_stream_dlg.h b/ui/gtk/rtp_stream_dlg.h
new file mode 100644
index 0000000000..a459b4ed02
--- /dev/null
+++ b/ui/gtk/rtp_stream_dlg.h
@@ -0,0 +1,52 @@
+/* rtp_stream_dlg.h
+ * RTP streams summary addition for Wireshark
+ *
+ * $Id$
+ *
+ * Copyright 2003, Alcatel Business Systems
+ * By Lars Ruoff <lars.ruoff@gmx.net>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __RTP_STREAM_DLG_H__
+#define __RTP_STREAM_DLG_H__
+
+#include <gtk/gtk.h>
+
+/** @file
+ * "RTP Stream Analysis" dialog box.
+ * @ingroup dialog_group
+ */
+
+/**
+ * Create or reactivate the rtp streams dialog box.
+ *
+ * @param list pointer to list of rtp_stream_info_t*
+ */
+void rtpstream_dlg_show(GList *list);
+
+/**
+ * Update the contents of the dialog box clist with that of list.
+ *
+ * @param list pointer to list of rtp_stream_info_t*
+ */
+void rtpstream_dlg_update(GList *list);
+
+#endif /* __RTP_STREAM_DLG_H__ */
diff --git a/ui/gtk/sat.h b/ui/gtk/sat.h
new file mode 100644
index 0000000000..c0cb1a08bc
--- /dev/null
+++ b/ui/gtk/sat.h
@@ -0,0 +1,43 @@
+/* sat.h
+ * 2003 Ronnie Sahlberg
+ * Sub-address types for MAC/URI addresses
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_SAT_H__
+#define __GTK_SAT_H__
+
+/** @file
+ * Sub-address type definitions.
+ */
+
+/** Address type */
+typedef enum {
+ SAT_NONE, /**< no address type */
+ SAT_ETHER, /**< MAC : Ethernet */
+ SAT_WLAN, /**< MAC : Wireless LAN */
+ SAT_FDDI, /**< MAC : FDDI */
+ SAT_TOKENRING, /**< MAC : Token Ring */
+ SAT_JXTA /**< URI : JXTA */
+} SAT_E;
+
+#endif /* __GTK_SAT_H__ */
diff --git a/ui/gtk/scsi_stat.c b/ui/gtk/scsi_stat.c
new file mode 100644
index 0000000000..4e4fe8658a
--- /dev/null
+++ b/ui/gtk/scsi_stat.c
@@ -0,0 +1,318 @@
+/* scsi_stat.c
+ * scsi_stat 2006 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* This module provides rpc call/reply SRT (Server Response Time) statistics
+ * to Wireshark.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/tap.h>
+#include <epan/conversation.h>
+#include <epan/dissectors/packet-scsi.h>
+#include <epan/dissectors/packet-fc.h>
+#include <epan/dissectors/packet-scsi-sbc.h>
+#include <epan/dissectors/packet-scsi-ssc.h>
+#include <epan/dissectors/packet-scsi-smc.h>
+#include <epan/dissectors/packet-scsi-osd.h>
+
+#include "../simple_dialog.h"
+#include "../globals.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/service_response_time_table.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/gtkglobals.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+/* used to keep track of the statistics for an entire scsi command set */
+typedef struct _scsistat_t {
+ GtkWidget *win;
+ srt_stat_table srt_table;
+ guint8 cmdset;
+ const value_string *cdbnames;
+ const char *prog;
+} scsistat_t;
+
+static guint8 scsi_program=0;
+
+enum
+{
+ SCSI_STAT_PROG_LABEL_SBC,
+ SCSI_STAT_PROG_LABEL_SSC,
+ SCSI_STAT_PROG_LABEL_MMC
+};
+
+
+static char *
+scsistat_gen_title(scsistat_t *rs)
+{
+ char *title;
+
+ title = g_strdup_printf("SCSI Service Response Time statistics for %s: %s",
+ rs->prog, cf_get_display_name(&cfile));
+ return title;
+}
+
+static void
+scsistat_set_title(scsistat_t *rs)
+{
+ char *title;
+
+ title = scsistat_gen_title(rs);
+ gtk_window_set_title(GTK_WINDOW(rs->win), title);
+ g_free(title);
+}
+
+static void
+scsistat_reset(void *arg)
+{
+ scsistat_t *rs = (scsistat_t *)arg;
+
+ reset_srt_table_data(&rs->srt_table);
+ scsistat_set_title(rs);
+}
+
+
+static int
+scsistat_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *arg2)
+{
+ scsistat_t *rs = (scsistat_t *)arg;
+ const scsi_task_data_t *ri = (const scsi_task_data_t *)arg2;
+
+ /* we are only interested in response packets */
+ if(ri->type!=SCSI_PDU_TYPE_RSP){
+ return 0;
+ }
+ /* we are only interested in a specific commandset */
+ if( (!ri->itl) || ((ri->itl->cmdset&SCSI_CMDSET_MASK)!=rs->cmdset) ){
+ return 0;
+ }
+ /* check that the opcode looks sane */
+ if( (!ri->itlq) || (ri->itlq->scsi_opcode>255) ){
+ return 0;
+ }
+
+ add_srt_table_data(&rs->srt_table, ri->itlq->scsi_opcode, &ri->itlq->fc_time, pinfo);
+
+ return 1;
+}
+
+static void
+scsistat_draw(void *arg)
+{
+ scsistat_t *rs = (scsistat_t *)arg;
+
+ draw_srt_table_data(&rs->srt_table);
+}
+
+
+
+/* since the gtk2 implementation of tap is multithreaded we must protect
+ * remove_tap_listener() from modifying the list while draw_tap_listener()
+ * is running. the other protected block is in main.c
+ *
+ * there should not be any other critical regions in gtk2
+ */
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ scsistat_t *rs=(scsistat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(rs);
+ unprotect_thread_critical_region();
+
+ free_srt_table_data(&rs->srt_table);
+ g_free(rs);
+}
+
+
+/* When called, this function will create a new instance of gtk2-scsistat.
+ */
+static void
+gtk_scsistat_init(const char *optarg, void* userdata _U_)
+{
+ scsistat_t *rs;
+ guint32 i;
+ char *title_string;
+ char *filter_string;
+ GtkWidget *vbox;
+ GtkWidget *stat_label;
+ GtkWidget *filter_label;
+ GtkWidget *bbox;
+ GtkWidget *close_bt;
+ int program, pos;
+ const char *filter=NULL;
+ GString *error_string;
+ const char *hf_name=NULL;
+
+ pos=0;
+ if(sscanf(optarg,"scsi,srt,%d,%n",&program,&pos)==1){
+ if(pos){
+ filter=optarg+pos;
+ } else {
+ filter=NULL;
+ }
+ } else {
+ fprintf(stderr, "wireshark: invalid \"-z scsi,srt,<cmdset>[,<filter>]\" argument\n");
+ exit(1);
+ }
+
+ scsi_program=program;
+ rs=(scsistat_t *)g_malloc(sizeof(scsistat_t));
+ rs->cmdset=program;
+ switch(program){
+ case SCSI_DEV_SBC:
+ rs->prog="SBC (disk)";
+ rs->cdbnames=scsi_sbc_vals;
+ hf_name="scsi.sbc.opcode";
+ break;
+ case SCSI_DEV_SSC:
+ rs->prog="SSC (tape)";
+ rs->cdbnames=scsi_ssc_vals;
+ hf_name="scsi.ssc.opcode";
+ break;
+ case SCSI_DEV_CDROM:
+ rs->prog="MMC (cd/dvd)";
+ rs->cdbnames=scsi_mmc_vals;
+ hf_name="scsi.mmc.opcode";
+ break;
+ case SCSI_DEV_SMC:
+ rs->prog="SMC (tape robot)";
+ rs->cdbnames=scsi_smc_vals;
+ hf_name="scsi.smc.opcode";
+ break;
+ case SCSI_DEV_OSD:
+ rs->prog="OSD (object based)";
+ rs->cdbnames=scsi_osd_vals;
+ hf_name="scsi.osd.opcode";
+ break;
+ }
+
+ rs->win = dlg_window_new("scsi-stat"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(rs->win), TRUE);
+ gtk_window_set_default_size(GTK_WINDOW(rs->win), 550, 400);
+ scsistat_set_title(rs);
+
+ vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(rs->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ title_string = scsistat_gen_title(rs);
+ stat_label=gtk_label_new(title_string);
+ g_free(title_string);
+ gtk_box_pack_start(GTK_BOX(vbox), stat_label, FALSE, FALSE, 0);
+
+ filter_string = g_strdup_printf("Filter: %s", filter ? filter : "");
+ filter_label=gtk_label_new(filter_string);
+ g_free(filter_string);
+ gtk_label_set_line_wrap(GTK_LABEL(filter_label), TRUE);
+ gtk_box_pack_start(GTK_BOX(vbox), filter_label, FALSE, FALSE, 0);
+
+ /* We must display TOP LEVEL Widget before calling init_srt_table() */
+ gtk_widget_show_all(rs->win);
+
+ init_srt_table(&rs->srt_table, 256, vbox, hf_name);
+
+ for(i=0;i<256;i++){
+ init_srt_table_row(&rs->srt_table, i, val_to_str(i, rs->cdbnames, "Unknown-0x%02x"));
+ }
+
+
+ error_string=register_tap_listener("scsi", rs, filter, 0, scsistat_reset, scsistat_packet, scsistat_draw);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ free_srt_table_data(&rs->srt_table);
+ g_free(rs);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(rs->win, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(rs->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(rs->win, "destroy", G_CALLBACK(win_destroy_cb), rs);
+
+ gtk_widget_show_all(rs->win);
+ window_present(rs->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(rs->win));
+}
+
+static enum_val_t scsi_command_sets[] = {
+ { "sbc", "SBC (disk)", SCSI_DEV_SBC },
+ { "ssc", "SSC (tape)", SCSI_DEV_SSC },
+ { "mmc", "MMC (cd/dvd)", SCSI_DEV_CDROM },
+ { "smc", "SMC (tape robot)", SCSI_DEV_SMC },
+ { "osd", "OSD (object based)", SCSI_DEV_OSD },
+ { NULL, NULL, 0 }
+};
+
+static tap_param scsi_stat_params[] = {
+ { PARAM_ENUM, "Command set", scsi_command_sets },
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg scsi_stat_dlg = {
+ "SCSI SRT Statistics",
+ "scsi,srt",
+ gtk_scsistat_init,
+ -1,
+ G_N_ELEMENTS(scsi_stat_params),
+ scsi_stat_params
+};
+
+void
+register_tap_listener_gtkscsistat(void)
+{
+ register_dfilter_stat(&scsi_stat_dlg, "SCSI",
+ REGISTER_STAT_GROUP_RESPONSE_TIME);
+}
+
+void scsi_srt_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &scsi_stat_dlg);
+}
+
diff --git a/ui/gtk/sctp_assoc_analyse.c b/ui/gtk/sctp_assoc_analyse.c
new file mode 100644
index 0000000000..27e1352294
--- /dev/null
+++ b/ui/gtk/sctp_assoc_analyse.c
@@ -0,0 +1,984 @@
+/*
+ * Copyright 2004, Irene Ruengeler <i.ruengeler [AT] fh-muenster.de>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+
+#include <epan/epan_dissect.h>
+#include "epan/filesystem.h"
+#include <epan/strutil.h>
+
+#include "../globals.h"
+#include "../simple_dialog.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/sctp_stat.h"
+#include "ui/gtk/gtkglobals.h"
+
+static sctp_assoc_info_t static_assoc;
+
+void
+decrease_childcount(struct sctp_analyse *parent)
+{
+ if (parent->num_children > 0)
+ parent->num_children--;
+}
+
+void
+increase_childcount(struct sctp_analyse *parent)
+{
+ parent->num_children++;
+}
+
+void
+set_child(struct sctp_udata *child, struct sctp_analyse *parent)
+{
+ parent->children = g_list_append(parent->children, child);
+}
+
+void
+remove_child(struct sctp_udata *child, struct sctp_analyse *parent)
+{
+ parent->children = g_list_remove(parent->children, child);
+}
+
+static void
+on_destroy(GObject *object _U_, gpointer user_data)
+{
+ struct sctp_analyse *u_data;
+ guint16 i, j;
+ GList *list;
+ struct sctp_udata *child_data;
+
+ u_data = (struct sctp_analyse*)user_data;
+
+ if (u_data->window)
+ {
+ gtk_grab_remove(GTK_WIDGET(u_data->window));
+ gtk_widget_destroy(GTK_WIDGET(u_data->window));
+ }
+ if (u_data->num_children>0)
+ {
+ j = u_data->num_children;
+ for (i=0; i<j; i++)
+ {
+ list = g_list_last(u_data->children);
+ child_data = (struct sctp_udata *)list->data;
+ gtk_grab_remove(GTK_WIDGET(child_data->io->window));
+ gtk_widget_destroy(GTK_WIDGET(child_data->io->window));
+ list = g_list_previous(list);
+ }
+ g_list_free(u_data->children);
+ u_data->children = NULL;
+ }
+
+ g_free(u_data->analyse_nb->page2);
+ g_free(u_data->analyse_nb->page3);
+ g_free(u_data->analyse_nb);
+ if (sctp_stat_get_info()->children != NULL)
+ {
+ remove_analyse_child(u_data);
+ decrease_analyse_childcount();
+ }
+ g_free(u_data);
+}
+
+static void
+on_notebook_switch_page(void)
+{
+}
+
+static void on_chunk_stat_bt(GtkWidget *widget _U_, struct sctp_analyse* u_data)
+{
+ sctp_assoc_info_t* assinfo = NULL;
+ int i;
+
+ assinfo = &static_assoc;
+ assinfo->addr_chunk_count = (static_assoc.addr_chunk_count);
+ for (i=0; i<NUM_CHUNKS; i++)
+ {
+ assinfo->chunk_count[i] = static_assoc.chunk_count[i];
+ assinfo->ep1_chunk_count[i] = static_assoc.ep1_chunk_count[i];
+ assinfo->ep2_chunk_count[i] = static_assoc.ep2_chunk_count[i];
+ }
+ u_data->assoc = assinfo;
+ sctp_chunk_dlg_show(u_data);
+}
+
+static void on_close_dlg(GtkWidget *widget _U_, struct sctp_analyse* u_data)
+{
+
+ if (u_data->window)
+ {
+ gtk_grab_remove(GTK_WIDGET(u_data->window));
+ gtk_widget_destroy(GTK_WIDGET(u_data->window));
+ }
+}
+
+static void on_chunk1_dlg(GtkWidget *widget _U_, struct sctp_analyse* u_data)
+{
+sctp_assoc_info_t* assinfo = NULL;
+
+ assinfo = &static_assoc;
+ assinfo->addr_chunk_count = (static_assoc.addr_chunk_count);
+ u_data->assoc = assinfo;
+ sctp_chunk_stat_dlg_show(1, u_data);
+}
+
+static void on_chunk2_dlg(GtkWidget *widget _U_, struct sctp_analyse* u_data)
+{
+sctp_assoc_info_t* assinfo=NULL;
+
+ assinfo = &static_assoc;
+ assinfo->addr_chunk_count = (static_assoc.addr_chunk_count);
+ u_data->assoc = assinfo;
+ sctp_chunk_stat_dlg_show(2, u_data);
+}
+
+static void on_graph1_dlg(GtkWidget *widget _U_, struct sctp_analyse* u_data)
+{
+ create_graph(1, u_data);
+}
+
+static void on_graph2_dlg(GtkWidget *widget _U_, struct sctp_analyse* u_data)
+{
+ create_graph(2, u_data);
+}
+
+static void on_graph_byte1_dlg(GtkWidget *widget _U_, struct sctp_analyse* u_data)
+{
+ create_byte_graph(1, u_data);
+}
+
+static void on_graph_byte2_dlg(GtkWidget *widget _U_, struct sctp_analyse* u_data)
+{
+ create_byte_graph(2, u_data);
+}
+
+void
+update_analyse_dlg(struct sctp_analyse* u_data)
+{
+ gchar label_txt[50];
+ gchar field[1][MAX_ADDRESS_LEN];
+ GList *list;
+ address *store = NULL;
+ GtkListStore *list_store = NULL;
+ GtkTreeIter iter;
+
+ if (u_data->assoc == NULL)
+ return;
+
+ if (u_data->window != NULL)
+ {
+ gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW
+ (u_data->analyse_nb->page2->clist))));
+ gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW
+ (u_data->analyse_nb->page3->clist))));
+ }
+
+
+ g_snprintf(label_txt, sizeof(label_txt),"Checksum Type: %s",u_data->assoc->checksum_type);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->checktype), label_txt);
+ g_snprintf(label_txt, sizeof(label_txt),"Checksum Errors: %u",u_data->assoc->n_checksum_errors);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->checksum), label_txt);
+ g_snprintf(label_txt, sizeof(label_txt),"Bundling Errors: %u",u_data->assoc->n_bundling_errors);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->bundling), label_txt);
+ g_snprintf(label_txt, sizeof(label_txt),"Padding Errors: %u",u_data->assoc->n_padding_errors);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->padding), label_txt);
+ g_snprintf(label_txt, sizeof(label_txt),"Length Errors: %u",u_data->assoc->n_length_errors);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->length), label_txt);
+ g_snprintf(label_txt, sizeof(label_txt),"Value Errors: %u",u_data->assoc->n_value_errors);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->value), label_txt);
+ g_snprintf(label_txt, sizeof(label_txt),"No of Data Chunks from EP1 to EP2: %u",u_data->assoc->n_data_chunks_ep1);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->chunks_ep1), label_txt);
+ g_snprintf(label_txt, sizeof(label_txt),"No of Data Bytes from EP1 to EP2: %u",u_data->assoc->n_data_bytes_ep1);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->bytes_ep1), label_txt);
+ g_snprintf(label_txt, sizeof(label_txt),"No of Data Chunks from EP2 to EP1: %u",u_data->assoc->n_data_chunks_ep2);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->chunks_ep2), label_txt);
+ g_snprintf(label_txt, sizeof(label_txt),"No of Data Bytes from EP2 to EP1: %u",u_data->assoc->n_data_bytes_ep2);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->bytes_ep2), label_txt);
+
+ if (u_data->assoc->init == TRUE)
+ gtk_frame_set_label(GTK_FRAME (u_data->analyse_nb->page2->addr_frame), "Complete list of IP-Addresses as provided in the INIT-Chunk");
+ else if (u_data->assoc->initack == TRUE && u_data->assoc->initack_dir == 1)
+ gtk_frame_set_label(GTK_FRAME (u_data->analyse_nb->page2->addr_frame), "Complete list of IP-Addresses as provided in the INITACK-Chunk");
+ else
+ gtk_frame_set_label(GTK_FRAME (u_data->analyse_nb->page2->addr_frame), "List of used IP-Addresses");
+
+
+
+ if (u_data->assoc->addr1 != NULL)
+ {
+ list = g_list_first(u_data->assoc->addr1);
+ while (list)
+ {
+ store = (address *) (list->data);
+ if (store->type == AT_IPv4)
+ {
+ g_snprintf(field[0], 30, "%s", ip_to_str((const guint8 *)(store->data)));
+ }
+ else if (store->type == AT_IPv6)
+ {
+ g_snprintf(field[0], 40, "%s", ip6_to_str((const struct e_in6_addr *)(store->data)));
+ }
+ list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (u_data->analyse_nb->page2->clist))); /* Get store */
+
+ gtk_list_store_insert_with_values( list_store , &iter, G_MAXINT,
+ 0, field[0], -1);
+ list = g_list_next(list);
+ }
+ }
+ else
+ {
+ return;
+ }
+ g_snprintf(label_txt, sizeof(label_txt),"Port: %u",u_data->assoc->port1);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->page2->port), label_txt);
+ g_snprintf(label_txt, sizeof(label_txt),"Sent Verification Tag: 0x%x",u_data->assoc->verification_tag1);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->page2->veritag), label_txt);
+
+ if (u_data->assoc->init == TRUE || (u_data->assoc->initack == TRUE && u_data->assoc->initack_dir == 1))
+ {
+ g_snprintf(label_txt, sizeof(label_txt),"Requested Number of Inbound Streams: %u",u_data->assoc->instream1);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->page2->max_in), label_txt);
+ g_snprintf(label_txt, sizeof(label_txt),"Minimum Number of Inbound Streams: %u",((u_data->assoc->instream1>u_data->assoc->outstream2)?u_data->assoc->outstream2:u_data->assoc->instream1));
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->page2->min_in), label_txt);
+
+ g_snprintf(label_txt, sizeof(label_txt),"Provided Number of Outbound Streams: %u",u_data->assoc->outstream1);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->page2->max_out), label_txt);
+ g_snprintf(label_txt, sizeof(label_txt),"Minimum Number of Outbound Streams: %u",((u_data->assoc->outstream1>u_data->assoc->instream2)?u_data->assoc->instream2:u_data->assoc->outstream1));
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->page2->max_out), label_txt);
+ }
+ else
+ {
+ g_snprintf(label_txt, sizeof(label_txt),"Used Number of Inbound Streams: %u",u_data->assoc->instream1);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->page2->max_in), label_txt);
+ g_snprintf(label_txt, sizeof(label_txt),"Used Number of Outbound Streams: %u",u_data->assoc->outstream1);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->page2->max_out), label_txt);
+ }
+
+ if (u_data->assoc->initack == TRUE && u_data->assoc->initack_dir == 2)
+ gtk_frame_set_label(GTK_FRAME (u_data->analyse_nb->page3->addr_frame), "Complete list of IP-Addresses as provided in the INITACK-Chunk");
+ else
+ gtk_frame_set_label(GTK_FRAME (u_data->analyse_nb->page3->addr_frame), "List of used IP-Addresses");
+
+
+ if (u_data->assoc->addr2 != NULL)
+ {
+
+ list = g_list_first(u_data->assoc->addr2);
+
+ while (list)
+ {
+ store = (address *) (list->data);
+ if (store->type == AT_IPv4)
+ {
+ g_snprintf(field[0], 30, "%s", ip_to_str((const guint8 *)(store->data)));
+ }
+ else if (store->type == AT_IPv6)
+ {
+ g_snprintf(field[0], 40, "%s", ip6_to_str((const struct e_in6_addr *)(store->data)));
+ }
+ list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (u_data->analyse_nb->page3->clist))); /* Get store */
+
+ gtk_list_store_insert_with_values( list_store , &iter, G_MAXINT,
+ 0, field[0], -1);
+ list = g_list_next(list);
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ g_snprintf(label_txt, sizeof(label_txt),"Port: %u",u_data->assoc->port2);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->page3->port), label_txt);
+ g_snprintf(label_txt, sizeof(label_txt),"Sent Verification Tag: 0x%x",u_data->assoc->verification_tag2);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->page3->veritag), label_txt);
+
+ if (u_data->assoc->initack == TRUE)
+ {
+ g_snprintf(label_txt, sizeof(label_txt),"Requested Number of Inbound Streams: %u",u_data->assoc->instream2);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->page3->max_in), label_txt);
+ g_snprintf(label_txt, sizeof(label_txt),"Minimum Number of Inbound Streams: %u",((u_data->assoc->instream2>u_data->assoc->outstream1)?u_data->assoc->outstream1:u_data->assoc->instream2));
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->page3->min_in), label_txt);
+
+ g_snprintf(label_txt, sizeof(label_txt),"Provided Number of Outbound Streams: %u",u_data->assoc->outstream2);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->page3->max_out), label_txt);
+ g_snprintf(label_txt, sizeof(label_txt),"Minimum Number of Outbound Streams: %u",((u_data->assoc->outstream2>u_data->assoc->instream1)?u_data->assoc->instream1:u_data->assoc->outstream2));
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->page3->min_out), label_txt);
+ }
+ else
+ {
+ g_snprintf(label_txt, sizeof(label_txt),"Used Number of Inbound Streams: %u",u_data->assoc->instream2);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->page3->max_in), label_txt);
+ g_snprintf(label_txt, sizeof(label_txt),"Used Number of Outbound Streams: %u",u_data->assoc->outstream2);
+ gtk_label_set_text(GTK_LABEL(u_data->analyse_nb->page3->min_out), label_txt);
+ }
+}
+
+
+static void
+sctp_set_filter (GtkButton *button _U_, struct sctp_analyse* u_data)
+{
+ gchar *f_string = NULL;
+ GList *srclist, *dstlist;
+ gchar *str=NULL;
+ GString *gstring=NULL;
+ struct sockaddr_in *infosrc=NULL;
+ struct sockaddr_in *infodst=NULL;
+ sctp_assoc_info_t *selected_stream;
+ gchar *filter_string = NULL;
+ selected_stream=u_data->assoc;
+
+ if (selected_stream->check_address==FALSE)
+ {
+ f_string = g_strdup_printf("((sctp.srcport==%u && sctp.dstport==%u && ((sctp.verification_tag==0x%x && sctp.verification_tag!=0x0) || "
+ "(sctp.verification_tag==0x0 && sctp.initiate_tag==0x%x) || "
+ "(sctp.verification_tag==0x%x && (sctp.abort_t_bit==1 || sctp.shutdown_complete_t_bit==1)))) ||"
+ "(sctp.srcport==%u && sctp.dstport==%u && ((sctp.verification_tag==0x%x && sctp.verification_tag!=0x0) || "
+ "(sctp.verification_tag==0x0 && sctp.initiate_tag==0x%x) ||"
+ "(sctp.verification_tag==0x%x && (sctp.abort_t_bit==1 || sctp.shutdown_complete_t_bit==1)))))",
+ selected_stream->port1,
+ selected_stream->port2,
+ selected_stream->verification_tag1,
+ selected_stream->initiate_tag,
+ selected_stream->verification_tag2,
+ selected_stream->port2,
+ selected_stream->port1,
+ selected_stream->verification_tag2,
+ selected_stream->initiate_tag,
+ selected_stream->verification_tag1);
+ filter_string = f_string;
+ }
+ else
+ {
+ srclist = g_list_first(selected_stream->addr1);
+ infosrc=(struct sockaddr_in *) (srclist->data);
+ gstring = g_string_new(g_strdup_printf("((sctp.srcport==%u && sctp.dstport==%u && (ip.src==%s",
+ selected_stream->port1, selected_stream->port2, ip_to_str((const guint8 *)&(infosrc->sin_addr.s_addr))));
+ srclist= g_list_next(srclist);
+
+ while (srclist)
+ {
+ infosrc=(struct sockaddr_in *) (srclist->data);
+ str =g_strdup_printf("|| ip.src==%s",ip_to_str((const guint8 *)&(infosrc->sin_addr.s_addr)));
+ g_string_append(gstring, str);
+ srclist= g_list_next(srclist);
+ }
+
+ dstlist = g_list_first(selected_stream->addr2);
+ infodst=(struct sockaddr_in *) (dstlist->data);
+ str = g_strdup_printf(") && (ip.dst==%s",ip_to_str((const guint8 *)&(infodst->sin_addr.s_addr)));
+ g_string_append(gstring, str);
+ dstlist= g_list_next(dstlist);
+ while (dstlist)
+ {
+ infodst=(struct sockaddr_in *) (dstlist->data);
+ str =g_strdup_printf("|| ip.dst==%s",ip_to_str((const guint8 *)&(infodst->sin_addr.s_addr)));
+ g_string_append(gstring, str);
+ dstlist= g_list_next(dstlist);
+ }
+
+ srclist = g_list_first(selected_stream->addr1);
+ infosrc=(struct sockaddr_in *) (srclist->data);
+ str = g_strdup_printf(")) || (sctp.dstport==%u && sctp.srcport==%u && (ip.dst==%s",
+ selected_stream->port1, selected_stream->port2, ip_to_str((const guint8 *)&(infosrc->sin_addr.s_addr)));
+ g_string_append(gstring, str);
+ srclist= g_list_next(srclist);
+
+ while (srclist)
+ {
+ infosrc=(struct sockaddr_in *) (srclist->data);
+ str =g_strdup_printf("|| ip.dst==%s",ip_to_str((const guint8 *)&(infosrc->sin_addr.s_addr)));
+ g_string_append(gstring, str);
+ srclist= g_list_next(srclist);
+ }
+
+ dstlist = g_list_first(selected_stream->addr2);
+ infodst=(struct sockaddr_in *) (dstlist->data);
+ str = g_strdup_printf(") && (ip.src==%s",ip_to_str((const guint8 *)&(infodst->sin_addr.s_addr)));
+ g_string_append(gstring, str);
+ dstlist= g_list_next(dstlist);
+ while (dstlist)
+ {
+ infodst=(struct sockaddr_in *) (dstlist->data);
+ str =g_strdup_printf("|| ip.src==%s",ip_to_str((const guint8 *)&(infodst->sin_addr.s_addr)));
+ g_string_append(gstring, str);
+ dstlist= g_list_next(dstlist);
+ }
+ str = g_strdup_printf(")))");
+ g_string_append(gstring, str);
+ filter_string = gstring->str;
+ g_string_free(gstring,FALSE);
+ }
+
+ if (filter_string != NULL) {
+ gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), filter_string);
+ } else {
+ g_assert_not_reached();
+ }
+}
+
+static void analyse_window_set_title(struct sctp_analyse *u_data)
+{
+ char *title;
+
+ if(!u_data->window){
+ return;
+ }
+ title = g_strdup_printf("SCTP Analyse Association: %s Port1 %u Port2 %u", cf_get_display_name(&cfile), u_data->assoc->port1, u_data->assoc->port2);
+ gtk_window_set_title(GTK_WINDOW(u_data->window), title);
+ g_free(title);
+}
+
+static
+GtkWidget *create_list(void)
+{
+ GtkListStore *list_store;
+ GtkWidget * list;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeView *list_view;
+ list_store = gtk_list_store_new(1,
+ G_TYPE_STRING /* IP address */
+ );
+
+ /* Create a view */
+ list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
+
+ list_view = GTK_TREE_VIEW(list);
+
+ /* Speed up the list display */
+ gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref (G_OBJECT (list_store));
+
+ /*
+ * Create the first column packet, associating the "text" attribute of the
+ * cell_renderer to the first column of the model
+ */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Address", renderer,
+ "text", 0,
+ NULL);
+
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 300);
+
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (list_view, column);
+
+ gtk_tree_view_set_headers_visible(list_view, FALSE);
+ return list;
+}
+
+static void create_analyse_window(struct sctp_analyse* u_data)
+{
+ GtkWidget *window = NULL;
+ GtkWidget *notebook;
+ GtkWidget *main_vb, *page1, *page2, *page3, *hbox, *vbox_l, *vbox_r, *addr_hb, *stat_fr;
+ GtkWidget *hbox_l1, *hbox_l2,*label, *h_button_box;
+ GtkWidget *chunk_stat_bt, *close_bt, *graph_bt1, *graph_bt2, *chunk_bt1, *bt_filter;
+
+ u_data->analyse_nb = g_malloc(sizeof(struct notes));
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
+ g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), u_data);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
+ gtk_container_add(GTK_CONTAINER(window), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* Start a notebook for flipping between sets of changes */
+ notebook = gtk_notebook_new();
+ gtk_container_add(GTK_CONTAINER(main_vb), notebook);
+ g_object_set_data(G_OBJECT(window), "notebook", notebook);
+ g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), NULL);
+
+ page1 = gtk_vbox_new(FALSE, 8);
+ gtk_container_set_border_width(GTK_CONTAINER(page1), 8);
+
+ u_data->analyse_nb->checktype = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(page1), u_data->analyse_nb->checktype, TRUE, TRUE, 0);
+
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->checktype),0,0);
+
+ u_data->analyse_nb->checksum = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(page1), u_data->analyse_nb->checksum, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->checksum),0,0);
+
+ u_data->analyse_nb->bundling = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(page1), u_data->analyse_nb->bundling, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->bundling),0,0);
+
+ u_data->analyse_nb->padding = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(page1), u_data->analyse_nb->padding, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->padding),0,0);
+
+ u_data->analyse_nb->length = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(page1), u_data->analyse_nb->length, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->length),0,0);
+
+ u_data->analyse_nb->value = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(page1), u_data->analyse_nb->value, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->value),0,0);
+
+
+ u_data->analyse_nb->chunks_ep1 = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(page1), u_data->analyse_nb->chunks_ep1, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->chunks_ep1),0,0);
+ u_data->analyse_nb->bytes_ep1 = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(page1), u_data->analyse_nb->bytes_ep1, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->bytes_ep1),0,0);
+ u_data->analyse_nb->chunks_ep2 = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(page1), u_data->analyse_nb->chunks_ep2, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->chunks_ep2),0,0);
+ u_data->analyse_nb->bytes_ep2 = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(page1), u_data->analyse_nb->bytes_ep2, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->bytes_ep2),0,0);
+
+ hbox = gtk_hbutton_box_new();
+ gtk_box_pack_start(GTK_BOX(page1), hbox, FALSE, FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 10);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_SPREAD);
+ gtk_box_set_spacing(GTK_BOX (hbox), 0);
+ gtk_widget_show(hbox);
+
+ chunk_stat_bt = gtk_button_new_with_label ("Chunk Statistics");
+ gtk_box_pack_start(GTK_BOX(hbox), chunk_stat_bt, FALSE, FALSE, 0);
+ gtk_widget_show(chunk_stat_bt);
+ g_signal_connect(chunk_stat_bt, "clicked", G_CALLBACK(on_chunk_stat_bt), u_data);
+
+ bt_filter = gtk_button_new_with_label ("Set filter");
+ gtk_box_pack_start(GTK_BOX(hbox), bt_filter, FALSE, FALSE, 0);
+ gtk_widget_show (bt_filter);
+ g_signal_connect(bt_filter, "clicked", G_CALLBACK(sctp_set_filter), u_data);
+
+ close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_box_pack_start(GTK_BOX(hbox), close_bt, FALSE, FALSE, 0);
+ gtk_widget_show(close_bt);
+ g_signal_connect(close_bt, "clicked", G_CALLBACK(on_close_dlg), u_data);
+
+ /* tab */
+ label = gtk_label_new(" Statistics ");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page1, label);
+
+
+ /* page for endpoint 1 */
+ page2 = gtk_vbox_new(FALSE, 8);
+ gtk_container_set_border_width(GTK_CONTAINER(page2), 8);
+
+ u_data->analyse_nb->page2 = g_malloc(sizeof(struct page));
+
+ u_data->analyse_nb->page2->addr_frame = gtk_frame_new(NULL);
+ gtk_container_add(GTK_CONTAINER(page2), u_data->analyse_nb->page2->addr_frame);
+
+ addr_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(addr_hb), 5);
+ gtk_container_add(GTK_CONTAINER(u_data->analyse_nb->page2->addr_frame), addr_hb);
+
+ u_data->analyse_nb->page2->scrolled_window = scrolled_window_new(NULL, NULL);
+ gtk_widget_set_size_request(u_data->analyse_nb->page2->scrolled_window, 560, 100);
+
+ u_data->analyse_nb->page2->clist = create_list();
+ gtk_widget_show(u_data->analyse_nb->page2->clist);
+
+ gtk_container_add(GTK_CONTAINER(u_data->analyse_nb->page2->scrolled_window), u_data->analyse_nb->page2->clist);
+
+ gtk_box_pack_start(GTK_BOX(addr_hb), u_data->analyse_nb->page2->scrolled_window, TRUE, TRUE, 0);
+ gtk_widget_show(u_data->analyse_nb->page2->scrolled_window);
+
+ stat_fr = gtk_frame_new(NULL);
+ gtk_container_add(GTK_CONTAINER(page2), stat_fr);
+
+ hbox = gtk_hbox_new(FALSE,3);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
+ gtk_container_add(GTK_CONTAINER(stat_fr), hbox);
+
+ vbox_l = gtk_vbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(hbox), vbox_l, TRUE, TRUE, 0);
+
+
+
+ hbox_l1 = gtk_hbox_new(FALSE,3);
+ gtk_box_pack_start(GTK_BOX(vbox_l), hbox_l1, TRUE, TRUE, 0);
+
+ u_data->analyse_nb->page2->port = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(hbox_l1), u_data->analyse_nb->page2->port, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->page2->port),0,0);
+
+ hbox_l2 = gtk_hbox_new(FALSE,3);
+ gtk_box_pack_start(GTK_BOX(vbox_l), hbox_l2, TRUE, TRUE, 0);
+
+
+ u_data->analyse_nb->page2->veritag = gtk_label_new("");
+
+ gtk_box_pack_start(GTK_BOX(hbox_l2), u_data->analyse_nb->page2->veritag, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->page2->veritag),0,0);
+ gtk_widget_show(vbox_l);
+
+ vbox_r = gtk_vbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(hbox), vbox_r, TRUE, TRUE, 0);
+
+ u_data->analyse_nb->page2->max_in = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(vbox_r), u_data->analyse_nb->page2->max_in, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->page2->max_in),0,0);
+ u_data->analyse_nb->page2->min_in = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(vbox_r), u_data->analyse_nb->page2->min_in, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->page2->min_in),0,0);
+
+ u_data->analyse_nb->page2->max_out = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(vbox_r), u_data->analyse_nb->page2->max_out, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->page2->max_out),0,0);
+ u_data->analyse_nb->page2->min_out = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(vbox_r), u_data->analyse_nb->page2->min_out, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->page2->min_out),0,0);
+
+
+ gtk_widget_show(vbox_r);
+
+ h_button_box=gtk_hbutton_box_new();
+ gtk_box_pack_start(GTK_BOX(page2), h_button_box, FALSE, FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(h_button_box), 10);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX (h_button_box), GTK_BUTTONBOX_SPREAD);
+ gtk_box_set_spacing(GTK_BOX (h_button_box), 0);
+ gtk_widget_show(h_button_box);
+
+ chunk_bt1 = gtk_button_new_with_label("Chunk Statistics");
+ gtk_box_pack_start(GTK_BOX(h_button_box), chunk_bt1, FALSE, FALSE, 0);
+ gtk_widget_show(chunk_bt1);
+ g_signal_connect(chunk_bt1, "clicked", G_CALLBACK(on_chunk1_dlg), u_data);
+
+ graph_bt1 = gtk_button_new_with_label("Graph TSN");
+ gtk_box_pack_start(GTK_BOX(h_button_box), graph_bt1, FALSE, FALSE, 0);
+ gtk_widget_show(graph_bt1);
+ g_signal_connect(graph_bt1, "clicked", G_CALLBACK(on_graph1_dlg), u_data);
+
+ graph_bt2 = gtk_button_new_with_label("Graph Bytes");
+ gtk_box_pack_start(GTK_BOX(h_button_box), graph_bt2, FALSE, FALSE, 0);
+ gtk_widget_show(graph_bt2);
+ g_signal_connect(graph_bt2, "clicked", G_CALLBACK(on_graph_byte1_dlg), u_data);
+ if (u_data->assoc->n_array_tsn1==0)
+ {
+ gtk_widget_set_sensitive(graph_bt1, FALSE);
+ gtk_widget_set_sensitive(graph_bt2, FALSE);
+ }
+ close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_box_pack_start(GTK_BOX(h_button_box), close_bt, FALSE, FALSE, 0);
+ gtk_widget_show(close_bt);
+ g_signal_connect(close_bt, "clicked", G_CALLBACK(on_close_dlg), u_data);
+
+ label = gtk_label_new(" Endpoint 1 ");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page2, label);
+
+ /* same page for endpoint 2*/
+
+ page3 = gtk_vbox_new(FALSE, 8);
+ gtk_container_set_border_width(GTK_CONTAINER(page3), 8);
+ u_data->analyse_nb->page3 = g_malloc(sizeof(struct page));
+ u_data->analyse_nb->page3->addr_frame = gtk_frame_new(NULL);
+
+ gtk_container_add(GTK_CONTAINER(page3), u_data->analyse_nb->page3->addr_frame);
+
+ addr_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(addr_hb), 5);
+ gtk_container_add(GTK_CONTAINER(u_data->analyse_nb->page3->addr_frame), addr_hb);
+
+ u_data->analyse_nb->page3->scrolled_window = scrolled_window_new(NULL, NULL);
+ gtk_widget_set_size_request(u_data->analyse_nb->page3->scrolled_window, 560, 100);
+
+ u_data->analyse_nb->page3->clist = create_list();
+ gtk_widget_show(u_data->analyse_nb->page3->clist);
+
+ gtk_container_add(GTK_CONTAINER(u_data->analyse_nb->page3->scrolled_window),
+ u_data->analyse_nb->page3->clist);
+
+ gtk_box_pack_start(GTK_BOX(addr_hb), u_data->analyse_nb->page3->scrolled_window, TRUE, TRUE, 0);
+ gtk_widget_show(u_data->analyse_nb->page3->scrolled_window);
+
+ stat_fr = gtk_frame_new(NULL);
+ gtk_container_add(GTK_CONTAINER(page3), stat_fr);
+
+ hbox = gtk_hbox_new(FALSE,3);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
+ gtk_container_add(GTK_CONTAINER(stat_fr), hbox);
+
+ vbox_l = gtk_vbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(hbox), vbox_l, TRUE, TRUE, 0);
+
+ hbox_l1 = gtk_hbox_new(FALSE,3);
+ gtk_box_pack_start(GTK_BOX(vbox_l), hbox_l1, TRUE, TRUE, 0);
+
+ u_data->analyse_nb->page3->port = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(hbox_l1), u_data->analyse_nb->page3->port, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->page3->port),0,0);
+
+ hbox_l2 = gtk_hbox_new(FALSE,3);
+ gtk_box_pack_start(GTK_BOX(vbox_l), hbox_l2, TRUE, TRUE, 0);
+
+
+ u_data->analyse_nb->page3->veritag = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(hbox_l2), u_data->analyse_nb->page3->veritag, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->page3->veritag),0,0);
+ gtk_widget_show(vbox_l);
+
+ vbox_r=gtk_vbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(hbox), vbox_r, TRUE, TRUE, 0);
+
+ u_data->analyse_nb->page3->max_in = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(vbox_r), u_data->analyse_nb->page3->max_in, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->page3->max_in),0,0);
+ u_data->analyse_nb->page3->min_in = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(vbox_r), u_data->analyse_nb->page3->min_in, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->page3->min_in),0,0);
+
+ u_data->analyse_nb->page3->max_out = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(vbox_r), u_data->analyse_nb->page3->max_out, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->page3->max_out),0,0);
+ u_data->analyse_nb->page3->min_out = gtk_label_new("");
+ gtk_box_pack_start(GTK_BOX(vbox_r), u_data->analyse_nb->page3->min_out, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC(u_data->analyse_nb->page3->min_out),0,0);
+
+ gtk_widget_show(vbox_r);
+
+ h_button_box=gtk_hbutton_box_new();
+ gtk_box_pack_start(GTK_BOX(page3), h_button_box, FALSE, FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(h_button_box), 10);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX (h_button_box), GTK_BUTTONBOX_SPREAD);
+ gtk_box_set_spacing(GTK_BOX (h_button_box), 0);
+ gtk_widget_show(h_button_box);
+
+ chunk_bt1 = gtk_button_new_with_label("Chunk Statistics");
+ gtk_box_pack_start(GTK_BOX(h_button_box), chunk_bt1, FALSE, FALSE, 0);
+ gtk_widget_show(chunk_bt1);
+ g_signal_connect(chunk_bt1, "clicked", G_CALLBACK(on_chunk2_dlg), u_data);
+
+ graph_bt1 = gtk_button_new_with_label("Graph TSN");
+ gtk_box_pack_start(GTK_BOX(h_button_box), graph_bt1, FALSE, FALSE, 0);
+ gtk_widget_show(graph_bt1);
+ g_signal_connect(graph_bt1, "clicked", G_CALLBACK(on_graph2_dlg), u_data);
+ graph_bt2 = gtk_button_new_with_label("Graph Bytes");
+ gtk_box_pack_start(GTK_BOX(h_button_box), graph_bt2, FALSE, FALSE, 0);
+ gtk_widget_show(graph_bt2);
+ g_signal_connect(graph_bt2, "clicked", G_CALLBACK(on_graph_byte2_dlg), u_data);
+ if (u_data->assoc->n_array_tsn2==0)
+ {
+ gtk_widget_set_sensitive(graph_bt1, FALSE);
+ gtk_widget_set_sensitive(graph_bt2, FALSE);
+ }
+ close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_box_pack_start(GTK_BOX(h_button_box), close_bt, FALSE, FALSE, 0);
+ gtk_widget_show(close_bt);
+ g_signal_connect(close_bt, "clicked", G_CALLBACK(on_close_dlg), u_data);
+
+ label = gtk_label_new(" Endpoint 2 ");
+ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page3, label);
+
+ /* show all notebooks */
+ gtk_widget_show_all(notebook);
+
+ gtk_widget_show(window);
+
+ u_data->window = window;
+ analyse_window_set_title(u_data);
+
+ update_analyse_dlg(u_data);
+}
+
+
+
+void assoc_analyse(sctp_assoc_info_t* assoc)
+{
+ struct sctp_analyse* u_data;
+ int i;
+
+ u_data = g_malloc(sizeof(struct sctp_analyse));
+ u_data->assoc = assoc;
+ u_data->assoc->addr_chunk_count = assoc->addr_chunk_count;
+ u_data->window = NULL;
+ u_data->analyse_nb = NULL;
+ u_data->children = NULL;
+ u_data->num_children = 0;
+ static_assoc.addr_chunk_count =assoc->addr_chunk_count;
+ static_assoc.port1 = assoc->port1;
+ static_assoc.port2 = assoc->port2;
+ for (i=0; i<NUM_CHUNKS; i++)
+ {
+ static_assoc.chunk_count[i] = assoc->chunk_count[i];
+ static_assoc.ep1_chunk_count[i] = assoc->ep1_chunk_count[i];
+ static_assoc.ep2_chunk_count[i] = assoc->ep2_chunk_count[i];
+ }
+ set_analyse_child(u_data);
+ increase_analyse_childcount();
+ static_assoc.addr_chunk_count = assoc->addr_chunk_count;
+ create_analyse_window(u_data);
+}
+
+
+static void sctp_analyse_cb(struct sctp_analyse* u_data, gboolean ext)
+{
+ GList *list, *framelist;
+ dfilter_t *sfcode;
+ capture_file *cf;
+ epan_dissect_t edt;
+ gboolean frame_matched, frame_found = FALSE;
+ frame_data *fdata;
+ gchar filter_text[256];
+ sctp_assoc_info_t* assoc = NULL;
+ int i;
+ guint32 *fn;
+
+ g_strlcpy(filter_text,"sctp",250);
+ if (!dfilter_compile(filter_text, &sfcode)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
+ return;
+ }
+
+ cf = &cfile;
+ fdata = cf->current_frame;
+
+ /* we are on the selected frame now */
+ if (fdata == NULL)
+ return; /* if we exit here it's an error */
+
+ /* dissect the current frame */
+ if (!cf_read_frame(cf, fdata))
+ return; /* error reading the frame */
+
+ epan_dissect_init(&edt, TRUE, FALSE);
+ epan_dissect_prime_dfilter(&edt, sfcode);
+ epan_dissect_run(&edt, &cf->pseudo_header, cf->pd, fdata, NULL);
+ frame_matched = dfilter_apply_edt(sfcode, &edt);
+
+ /* if it is not an sctp frame, show the dialog */
+
+ if (frame_matched != 1) {
+ epan_dissect_cleanup(&edt);
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Please choose an SCTP packet!");
+ return;
+ }
+
+ list = g_list_first(sctp_stat_get_info()->assoc_info_list);
+
+ while (list)
+ {
+ assoc = (sctp_assoc_info_t*)(list->data);
+ framelist = g_list_first(assoc->frame_numbers);
+ while(framelist)
+ {
+ fn = (guint32 *)framelist->data;
+ if (*fn == fdata->num)
+ {
+ frame_found = TRUE;
+ break;
+ }
+ framelist = g_list_next(framelist);
+ }
+ if (frame_found)
+ {
+ u_data->assoc = assoc;
+ u_data->assoc->addr_chunk_count = assoc->addr_chunk_count;
+ static_assoc.addr_chunk_count = assoc->addr_chunk_count;
+ static_assoc.port1 = assoc->port1;
+ static_assoc.port2 = assoc->port2;
+ for (i=0; i<NUM_CHUNKS; i++)
+ {
+ static_assoc.chunk_count[i] = assoc->chunk_count[i];
+ static_assoc.ep1_chunk_count[i] = assoc->ep1_chunk_count[i];
+ static_assoc.ep2_chunk_count[i] = assoc->ep2_chunk_count[i];
+ }
+ if (ext == FALSE)
+ create_analyse_window(u_data);
+ return;
+ }
+ else
+ list = g_list_next(list);
+
+ }
+ if (!frame_found)
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Assoc not found!");
+}
+
+void sctp_set_assoc_filter(void)
+{
+struct sctp_analyse * u_data;
+
+ /* Register the tap listener */
+ if (sctp_stat_get_info()->is_registered == FALSE)
+ register_tap_listener_sctp_stat();
+ /* (redissect all packets) */
+
+ sctp_stat_scan();
+ u_data = g_malloc(sizeof(struct sctp_analyse));
+ u_data->assoc = NULL;
+ u_data->children = NULL;
+ u_data->analyse_nb = NULL;
+ u_data->window = NULL;
+ u_data->num_children = 0;
+ cf_retap_packets(&cfile);
+ sctp_analyse_cb(u_data, TRUE);
+ sctp_set_filter(NULL, u_data);
+}
+
+void sctp_analyse_start(GtkAction *action _U_, gpointer user_data _U_)
+{
+ struct sctp_analyse * u_data;
+
+ /* Register the tap listener */
+ if (sctp_stat_get_info()->is_registered == FALSE)
+ register_tap_listener_sctp_stat();
+ /* (redissect all packets) */
+
+ sctp_stat_scan();
+
+ u_data = g_malloc(sizeof(struct sctp_analyse));
+ u_data->assoc = NULL;
+ u_data->children = NULL;
+ u_data->analyse_nb = NULL;
+ u_data->window = NULL;
+ u_data->num_children = 0;
+
+ cf_retap_packets(&cfile);
+ sctp_analyse_cb(u_data, FALSE);
+}
+
+
+void
+register_tap_listener_sctp_analyse(void)
+{
+}
+
diff --git a/ui/gtk/sctp_byte_graph_dlg.c b/ui/gtk/sctp_byte_graph_dlg.c
new file mode 100644
index 0000000000..6be366fbbb
--- /dev/null
+++ b/ui/gtk/sctp_byte_graph_dlg.c
@@ -0,0 +1,1542 @@
+/*
+ * Copyright 2004, Irene Ruengeler <i.ruengeler [AT] fh-muenster.de>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/filesystem.h"
+#include <epan/strutil.h>
+
+#include "../globals.h"
+#include "../ui_util.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/sctp_stat.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+#define DEFAULT_PIXELS_PER_TICK 2
+#define MAX_PIXELS_PER_TICK 4
+#define AUTO_MAX_YSCALE 0
+#define MAX_TICK_VALUES 5
+#define DEFAULT_TICK_VALUE 3
+#define MAX_YSCALE 22
+#define MAX_COUNT_TYPES 3
+
+#define COUNT_TYPE_FRAMES 0
+#define COUNT_TYPE_BYTES 1
+#define COUNT_TYPE_ADVANCED 2
+
+#define LEFT_BORDER 80
+#define RIGHT_BORDER 20
+#define TOP_BORDER 20
+#define BOTTOM_BORDER 50
+
+#define SUB_32(a, b) a-b
+
+struct chunk_header {
+ guint8 type;
+ guint8 flags;
+ guint16 length;
+};
+
+struct data_chunk_header {
+ guint8 type;
+ guint8 flags;
+ guint16 length;
+ guint32 tsn;
+ guint16 sid;
+ guint16 ssn;
+ guint32 ppi;
+};
+
+struct init_chunk_header {
+ guint8 type;
+ guint8 flags;
+ guint16 length;
+ guint32 initiate_tag;
+ guint32 a_rwnd;
+ guint16 mos;
+ guint16 mis;
+ guint32 initial_tsn;
+};
+
+struct sack_chunk_header {
+ guint8 type;
+ guint8 flags;
+ guint16 length;
+ guint32 cum_tsn_ack;
+ guint32 a_rwnd;
+ guint16 nr_of_gaps;
+ guint16 nr_of_dups;
+ guint8 *tsns;
+};
+
+struct gaps {
+ guint16 start;
+ guint16 end;
+};
+
+static gboolean label_set = FALSE;
+static guint32 max_tsn=0, min_tsn=0;
+
+
+static void sctp_graph_set_title(struct sctp_udata *u_data);
+static void create_draw_area(GtkWidget *box, struct sctp_udata *u_data);
+static GtkWidget *zoomout_bt;
+
+static void draw_sack_graph(struct sctp_udata *u_data)
+{
+ GdkColor red_color = {0, 65535, 0, 0};
+ GdkColor green_color = {0, 0, 65535, 0};
+ gint diff;
+ GPtrArray *array = NULL;
+ guint32 i, size = 0, start=0, end;
+ gboolean more = FALSE;
+ gint width;
+ cairo_t *cr;
+
+ if (u_data->dir == 1)
+ {
+ array = u_data->assoc->sort_sack1;
+ size=u_data->assoc->n_sack_chunks_ep1;
+ if (u_data->io->tmp == FALSE)
+ {
+ min_tsn = 0;
+ max_tsn = u_data->assoc->max_bytes1;
+ }
+ else
+ {
+ min_tsn = u_data->io->tmp_min_tsn1;
+ max_tsn = u_data->io->tmp_max_tsn1;
+ }
+ }
+ else if (u_data->dir == 2)
+ {
+ array = u_data->assoc->sort_sack2;
+ size = u_data->assoc->n_sack_chunks_ep2;
+ if (u_data->io->tmp == FALSE)
+ {
+ min_tsn = 0;
+ max_tsn = u_data->assoc->max_bytes2;
+ }
+ else
+ {
+ min_tsn = u_data->io->tmp_min_tsn2;
+ max_tsn = u_data->io->tmp_max_tsn2;
+ }
+ }
+
+ width = u_data->io->max_x - u_data->io->min_x;
+
+ for (i=0; i<size; i++)
+ {
+ if (u_data->io->uoff)
+ diff = (gint)((struct tsn_sort*)(g_ptr_array_index(array, i)))->secs - u_data->io->min_x;
+ else
+ diff = (gint)((struct tsn_sort*)(g_ptr_array_index(array, i)))->secs * 1000000 + ((struct tsn_sort*)(g_ptr_array_index(array, i)))->usecs - u_data->io->min_x;
+ end = start + ((struct tsn_sort*)(g_ptr_array_index(array, i)))->length;
+ if (end>max_tsn)
+ {
+ end = max_tsn;
+ more = TRUE;
+ }
+
+ if (start >= min_tsn && diff > 0 && diff <= width)
+ {
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ gdk_cairo_set_source_color (cr, &red_color);
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr,
+ LEFT_BORDER+u_data->io->offset+u_data->io->x_interval*diff+0.5,
+ u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset-((SUB_32(start,min_tsn))*u_data->io->y_interval)+0.5);
+ cairo_line_to(cr,
+ LEFT_BORDER+u_data->io->offset+u_data->io->x_interval*diff+0.5,
+ u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset-((SUB_32(end,min_tsn))*u_data->io->y_interval)+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ if (more == TRUE)
+ {
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ gdk_cairo_set_source_color (cr, &green_color);
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr,
+ LEFT_BORDER+u_data->io->offset+u_data->io->x_interval*diff+0.5,
+ u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset-((SUB_32(end,min_tsn))*u_data->io->y_interval)+0.5);
+ cairo_line_to(cr,
+ LEFT_BORDER+u_data->io->offset+u_data->io->x_interval*diff,
+ u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset-((SUB_32(end+10,min_tsn))*u_data->io->y_interval)+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ more = FALSE;
+ }
+ }
+
+ }
+}
+
+
+static void draw_tsn_graph(struct sctp_udata *u_data)
+{
+GPtrArray *array = NULL;
+guint32 i, size = 0, start, end;
+gint diff, width;
+cairo_t *cr;
+
+ if (u_data->dir == 1)
+ {
+ array = u_data->assoc->sort_tsn1;
+ size = u_data->assoc->n_data_chunks_ep1;
+ if (u_data->io->tmp == FALSE)
+ {
+ min_tsn = 0;
+ max_tsn = u_data->assoc->max_bytes1;
+ }
+ else
+ {
+ min_tsn = u_data->io->tmp_min_tsn1;
+ max_tsn = u_data->io->tmp_max_tsn1;
+ }
+ }
+ else if (u_data->dir == 2)
+ {
+ array = u_data->assoc->sort_tsn2;
+ size = u_data->assoc->n_data_chunks_ep2;
+ if (u_data->io->tmp == FALSE)
+ {
+ min_tsn = 0;
+ max_tsn = u_data->assoc->max_bytes2;
+ }
+ else
+ {
+ min_tsn = u_data->io->tmp_min_tsn2;
+ max_tsn = u_data->io->tmp_max_tsn2;
+ }
+ }
+ width = u_data->io->max_x - u_data->io->min_x;
+
+ for (i=0; i<size; i++)
+ {
+ if (u_data->io->uoff)
+ diff = (gint)((struct tsn_sort*)(g_ptr_array_index(array, i)))->secs -u_data->io->min_x;
+ else
+ diff = (gint)((struct tsn_sort*)(g_ptr_array_index(array, i)))->secs*1000000 + ((struct tsn_sort*)(g_ptr_array_index(array, i)))->usecs-u_data->io->min_x;
+ start = ((struct tsn_sort*)(g_ptr_array_index(array, i)))->offset;
+ end = start + ((struct tsn_sort*)(g_ptr_array_index(array, i)))->length;
+ if (start >= min_tsn && diff > 0 && diff <= width){
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr,
+ (LEFT_BORDER+u_data->io->offset+u_data->io->x_interval*diff)+0.5,
+ (u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset-((SUB_32(start,min_tsn))*u_data->io->y_interval))+0.5);
+ cairo_line_to(cr,
+ (LEFT_BORDER+u_data->io->offset+u_data->io->x_interval*diff)+0.5,
+ (u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset-((SUB_32(end,min_tsn))*u_data->io->y_interval))+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ }
+ }
+
+}
+
+
+static void sctp_graph_draw(struct sctp_udata *u_data)
+{
+ int length, lwidth;
+ guint32 distance=5, i, e, sec, w, start, a, j, b;
+ gint label_width, label_height;
+ char label_string[15];
+ gfloat dis;
+ gboolean write_label = FALSE;
+ PangoLayout *layout;
+ GtkAllocation widget_alloc;
+ cairo_t *cr;
+
+ if (u_data->io->x1_tmp_sec == 0 && u_data->io->x1_tmp_usec == 0)
+ u_data->io->offset = 0;
+ else
+ u_data->io->offset = 5;
+
+ if (u_data->io->x2_tmp_sec - u_data->io->x1_tmp_sec > 1500)
+ {
+ u_data->io->min_x=u_data->io->x1_tmp_sec;
+ u_data->io->max_x=u_data->io->x2_tmp_sec;
+ u_data->io->uoff = TRUE;
+ }
+ else
+ {
+ u_data->io->min_x=((guint32) (u_data->io->x1_tmp_sec*1000000.0))+u_data->io->x1_tmp_usec;
+ u_data->io->max_x=((guint32) (u_data->io->x2_tmp_sec*1000000.0))+u_data->io->x2_tmp_usec;
+ u_data->io->uoff = FALSE;
+ }
+
+ u_data->io->tmp_width = u_data->io->max_x - u_data->io->min_x;
+
+ if (u_data->dir == 1)
+ {
+ if (u_data->io->tmp == FALSE)
+ {
+ if (u_data->assoc->sort_tsn1 != NULL)
+ u_data->io->max_y = u_data->io->tmp_max_tsn1 - u_data->io->tmp_min_tsn1;
+ else
+ u_data->io->max_y = 0;
+ u_data->io->min_y = 0;
+ }
+ else
+ {
+ u_data->io->max_y = u_data->io->tmp_max_tsn1;
+ u_data->io->min_y = u_data->io->tmp_min_tsn1;
+ }
+ }
+ else if (u_data->dir == 2)
+ {
+ if (u_data->io->tmp == FALSE)
+ {
+ if (u_data->assoc->tsn2 != NULL)
+ u_data->io->max_y = u_data->io->tmp_max_tsn2 - u_data->io->tmp_min_tsn2;
+ else
+ u_data->io->max_y = 0;
+ u_data->io->min_y = 0;
+ }
+ else
+ {
+ u_data->io->max_y = u_data->io->tmp_max_tsn2;
+ u_data->io->min_y = u_data->io->tmp_min_tsn2;
+ }
+ }
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ gtk_widget_get_allocation(u_data->io->draw_area, &widget_alloc);
+ cairo_rectangle (cr,
+ 0,
+ 0,
+ widget_alloc.width,
+ widget_alloc.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+ distance = 5;
+ /* x_axis */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, LEFT_BORDER+u_data->io->offset+0.5, u_data->io->surface_height - BOTTOM_BORDER+0.5);
+ cairo_line_to(cr, u_data->io->surface_width - RIGHT_BORDER + u_data->io->offset+0.5, u_data->io->surface_height - BOTTOM_BORDER+0.5);
+
+ cairo_move_to(cr, u_data->io->surface_width - RIGHT_BORDER + u_data->io->offset+0.5, u_data->io->surface_height - BOTTOM_BORDER+0.5);
+ cairo_line_to(cr, u_data->io->surface_width - RIGHT_BORDER + u_data->io->offset - 5+0.5, u_data->io->surface_height - BOTTOM_BORDER - 5+0.5);
+
+ cairo_move_to(cr, u_data->io->surface_width - RIGHT_BORDER + u_data->io->offset + 0.5, u_data->io->surface_height - BOTTOM_BORDER + 0.5);
+ cairo_line_to(cr, u_data->io->surface_width - RIGHT_BORDER + u_data->io->offset - 5.5, u_data->io->surface_height - BOTTOM_BORDER + 5.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+ u_data->io->axis_width = u_data->io->surface_width - LEFT_BORDER - RIGHT_BORDER - u_data->io->offset;
+
+ if(u_data->io->tmp_width>0){
+ u_data->io->x_interval = (float)((u_data->io->axis_width*1.0)/u_data->io->tmp_width); /*distance in pixels between 2 data points*/
+ } else {
+ u_data->io->x_interval = (float)(u_data->io->axis_width);
+ }
+
+ e=0;
+ if (u_data->io->x_interval < 1)
+ {
+ dis = 1 / u_data->io->x_interval;
+ while (dis >1)
+ {
+ dis /= 10;
+ e++;
+ }
+ distance = 1;
+ for (i=0; i<=e+1; i++)
+ distance *= 10;
+ }
+ else
+ distance = 5;
+
+ g_snprintf(label_string, sizeof(label_string), "%d", 0);
+ memcpy(label_string,(gchar *)g_locale_to_utf8(label_string, -1 , NULL, NULL, NULL), 15);
+ layout = gtk_widget_create_pango_layout(u_data->io->draw_area, label_string);
+ pango_layout_get_pixel_size(layout, &label_width, &label_height);
+
+ if (u_data->io->x1_tmp_usec == 0)
+ sec = u_data->io->x1_tmp_sec;
+ else
+ sec = u_data->io->x1_tmp_sec+1;
+
+ if (u_data->io->offset != 0)
+ {
+ g_snprintf(label_string, sizeof(label_string), "%u", u_data->io->x1_tmp_sec);
+ memcpy(label_string,(gchar *)g_locale_to_utf8(label_string, -1 , NULL, NULL, NULL), sizeof(label_string));
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_move_to (cr, LEFT_BORDER - 25, u_data->io->surface_height - BOTTOM_BORDER + 20);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+
+ }
+ w = (guint32)(500 / (guint32)(distance * u_data->io->x_interval));
+ if (w == 0)
+ w = 1;
+ if (w == 4 || w==3 || w==2)
+ {
+ w = 5;
+ a = distance / 10;
+ b = (guint32)((u_data->io->min_x/100000))%10; /* start for labels*/
+ }
+ else
+ {
+ a = distance / 5;
+ b = 0;
+ }
+
+ if (!u_data->io->uoff)
+ {
+ if (a>=1000000)
+ {
+ start=u_data->io->min_x/1000000*1000000;
+ if (a==1000000)
+ b = 0;
+ }
+ else
+ {
+ start=u_data->io->min_x/100000;
+ if (start%2!=0)
+ start--;
+ start*=100000;
+ b = (guint32)((start/100000))%10;
+ }
+ }
+ else
+ {
+ start = u_data->io->min_x;
+ if (start%2!=0)
+ start--;
+ b = 0;
+
+ }
+
+ for (i=start, j=b; i<=u_data->io->max_x; i+=a, j++)
+ {
+ if (!u_data->io->uoff)
+ if (i >= u_data->io->min_x && i % 1000000 != 0)
+ {
+ length = 5;
+ g_snprintf(label_string, sizeof(label_string), "%d", i%1000000);
+
+ if (j % w == 0)
+ {
+ length = 10;
+
+ memcpy(label_string,(gchar *)g_locale_to_utf8(label_string, -1 , NULL, NULL, NULL), sizeof(label_string));
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_move_to (cr,
+ LEFT_BORDER + u_data->io->offset + (i - u_data->io->min_x) * u_data->io->x_interval - lwidth / 2,
+ u_data->io->surface_height - BOTTOM_BORDER + 10);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr,
+ LEFT_BORDER + u_data->io->offset + (i - u_data->io->min_x) * u_data->io->x_interval + 0.5,
+ u_data->io->surface_height - BOTTOM_BORDER + 0.5);
+ cairo_line_to(cr,
+ LEFT_BORDER + u_data->io->offset + (i - u_data->io->min_x) * u_data->io->x_interval + 0.5,
+ u_data->io->surface_height - BOTTOM_BORDER + length + 0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ }
+
+ if (!u_data->io->uoff)
+ {
+ if (i%1000000==0 && j%w==0)
+ {
+ sec=i/1000000;
+ write_label = TRUE;
+ }
+ }
+ else
+ {
+ if (j%w == 0)
+ {
+ sec = i;
+ write_label = TRUE;
+ }
+ }
+ if (write_label)
+ {
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr,
+ LEFT_BORDER + u_data->io->offset + (i - u_data->io->min_x) * u_data->io->x_interval + 0.5,
+ u_data->io->surface_height - BOTTOM_BORDER + 0.5);
+ cairo_line_to(cr,
+ LEFT_BORDER + u_data->io->offset + (i - u_data->io->min_x) * u_data->io->x_interval + 0.5,
+ u_data->io->surface_height - BOTTOM_BORDER + 10 + 0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+ g_snprintf(label_string, sizeof(label_string), "%d", sec);
+ memcpy(label_string,(gchar *)g_locale_to_utf8(label_string, -1 , NULL, NULL, NULL), sizeof(label_string));
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_move_to (cr,
+ (LEFT_BORDER + u_data->io->offset + (i - u_data->io->min_x) * u_data->io->x_interval-10),
+ u_data->io->surface_height - BOTTOM_BORDER + 20);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+
+ write_label = FALSE;
+ }
+ }
+
+ g_strlcpy(label_string, "sec", sizeof(label_string));
+
+ memcpy(label_string,(gchar *)g_locale_to_utf8(label_string, -1 , NULL, NULL, NULL), sizeof(label_string));
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_move_to (cr,
+ u_data->io->surface_width - RIGHT_BORDER - 10,
+ u_data->io->surface_height - BOTTOM_BORDER + 30);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+
+ distance = 5;
+
+ /* y-axis */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, LEFT_BORDER + 0.5, TOP_BORDER - u_data->io->offset + 0.5);
+ cairo_line_to(cr, LEFT_BORDER + 0.5, u_data->io->surface_height - BOTTOM_BORDER - u_data->io->offset + 0.5);
+
+ cairo_move_to(cr, LEFT_BORDER + 0.5, TOP_BORDER - u_data->io->offset + 0.5);
+ cairo_line_to(cr, LEFT_BORDER - 5 + 0.5, TOP_BORDER - u_data->io->offset + 5 + 0.5);
+
+ cairo_move_to(cr, LEFT_BORDER + 0.5, TOP_BORDER - u_data->io->offset + 0.5);
+ cairo_line_to(cr, LEFT_BORDER +5 + 0.5, TOP_BORDER - u_data->io->offset + 5 + 0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+ u_data->io->y_interval = (float)(((u_data->io->surface_height - TOP_BORDER - BOTTOM_BORDER) * 1.0)/(u_data->io->max_y - u_data->io->min_y));
+
+ e = 0;
+ if (u_data->io->y_interval < 1)
+ {
+ dis = 1 / u_data->io->y_interval;
+ while (dis > 1)
+ {
+ dis /= 10;
+ e++;
+ }
+ distance = 1;
+ for (i=0; i<=e; i++)
+ distance = distance * 10;
+ }
+ else if (u_data->io->y_interval<2)
+ distance = 10;
+
+ if (u_data->io->max_y > 0)
+ {
+ for (i=u_data->io->min_y/distance*distance; i<=u_data->io->max_y; i+=distance/5)
+ {
+ if (i >= u_data->io->min_y)
+ {
+ length = 5;
+ g_snprintf(label_string, sizeof(label_string), "%d", i);
+
+ if (i%distance == 0 || (distance <= 5 && u_data->io->y_interval > 10))
+ {
+ length = 10;
+
+ memcpy(label_string,(gchar *)g_locale_to_utf8(label_string, -1 , NULL, NULL, NULL), sizeof(label_string));
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_move_to (cr,
+ LEFT_BORDER - length - lwidth - 5,
+ u_data->io->surface_height - BOTTOM_BORDER - u_data->io->offset - (i - u_data->io->min_y) * u_data->io->y_interval - 3);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr,
+ LEFT_BORDER - length + 0.5,
+ u_data->io->surface_height - BOTTOM_BORDER - u_data->io->offset - (i - u_data->io->min_y) * u_data->io->y_interval + 0.5);
+ cairo_line_to(cr,
+ LEFT_BORDER + 0.5,
+ u_data->io->surface_height - BOTTOM_BORDER - u_data->io->offset - (i - u_data->io->min_y) * u_data->io->y_interval + 0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ }
+ }
+ }
+ else
+ simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK, "No Data Chunks sent");
+
+ g_object_unref(G_OBJECT(layout));
+}
+
+
+static void sctp_graph_redraw(struct sctp_udata *u_data)
+{
+ sctp_graph_t *ios;
+ GtkAllocation widget_alloc;
+ cairo_t *cr;
+
+ u_data->io->needs_redraw = TRUE;
+
+ sctp_graph_draw(u_data);
+ switch (u_data->io->graph_type)
+ {
+ case 0:
+ draw_sack_graph(u_data);
+ draw_tsn_graph(u_data);
+ break;
+ case 1:
+ draw_tsn_graph(u_data);
+ break;
+ case 2:
+ draw_sack_graph(u_data);
+ break;
+ }
+
+ ios=(sctp_graph_t *)g_object_get_data(G_OBJECT(u_data->io->draw_area), "sctp_graph_t");
+ g_assert(ios != NULL);
+
+ cr = gdk_cairo_create (gtk_widget_get_window(u_data->io->draw_area));
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, ios->surface, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, ios->pixmap, 0, 0);
+#endif
+ gtk_widget_get_allocation(u_data->io->draw_area, &widget_alloc);
+ cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+}
+
+
+static void on_sack_bt(GtkWidget *widget _U_, struct sctp_udata *u_data)
+{
+ u_data = (struct sctp_udata *) u_data;
+ u_data->io->graph_type = 2;
+ sctp_graph_redraw(u_data);
+}
+
+static void on_tsn_bt(GtkWidget *widget _U_, struct sctp_udata *u_data)
+{
+ u_data->io->graph_type = 1;
+ sctp_graph_redraw(u_data);
+}
+
+static void on_both_bt(GtkWidget *widget _U_, struct sctp_udata *u_data)
+{
+ u_data->io->graph_type = 0;
+ sctp_graph_redraw(u_data);
+}
+
+static void
+sctp_graph_close_cb(GtkWidget* widget _U_, gpointer u_data)
+{
+ struct sctp_udata *udata;
+
+ udata = (struct sctp_udata *)u_data;
+ gtk_grab_remove(GTK_WIDGET(udata->io->window));
+ gtk_widget_destroy(GTK_WIDGET(udata->io->window));
+}
+
+
+
+static gboolean
+on_configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer user_data)
+{
+ struct sctp_udata *u_data = user_data;
+ GtkAllocation widget_alloc;
+ cairo_t *cr;
+
+ g_assert(u_data->io != NULL);
+
+#if GTK_CHECK_VERSION(2,22,0)
+ if(u_data->io->surface){
+ cairo_surface_destroy (u_data->io->surface);
+ u_data->io->surface=NULL;
+ }
+ gtk_widget_get_allocation(widget, &widget_alloc);
+ u_data->io->surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
+ CAIRO_CONTENT_COLOR,
+ widget_alloc.width,
+ widget_alloc.height);
+#else
+ if(u_data->io->pixmap){
+ g_object_unref(u_data->io->pixmap);
+ u_data->io->pixmap = NULL;
+ }
+ gtk_widget_get_allocation(widget, &widget_alloc);
+ u_data->io->pixmap = gdk_pixmap_new(gtk_widget_get_window(widget),
+ widget_alloc.width,
+ widget_alloc.height,
+ -1);
+#endif
+ u_data->io->surface_width = widget_alloc.width;
+ u_data->io->surface_height = widget_alloc.height;
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+ sctp_graph_redraw(u_data);
+ return TRUE;
+}
+
+#if GTK_CHECK_VERSION(3,0,0)
+static gboolean
+on_draw_area_draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data)
+{
+ sctp_graph_t *ios = user_data;
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation (widget, &allocation);
+ cairo_set_source_surface (cr, ios->surface, 0, 0);
+ cairo_rectangle (cr, 0, 0, allocation.width, allocation.width);
+ cairo_fill (cr);
+
+ return FALSE;
+}
+#else
+static gboolean
+on_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
+{
+ sctp_graph_t *ios = user_data;
+ cairo_t *cr;
+
+ g_assert(ios != NULL);
+
+ cr = gdk_cairo_create (gtk_widget_get_window(widget));
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, ios->surface, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, ios->pixmap, 0, 0);
+#endif
+ cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+#endif
+
+static void
+on_zoomin_bt (GtkWidget *widget _U_, struct sctp_udata *u_data)
+{
+ sctp_min_max_t *tmp_minmax;
+
+ if (u_data->io->rectangle==TRUE)
+ {
+ tmp_minmax = g_malloc(sizeof(sctp_min_max_t));
+
+ u_data->io->tmp_min_tsn1=u_data->io->y1_tmp+u_data->io->min_y;
+ u_data->io->tmp_max_tsn1=u_data->io->y2_tmp+1+u_data->io->min_y;
+
+ u_data->io->tmp_min_tsn2=u_data->io->tmp_min_tsn1;
+ u_data->io->tmp_max_tsn2=u_data->io->tmp_max_tsn1;
+ tmp_minmax->tmp_min_secs=u_data->io->x1_tmp_sec;
+ tmp_minmax->tmp_min_usecs= u_data->io->x1_tmp_usec;
+ tmp_minmax->tmp_max_secs= u_data->io->x2_tmp_sec;
+ tmp_minmax->tmp_max_usecs= u_data->io->x2_tmp_usec;
+ tmp_minmax->tmp_min_tsn1=u_data->io->tmp_min_tsn1;
+ tmp_minmax->tmp_max_tsn1=u_data->io->tmp_max_tsn1;
+ tmp_minmax->tmp_min_tsn2=u_data->io->tmp_min_tsn2;
+ tmp_minmax->tmp_max_tsn2=u_data->io->tmp_max_tsn2;
+ u_data->assoc->min_max = g_slist_prepend(u_data->assoc->min_max, tmp_minmax);
+ u_data->io->length = g_slist_length(u_data->assoc->min_max);
+ u_data->io->tmp=TRUE;
+ u_data->io->rectangle=FALSE;
+ gtk_widget_set_sensitive(zoomout_bt, TRUE);
+ sctp_graph_redraw(u_data);
+ }
+ else
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Please draw a rectangle around the area you want to zoom in!");
+ }
+}
+
+static void
+zoomin_bt_fcn (struct sctp_udata *u_data)
+{
+ sctp_min_max_t *tmp_minmax;
+
+ tmp_minmax = g_malloc(sizeof(sctp_min_max_t));
+
+ u_data->io->tmp_min_tsn1=u_data->io->y1_tmp+u_data->io->min_y;
+ u_data->io->tmp_max_tsn1=u_data->io->y2_tmp+1+u_data->io->min_y;
+
+ u_data->io->tmp_min_tsn2=u_data->io->tmp_min_tsn1;
+ u_data->io->tmp_max_tsn2=u_data->io->tmp_max_tsn1;
+ tmp_minmax->tmp_min_secs=u_data->io->x1_tmp_sec;
+ tmp_minmax->tmp_min_usecs=u_data->io->x1_tmp_usec;
+ tmp_minmax->tmp_max_secs=u_data->io->x2_tmp_sec;
+ tmp_minmax->tmp_max_usecs=u_data->io->x2_tmp_usec;
+ tmp_minmax->tmp_min_tsn1=u_data->io->tmp_min_tsn1;
+ tmp_minmax->tmp_max_tsn1=u_data->io->tmp_max_tsn1;
+ tmp_minmax->tmp_min_tsn2=u_data->io->tmp_min_tsn2;
+ tmp_minmax->tmp_max_tsn2=u_data->io->tmp_max_tsn2;
+ u_data->assoc->min_max = g_slist_prepend(u_data->assoc->min_max, tmp_minmax);
+ u_data->io->length = g_slist_length(u_data->assoc->min_max);
+ u_data->io->tmp=TRUE;
+ u_data->io->rectangle=FALSE;
+ gtk_widget_set_sensitive(zoomout_bt, TRUE);
+ sctp_graph_redraw(u_data);
+}
+
+static void
+on_zoomout_bt (GtkWidget *widget _U_, struct sctp_udata *u_data)
+{
+ sctp_min_max_t *tmp_minmax, *mm;
+ gint l;
+
+ l = g_slist_length(u_data->assoc->min_max);
+
+ if (u_data->assoc->min_max!=NULL)
+ {
+ mm=(sctp_min_max_t *)((u_data->assoc->min_max)->data);
+ u_data->assoc->min_max=g_slist_remove(u_data->assoc->min_max, mm);
+ g_free(mm);
+
+ if (l>2)
+ {
+ tmp_minmax = (sctp_min_max_t *)u_data->assoc->min_max->data;
+ u_data->io->x1_tmp_sec=tmp_minmax->tmp_min_secs;
+ u_data->io->x1_tmp_usec=tmp_minmax->tmp_min_usecs;
+ u_data->io->x2_tmp_sec=tmp_minmax->tmp_max_secs;
+ u_data->io->x2_tmp_usec=tmp_minmax->tmp_max_usecs;
+ u_data->io->tmp_min_tsn1=tmp_minmax->tmp_min_tsn1;
+ u_data->io->tmp_max_tsn1=tmp_minmax->tmp_max_tsn1;
+ u_data->io->tmp_min_tsn2=tmp_minmax->tmp_min_tsn2;
+ u_data->io->tmp_max_tsn2=tmp_minmax->tmp_max_tsn2;
+ u_data->io->tmp=TRUE;
+ }
+ else
+ {
+ u_data->io->x1_tmp_sec=u_data->assoc->min_secs;
+ u_data->io->x1_tmp_usec=u_data->assoc->min_usecs;
+ u_data->io->x2_tmp_sec=u_data->assoc->max_secs;
+ u_data->io->x2_tmp_usec=u_data->assoc->max_usecs;
+ u_data->io->tmp_min_tsn1=0;
+ u_data->io->tmp_max_tsn1=u_data->assoc->max_bytes1;
+ u_data->io->tmp_min_tsn2=0;
+ u_data->io->tmp_max_tsn2=u_data->assoc->max_bytes2;
+ u_data->io->tmp=FALSE;
+ }
+ }
+ else
+ {
+ u_data->io->x1_tmp_sec=u_data->assoc->min_secs;
+ u_data->io->x1_tmp_usec=u_data->assoc->min_usecs;
+ u_data->io->x2_tmp_sec=u_data->assoc->max_secs;
+ u_data->io->x2_tmp_usec=u_data->assoc->max_usecs;
+ u_data->io->tmp_min_tsn1=0;
+ u_data->io->tmp_max_tsn1=u_data->assoc->max_bytes1;
+ u_data->io->tmp_min_tsn2=0;
+ u_data->io->tmp_max_tsn2=u_data->assoc->max_bytes2;
+ u_data->io->tmp=FALSE;
+ }
+ if (g_slist_length(u_data->assoc->min_max)==1)
+ gtk_widget_set_sensitive(zoomout_bt, FALSE);
+ sctp_graph_redraw(u_data);
+}
+
+
+static gboolean
+on_button_press_event (GtkWidget *widget _U_, GdkEventButton *event, gpointer user_data)
+{
+ struct sctp_udata *u_data = user_data;
+ sctp_graph_t *ios;
+ cairo_t *cr;
+
+ if (u_data->io->rectangle==TRUE)
+ {
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_rectangle (cr,
+ floor(MIN(u_data->io->x_old,u_data->io->x_new)),
+ floor(MIN(u_data->io->y_old,u_data->io->y_new)),
+ abs((long)(u_data->io->x_new-u_data->io->x_old)),
+ abs((long)(u_data->io->y_new-u_data->io->y_old)));
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_stroke (cr);
+ cairo_destroy (cr);
+
+
+ ios=(sctp_graph_t *)g_object_get_data(G_OBJECT(u_data->io->draw_area), "sctp_graph_t");
+ g_assert(ios != NULL);
+
+ cr = gdk_cairo_create (gtk_widget_get_window(u_data->io->draw_area));
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, ios->surface, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, ios->pixmap, 0, 0);
+#endif
+ cairo_rectangle (cr, 0, 0, abs((long)(u_data->io->x_new-u_data->io->x_old)), abs((long)(u_data->io->y_new-u_data->io->y_old)));
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+
+ sctp_graph_redraw(u_data);
+ }
+
+ u_data->io->x_old=event->x;
+ u_data->io->y_old=event->y;
+ if (u_data->io->y_old>u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset)
+ u_data->io->y_old=u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset;
+ if (u_data->io->x_old<LEFT_BORDER+u_data->io->offset)
+ u_data->io->x_old=LEFT_BORDER+u_data->io->offset;
+ u_data->io->rectangle=FALSE;
+
+ return TRUE;
+}
+
+
+static gboolean
+on_button_release_event (GtkWidget *widget _U_, GdkEventButton *event, gpointer user_data)
+{
+ struct sctp_udata *u_data = user_data;
+ sctp_graph_t *ios;
+ guint32 helpx, helpy, x1_tmp, x2_tmp, y_value;
+ gint label_width, label_height;
+ gdouble x_value, position, tfirst;
+ gint lwidth;
+ char label_string[30];
+ GList *tsnlist=NULL;
+ tsn_t *tsn, *tmptsn;
+ PangoLayout *layout;
+ GtkAllocation widget_alloc;
+ cairo_t *cr;
+
+ g_snprintf(label_string, 15, "%d", 0);
+ memcpy(label_string,(gchar *)g_locale_to_utf8(label_string, -1 , NULL, NULL, NULL), 15);
+ layout = gtk_widget_create_pango_layout(u_data->io->draw_area, label_string);
+ pango_layout_get_pixel_size(layout, &label_width, &label_height);
+
+ if (event->y > u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset)
+ event->y = u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset;
+ if (event->x < LEFT_BORDER+u_data->io->offset)
+ event->x = LEFT_BORDER+u_data->io->offset;
+
+ if (abs((long)(event->x-u_data->io->x_old))>10 || abs((long)(event->y-u_data->io->y_old))>10)
+ {
+ u_data->io->rect_x_min = (guint32) floor(MIN(u_data->io->x_old,event->x));
+ u_data->io->rect_x_max = (guint32) ceil(MAX(u_data->io->x_old,event->x));
+ u_data->io->rect_y_min = (guint32) floor(MIN(u_data->io->y_old,event->y));
+ u_data->io->rect_y_max = (guint32) ceil(MAX(u_data->io->y_old,event->y));
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_rectangle (cr,
+ u_data->io->rect_x_min+0.5,
+ u_data->io->rect_y_min+0.5,
+ u_data->io->rect_x_max - u_data->io->rect_x_min,
+ u_data->io->rect_y_max - u_data->io->rect_y_min);
+ cairo_set_line_width (cr, 1.0);
+ cairo_stroke (cr);
+ cairo_destroy (cr);
+
+ ios=(sctp_graph_t *)g_object_get_data(G_OBJECT(u_data->io->draw_area), "sctp_graph_t");
+ g_assert(ios != NULL);
+
+ cr = gdk_cairo_create (gtk_widget_get_window(u_data->io->draw_area));
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, ios->surface, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, ios->pixmap, 0, 0);
+#endif
+ gtk_widget_get_allocation(u_data->io->draw_area, &widget_alloc);
+ cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+
+ x1_tmp=(guint32) floor(u_data->io->min_x+((u_data->io->x_old-LEFT_BORDER-u_data->io->offset)*u_data->io->tmp_width/u_data->io->axis_width));
+ x2_tmp=(guint32) floor(u_data->io->min_x+((event->x-LEFT_BORDER-u_data->io->offset)*u_data->io->tmp_width/u_data->io->axis_width));
+ helpx=MIN(x1_tmp, x2_tmp);
+ if (helpx==x2_tmp)
+ {
+ x2_tmp=x1_tmp;
+ x1_tmp=helpx;
+ }
+ if (u_data->io->uoff)
+ {
+ if (x2_tmp - x1_tmp <= 1500)
+ u_data->io->uoff = FALSE;
+ u_data->io->x1_tmp_sec=(guint32)x1_tmp;
+ u_data->io->x1_tmp_usec=0;
+ u_data->io->x2_tmp_sec=(guint32)x2_tmp;
+ u_data->io->x2_tmp_usec=0;
+ }
+ else
+ {
+ u_data->io->x1_tmp_sec=(guint32)x1_tmp/1000000;
+ u_data->io->x1_tmp_usec=x1_tmp%1000000;
+ u_data->io->x2_tmp_sec=(guint32)x2_tmp/1000000;
+ u_data->io->x2_tmp_usec=x2_tmp%1000000;
+ }
+ u_data->io->x1_akt_sec = u_data->io->x1_tmp_sec;
+ u_data->io->x1_akt_usec = u_data->io->x1_tmp_usec;
+ u_data->io->x2_akt_sec = u_data->io->x2_tmp_sec;
+ u_data->io->x2_akt_usec = u_data->io->x2_tmp_usec;
+
+ u_data->io->y1_tmp=(guint32)((u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset-u_data->io->y_old)/u_data->io->y_interval);
+ u_data->io->y2_tmp=(guint32)((u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset-event->y)/u_data->io->y_interval);
+ helpy = MIN(u_data->io->y1_tmp, u_data->io->y2_tmp);
+ u_data->io->y2_tmp = MAX(u_data->io->y1_tmp, u_data->io->y2_tmp);
+ u_data->io->y1_tmp = helpy;
+ u_data->io->x_new=event->x;
+ u_data->io->y_new=event->y;
+ u_data->io->rectangle=TRUE;
+ u_data->io->rectangle_present=TRUE;
+ }
+ else
+ {
+ if (u_data->io->rectangle_present==TRUE)
+ {
+ u_data->io->rectangle_present=FALSE;
+ if (event->x >= u_data->io->rect_x_min && event->x <= u_data->io->rect_x_max &&
+ event->y >= u_data->io->rect_y_min && event->y <= u_data->io->rect_y_max)
+ zoomin_bt_fcn(u_data);
+ else
+ {
+ u_data->io->x1_tmp_sec = u_data->io->x1_akt_sec;
+ u_data->io->x1_tmp_usec = u_data->io->x1_akt_usec;
+ u_data->io->x2_tmp_sec = u_data->io->x2_akt_sec;
+ u_data->io->x2_tmp_usec = u_data->io->x2_akt_usec;
+ sctp_graph_redraw(u_data);
+ }
+ }
+ else if (label_set)
+ {
+ label_set = FALSE;
+ sctp_graph_redraw(u_data);
+ }
+ else
+ {
+ x_value = ((event->x-LEFT_BORDER-u_data->io->offset) * ((u_data->io->x2_tmp_sec+u_data->io->x2_tmp_usec/1000000.0)-(u_data->io->x1_tmp_sec+u_data->io->x1_tmp_usec/1000000.0)) / (u_data->io->surface_width-LEFT_BORDER-RIGHT_BORDER-u_data->io->offset))+u_data->io->x1_tmp_sec+u_data->io->x1_tmp_usec/1000000.0;
+ y_value = (guint32) floor((u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset-event->y) * (max_tsn - min_tsn) / (u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset)) + min_tsn;
+
+ if (u_data->dir == 1)
+ tsnlist = g_list_last(u_data->assoc->tsn1);
+ else
+ tsnlist = g_list_last(u_data->assoc->tsn2);
+
+ tsn = (tsn_t*) (tsnlist->data);
+ tmptsn =(tsn_t*)(tsnlist->data);
+ tfirst = tsn->secs + tsn->usecs/1000000.0;
+
+ while (tsnlist)
+ {
+ tsnlist = g_list_previous(tsnlist);
+ tsn = (tsn_t*) (tsnlist->data);
+ if (tsn->secs+tsn->usecs/1000000.0<x_value)
+ {
+ tfirst = tsn->secs+tsn->usecs/1000000.0;
+ tmptsn =tsn;
+ }
+ else
+ {
+ if ((tfirst+tsn->secs+tsn->usecs/1000000.0)/2.0<x_value)
+ {
+ x_value = tsn->secs+tsn->usecs/1000000.0;
+ tmptsn = tsn;
+ }
+ else
+ x_value = tmptsn->secs+tmptsn->usecs/1000000.0;
+ break;
+ }
+ }
+ cf_goto_frame(&cfile, tmptsn->frame_number);
+ g_snprintf(label_string, sizeof(label_string), "(%.6f, %u)", x_value, y_value);
+ label_set = TRUE;
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr,
+ (event->x-2)+0.5,
+ (event->y)+0.5);
+ cairo_line_to(cr,
+ (event->x+2)+0.5,
+ (event->y)+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr,
+ (event->x)+0.5,
+ (event->y-2)+0.5);
+ cairo_line_to(cr,
+ (event->x)+0.5,
+ (event->y+2)+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ if (event->x+150>=u_data->io->surface_width)
+ position = event->x - 150;
+ else
+ position = event->x + 5;
+
+ memcpy(label_string,(gchar *)g_locale_to_utf8(label_string, -1 , NULL, NULL, NULL), 15);
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_move_to (cr,
+ position,
+ event->y-10);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+
+ ios=(sctp_graph_t *)g_object_get_data(G_OBJECT(u_data->io->draw_area), "sctp_graph_t");
+ g_assert(ios != NULL);
+
+ cr = gdk_cairo_create (gtk_widget_get_window(u_data->io->draw_area));
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, ios->surface, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, ios->pixmap, 0, 0);
+#endif
+ gtk_widget_get_allocation(u_data->io->draw_area, &widget_alloc);
+ cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+
+ }
+ }
+ g_object_unref(G_OBJECT(layout));
+ return TRUE;
+}
+
+static void init_sctp_graph_window(struct sctp_udata *u_data)
+{
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *bt_close, *sack_bt, *tsn_bt, *both_bt, *zoomin_bt;
+
+ /* create the main window */
+
+ u_data->io->window = dlg_window_new("SCTP Graphics"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(u_data->io->window), TRUE);
+
+ vbox=gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(u_data->io->window), vbox);
+ gtk_widget_show(vbox);
+
+ create_draw_area(vbox, u_data);
+
+ sctp_graph_set_title(u_data);
+
+ hbox = gtk_hbutton_box_new();
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 10);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_SPREAD);
+ gtk_box_set_spacing(GTK_BOX (hbox), 0);
+ gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
+ gtk_widget_show(hbox);
+
+ sack_bt = gtk_button_new_with_label ("Adv. Rec. Window");
+ gtk_box_pack_start(GTK_BOX(hbox), sack_bt, FALSE, FALSE, 0);
+ gtk_widget_show(sack_bt);
+
+ g_signal_connect(sack_bt, "clicked", G_CALLBACK(on_sack_bt), u_data);
+
+ tsn_bt = gtk_button_new_with_label ("Data bytes sent");
+ gtk_box_pack_start(GTK_BOX(hbox), tsn_bt, FALSE, FALSE, 0);
+ gtk_widget_show(tsn_bt);
+ g_signal_connect(tsn_bt, "clicked", G_CALLBACK(on_tsn_bt), u_data);
+
+ both_bt = gtk_button_new_with_label ("Show both");
+ gtk_box_pack_start(GTK_BOX(hbox), both_bt, FALSE, FALSE, 0);
+ gtk_widget_show(both_bt);
+ g_signal_connect(both_bt, "clicked", G_CALLBACK(on_both_bt), u_data);
+
+ zoomin_bt = gtk_button_new_with_label ("Zoom in");
+ gtk_box_pack_start(GTK_BOX(hbox), zoomin_bt, FALSE, FALSE, 0);
+ gtk_widget_show(zoomin_bt);
+ g_signal_connect(zoomin_bt, "clicked", G_CALLBACK(on_zoomin_bt), u_data);
+ gtk_widget_set_tooltip_text(zoomin_bt, "Zoom in the area you have selected");
+
+ zoomout_bt = gtk_button_new_with_label ("Zoom out");
+ gtk_box_pack_start(GTK_BOX(hbox), zoomout_bt, FALSE, FALSE, 0);
+ gtk_widget_show(zoomout_bt);
+ g_signal_connect(zoomout_bt, "clicked", G_CALLBACK(on_zoomout_bt), u_data);
+ gtk_widget_set_tooltip_text(zoomout_bt, "Zoom out one step");
+ gtk_widget_set_sensitive(zoomout_bt, FALSE);
+
+ bt_close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_box_pack_start(GTK_BOX(hbox), bt_close, FALSE, FALSE, 0);
+ gtk_widget_show(bt_close);
+ g_signal_connect(bt_close, "clicked", G_CALLBACK(sctp_graph_close_cb), u_data);
+
+ g_signal_connect(u_data->io->draw_area,"button_press_event",G_CALLBACK(on_button_press_event), u_data);
+ g_signal_connect(u_data->io->draw_area,"button_release_event",G_CALLBACK(on_button_release_event), u_data);
+ gtk_widget_set_events(u_data->io->draw_area, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_EXPOSURE_MASK);
+ /* dlg_set_cancel(u_data->io->window, bt_close); */
+
+ gtk_widget_show(u_data->io->window);
+}
+
+static void sctp_graph_set_title(struct sctp_udata *u_data)
+{
+ char *title;
+
+ if(!u_data->io->window)
+ {
+ return;
+ }
+ title = g_strdup_printf("SCTP Data and Adv.Rcv.Window over Time: %s Port1 %u Port2 %u Endpoint %u",
+ cf_get_display_name(&cfile), u_data->parent->assoc->port1, u_data->parent->assoc->port2, u_data->dir);
+ gtk_window_set_title(GTK_WINDOW(u_data->io->window), title);
+ g_free(title);
+}
+
+
+static void
+gtk_sctpgraph_init(struct sctp_udata *u_data)
+{
+ sctp_graph_t *io;
+ sctp_min_max_t* tmp_minmax;
+
+ io=g_malloc(sizeof(sctp_graph_t));
+ io->needs_redraw=TRUE;
+ io->x_interval=1000;
+ io->window=NULL;
+ io->draw_area=NULL;
+#if GTK_CHECK_VERSION(2,22,0)
+ io->surface=NULL;
+#else
+ io->pixmap=NULL;
+#endif
+ io->surface_width=800;
+ io->surface_height=600;
+ io->graph_type=0;
+ u_data->io=io;
+
+
+ u_data->io->x1_tmp_sec=u_data->assoc->min_secs;
+ u_data->io->x1_tmp_usec=u_data->assoc->min_usecs;
+ u_data->io->x2_tmp_sec=u_data->assoc->max_secs;
+ u_data->io->x2_tmp_usec=u_data->assoc->max_usecs;
+ u_data->io->tmp_min_tsn1=0;
+ u_data->io->tmp_max_tsn1=u_data->assoc->max_bytes1;
+ u_data->io->tmp_min_tsn2=0;
+ u_data->io->tmp_max_tsn2=u_data->assoc->max_bytes2;
+ u_data->io->tmp=FALSE;
+ tmp_minmax = g_malloc(sizeof(sctp_min_max_t));
+ tmp_minmax->tmp_min_secs = u_data->assoc->min_secs;
+ tmp_minmax->tmp_min_usecs=u_data->assoc->min_usecs;
+ tmp_minmax->tmp_max_secs=u_data->assoc->max_secs;
+ tmp_minmax->tmp_max_usecs=u_data->assoc->max_usecs;
+ tmp_minmax->tmp_min_tsn2=u_data->io->tmp_min_tsn2;
+ tmp_minmax->tmp_min_tsn1=u_data->io->tmp_min_tsn1;
+ tmp_minmax->tmp_max_tsn1=u_data->io->tmp_max_tsn1;
+ tmp_minmax->tmp_max_tsn2=u_data->io->tmp_max_tsn2;
+ u_data->assoc->min_max = g_slist_prepend(u_data->assoc->min_max, tmp_minmax);
+
+ /* build the GUI */
+ init_sctp_graph_window(u_data);
+ sctp_graph_redraw(u_data);
+}
+
+
+static void
+quit(GObject *object _U_, gpointer user_data)
+{
+ struct sctp_udata *u_data=user_data;
+
+ decrease_childcount(u_data->parent);
+ remove_child(u_data, u_data->parent);
+ g_free(u_data->io);
+ u_data->assoc->min_max = NULL;
+ g_free(u_data);
+}
+
+
+static void create_draw_area(GtkWidget *box, struct sctp_udata *u_data)
+{
+ u_data->io->draw_area=gtk_drawing_area_new();
+ g_object_set_data(G_OBJECT(u_data->io->draw_area), "sctp_graph_t", u_data->io);
+ g_signal_connect(u_data->io->draw_area, "destroy", G_CALLBACK(quit), u_data);
+
+ gtk_widget_set_size_request(u_data->io->draw_area, u_data->io->surface_width, u_data->io->surface_height);
+
+ /* signals needed to handle backing pixmap */
+#if GTK_CHECK_VERSION(3,0,0)
+ g_signal_connect(u_data->io->draw_area, "draw", G_CALLBACK(on_draw_area_draw_event), u_data->io);
+#else
+ g_signal_connect(u_data->io->draw_area, "expose_event", G_CALLBACK(on_expose_event), u_data->io);
+#endif
+ g_signal_connect(u_data->io->draw_area, "configure_event", G_CALLBACK(on_configure_event), u_data);
+
+ gtk_widget_show(u_data->io->draw_area);
+ gtk_box_pack_start(GTK_BOX(box), u_data->io->draw_area, TRUE, TRUE, 0);
+}
+
+static void insertion(GPtrArray *array, guint32 N)
+{
+ guint32 i, j;
+ guint32 v;
+ struct tsn_sort *help=NULL;
+
+ for (i=1; i<N; i++)
+ {
+ v = ((struct tsn_sort*)(g_ptr_array_index(array,i)))->tsnumber;
+ j=i;
+ while (j>=1 && ((struct tsn_sort*)(g_ptr_array_index(array, j-1)))->tsnumber > v)
+ {
+ help=g_ptr_array_index(array, j);
+ g_ptr_array_index(array, j)=g_ptr_array_index(array, j-1);
+ g_ptr_array_index(array, j-1)=help;
+ j--;
+ }
+ ((struct tsn_sort*)(g_ptr_array_index(array, j)))->tsnumber=v;
+ }
+}
+
+static void set_arw_offsets(struct sctp_udata *u_data)
+{
+ GPtrArray *s_array=NULL, *t_array=NULL;
+ guint32 i, j=0;
+
+ if (u_data->dir==1 && u_data->assoc->n_sack_chunks_ep1>0)
+ {
+ s_array=u_data->assoc->sort_sack1;
+ t_array=u_data->assoc->sort_tsn1;
+ insertion(s_array,u_data->assoc->n_sack_chunks_ep1);
+
+ for (i=0; i<u_data->assoc->n_sack_chunks_ep1; i++)
+ {
+ while (((struct tsn_sort*)(g_ptr_array_index(s_array, i)))->tsnumber > ((struct tsn_sort*)(g_ptr_array_index(t_array, j)))->tsnumber)
+ {
+ j++;
+ }
+ ((struct tsn_sort*)(g_ptr_array_index(s_array,i)))->offset = ((struct tsn_sort*)(g_ptr_array_index(t_array, j)))->offset
+ + ((struct tsn_sort*)(g_ptr_array_index(t_array, j)))->length;
+ }
+
+ u_data->assoc->sort_sack1=s_array;
+ }
+
+ if (u_data->dir==2 && u_data->assoc->n_sack_chunks_ep2>0)
+ {
+ s_array=u_data->assoc->sort_sack2;
+ t_array=u_data->assoc->sort_tsn2;
+ insertion(s_array,u_data->assoc->n_sack_chunks_ep2);
+ j=0;
+ for (i=0; i<u_data->assoc->n_sack_chunks_ep2; i++)
+ {
+ while (((struct tsn_sort*)(g_ptr_array_index(s_array, i)))->tsnumber > ((struct tsn_sort*)(g_ptr_array_index(t_array,j)))->tsnumber)
+ {
+ j++;
+ }
+ ((struct tsn_sort*)(g_ptr_array_index(s_array, i)))->offset = ((struct tsn_sort*)(g_ptr_array_index(t_array, j)))->offset
+ + ((struct tsn_sort*)(g_ptr_array_index(t_array, j)))->length;
+ }
+ u_data->assoc->sort_sack2=s_array;
+ }
+}
+
+static void compute_offsets(struct sctp_udata *u_data)
+{
+ struct tsn_sort t_sort;
+ GPtrArray *array=NULL;
+ guint32 i;
+ guint32 sum=0;
+ guint32 tsntmp=0;
+
+ if (u_data->dir==1 && u_data->assoc->n_array_tsn1>0)
+ {
+ array=u_data->assoc->sort_tsn1;
+ insertion(array,u_data->assoc->n_array_tsn1);
+
+ for (i=0; i<u_data->assoc->n_array_tsn1; i++)
+ {
+ ((struct tsn_sort*)(g_ptr_array_index(array, i)))->offset=sum;
+ t_sort.tsnumber=((struct tsn_sort*)(g_ptr_array_index(array, i)))->tsnumber;
+ if (t_sort.tsnumber>tsntmp)
+ sum+=((struct tsn_sort*)(g_ptr_array_index(array, i)))->length;
+ tsntmp=t_sort.tsnumber;
+ }
+ u_data->assoc->max_bytes1= ((struct tsn_sort*)(g_ptr_array_index(array, i-1)))->offset + ((struct tsn_sort*)(g_ptr_array_index(array, i-1)))->length;
+ u_data->assoc->sort_tsn1=array;
+ }
+ if (u_data->dir==2 && u_data->assoc->n_array_tsn2>0)
+ {
+ sum=0;
+ array=u_data->assoc->sort_tsn2;
+ insertion(array,u_data->assoc->n_array_tsn2);
+
+ for (i=0; i<u_data->assoc->n_array_tsn2; i++)
+ {
+ ((struct tsn_sort*)(g_ptr_array_index(array,i)))->offset=sum;
+ t_sort.tsnumber=((struct tsn_sort*)(g_ptr_array_index(array, i)))->tsnumber;
+ if (t_sort.tsnumber>tsntmp)
+ sum+=((struct tsn_sort*)(g_ptr_array_index(array, i)))->length;
+ tsntmp=t_sort.tsnumber;
+ }
+
+ u_data->assoc->max_bytes2= ((struct tsn_sort*)(g_ptr_array_index(array, u_data->assoc->n_data_chunks_ep2-1)))->offset + ((struct tsn_sort*)(g_ptr_array_index(array, u_data->assoc->n_data_chunks_ep2-1)))->length;
+ u_data->assoc->sort_tsn2=array;
+ }
+}
+
+void create_byte_graph(guint16 dir, struct sctp_analyse* userdata)
+{
+ struct sctp_udata *u_data;
+
+ u_data=g_malloc(sizeof(struct sctp_udata));
+ u_data->assoc=g_malloc(sizeof(sctp_assoc_info_t));
+ u_data->assoc=userdata->assoc;
+ u_data->io=NULL;
+ u_data->dir = dir;
+ u_data->parent = userdata;
+ if ((u_data->dir==1 && (u_data->assoc->n_array_tsn1==0))|| (u_data->dir==2 && (u_data->assoc->n_array_tsn2==0)))
+ simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK, "No Data Chunks sent");
+ else
+ {
+ set_child(u_data, u_data->parent);
+ increase_childcount(u_data->parent);
+ compute_offsets(u_data);
+ set_arw_offsets(u_data);
+ gtk_sctpgraph_init(u_data);
+ }
+
+}
+
diff --git a/ui/gtk/sctp_chunk_stat.c b/ui/gtk/sctp_chunk_stat.c
new file mode 100644
index 0000000000..6c7d86c7c0
--- /dev/null
+++ b/ui/gtk/sctp_chunk_stat.c
@@ -0,0 +1,339 @@
+/* sctp_chunk_stat.c
+ * SCTP chunk counter for Wireshark
+ * Copyright 2005 Oleg Terletsky <oleg.terletsky [AT] comverse.com>
+ * Copyright 2009 Varun Notibala <nbvarun [AT] gmail.com>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/value_string.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-sctp.h>
+
+#include "../simple_dialog.h"
+#include "../file.h"
+#include "../globals.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_util.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/sctp_stat.h"
+
+
+static void sctpstat_init(const char *optarg, void *userdata);
+
+static tap_param sctp_stat_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg sctp_stat_dlg = {
+ "SCTP Statistics",
+ "sctp,stat",
+ sctpstat_init,
+ -1,
+ G_N_ELEMENTS(sctp_stat_params),
+ sctp_stat_params
+};
+
+typedef struct sctp_ep {
+ struct sctp_ep* next;
+ address src;
+ address dst;
+ guint16 sport;
+ guint16 dport;
+ guint32 chunk_count[256];
+} sctp_ep_t;
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _sctp_stat_t {
+ GtkWidget *win;
+ GtkWidget *vbox;
+ char *filter;
+ GtkWidget *scrolled_window;
+ GtkTreeView *table;
+ guint32 number_of_packets;
+ sctp_ep_t* ep_list;
+} sctpstat_t;
+
+typedef struct _sctp_info sctp_into_t;
+
+#define CHUNK_TYPE(x)(tvb_get_guint8((x), CHUNK_TYPE_OFFSET))
+
+static void
+sctpstat_reset(void *phs)
+{
+ sctpstat_t* sctp_stat = (sctpstat_t *)phs;
+ sctp_ep_t* list = (sctp_ep_t*)sctp_stat->ep_list;
+ sctp_ep_t* tmp = NULL;
+ guint16 chunk_type;
+
+ if(!list)
+ return;
+
+ for(tmp = list; tmp ; tmp=tmp->next)
+ for(chunk_type = 0; chunk_type < 256; chunk_type++)
+ tmp->chunk_count[chunk_type] = 0;
+
+ sctp_stat->number_of_packets = 0;
+}
+
+static sctp_ep_t* alloc_sctp_ep(struct _sctp_info *si)
+{
+ sctp_ep_t* ep;
+ guint16 chunk_type;
+
+ if(!si)
+ return NULL;
+
+ if (!(ep = g_malloc(sizeof(sctp_ep_t))))
+ return NULL;
+
+ COPY_ADDRESS(&ep->src,&si->ip_src);
+ COPY_ADDRESS(&ep->dst,&si->ip_dst);
+ ep->sport = si->sport;
+ ep->dport = si->dport;
+ ep->next = NULL;
+ for(chunk_type = 0; chunk_type < 256; chunk_type++)
+ ep->chunk_count[chunk_type] = 0;
+ return ep;
+}
+
+static int
+sctpstat_packet(void *phs, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *phi)
+{
+
+ sctpstat_t *hs=(sctpstat_t *)phs;
+ sctp_ep_t *tmp = NULL, *te = NULL;
+ struct _sctp_info *si = (struct _sctp_info *) phi;
+ guint32 tvb_number;
+
+ if (!hs)
+ return (0);
+
+ hs->number_of_packets++;
+ if(!hs->ep_list) {
+ hs->ep_list = alloc_sctp_ep(si);
+ te = hs->ep_list;
+ } else {
+ for(tmp=hs->ep_list ; tmp ; tmp=tmp->next) {
+ if((!CMP_ADDRESS(&tmp->src,&si->ip_src)) &&
+ (!CMP_ADDRESS(&tmp->dst,&si->ip_dst)) &&
+ (tmp->sport == si->sport) &&
+ (tmp->dport == si->dport)) {
+ te = tmp;
+ break;
+ }
+ }
+ if(!te) {
+ if ((te = alloc_sctp_ep(si))) {
+ te->next = hs->ep_list;
+ hs->ep_list = te;
+ }
+ }
+ }
+
+ if(!te)
+ return (0);
+
+
+ if (si->number_of_tvbs > 0) {
+ for(tvb_number = 0; tvb_number < si->number_of_tvbs; tvb_number++) {
+ if (IS_SCTP_CHUNK_TYPE(CHUNK_TYPE(si->tvb[tvb_number])))
+ (te->chunk_count[CHUNK_TYPE(si->tvb[tvb_number])])++;
+ else
+ (te->chunk_count[OTHER_CHUNKS_INDEX])++;
+ }
+ }
+ return (1);
+}
+
+
+static void
+sctpstat_draw(void *phs)
+{
+ sctpstat_t *hs=(sctpstat_t *)phs;
+ sctp_ep_t* list = hs->ep_list, *tmp;
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ /* Now print Message and Reason Counter Table */
+ /* clear list before printing */
+ /* XXX use an iter for new/modified ? */
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(hs->table));
+ gtk_list_store_clear(store);
+
+ for(tmp = list ; tmp ; tmp=tmp->next) {
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter,
+ 0, ep_address_to_str(&tmp->src),
+ 1, tmp->sport,
+ 2, ep_address_to_str(&tmp->dst),
+ 3, tmp->dport,
+ 4, tmp->chunk_count[SCTP_DATA_CHUNK_ID],
+ 5, tmp->chunk_count[SCTP_SACK_CHUNK_ID],
+ 6, tmp->chunk_count[SCTP_HEARTBEAT_CHUNK_ID],
+ 7, tmp->chunk_count[SCTP_HEARTBEAT_ACK_CHUNK_ID],
+ 8, tmp->chunk_count[SCTP_INIT_CHUNK_ID],
+ 9, tmp->chunk_count[SCTP_INIT_ACK_CHUNK_ID],
+ 10, tmp->chunk_count[SCTP_COOKIE_ECHO_CHUNK_ID],
+ 11, tmp->chunk_count[SCTP_COOKIE_ACK_CHUNK_ID],
+ 12, tmp->chunk_count[SCTP_ABORT_CHUNK_ID],
+ 13, tmp->chunk_count[SCTP_ERROR_CHUNK_ID],
+ 14, tmp->chunk_count[SCTP_NR_SACK_CHUNK_ID],
+ 15, tmp->chunk_count[SCTP_ASCONF_ACK_CHUNK_ID],
+ 16, tmp->chunk_count[SCTP_PKTDROP_CHUNK_ID],
+ 17, tmp->chunk_count[SCTP_FORWARD_TSN_CHUNK_ID],
+ 18, tmp->chunk_count[SCTP_ASCONF_CHUNK_ID],
+ 19, tmp->chunk_count[OTHER_CHUNKS_INDEX],
+ -1
+ );
+ }
+}
+
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ sctpstat_t *hs=(sctpstat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(hs);
+ unprotect_thread_critical_region();
+
+ if(hs->filter){
+ g_free(hs->filter);
+ hs->filter=NULL;
+ }
+ g_free(hs);
+}
+
+
+static const stat_column titles[]={
+ {G_TYPE_STRING, LEFT, "Source IP" },
+ {G_TYPE_UINT, RIGHT, "Source Port" },
+ {G_TYPE_STRING, LEFT, "Dest IP" },
+ {G_TYPE_UINT, RIGHT, "Dest Port" },
+ {G_TYPE_UINT, RIGHT, "DATA" },
+ {G_TYPE_UINT, RIGHT, "SACK" },
+ {G_TYPE_UINT, RIGHT, "HBEAT" },
+ {G_TYPE_UINT, RIGHT, "HBEAT-ACK" },
+ {G_TYPE_UINT, RIGHT, "INIT" },
+ {G_TYPE_UINT, RIGHT, "INIT-ACK" },
+ {G_TYPE_UINT, RIGHT, "COOKIE" },
+ {G_TYPE_UINT, RIGHT, "COOKIE-ACK" },
+ {G_TYPE_UINT, RIGHT, "ABORT" },
+ {G_TYPE_UINT, RIGHT, "ERROR" },
+ {G_TYPE_UINT, RIGHT, "NR-SACK" },
+ {G_TYPE_UINT, RIGHT, "ASCONF-ACK" },
+ {G_TYPE_UINT, RIGHT, "PKTDROP" },
+ {G_TYPE_UINT, RIGHT, "FORWARD-TSN" },
+ {G_TYPE_UINT, RIGHT, "ASCONF" },
+ {G_TYPE_UINT, RIGHT, "Others" }
+};
+
+static void
+sctpstat_init(const char *optarg, void *userdata _U_)
+{
+ sctpstat_t *hs;
+ GString *error_string;
+ GtkWidget *bbox;
+ GtkWidget *close_bt;
+
+ hs=g_malloc(sizeof(sctpstat_t));
+ if(strncmp(optarg,"sctp,stat,",10) == 0){
+ hs->filter=g_strdup(optarg+10);
+ } else {
+ hs->filter=NULL;
+ }
+ hs->ep_list = NULL;
+ hs->number_of_packets = 0;
+ sctpstat_reset(hs);
+
+ hs->win = dlg_window_new("Wireshark: SCTP Chunk Statistics"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(hs->win), TRUE);
+ gtk_window_set_default_size(GTK_WINDOW(hs->win), 700, 250);
+
+ hs->vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(hs->vbox), 12);
+
+ init_main_stat_window(hs->win, hs->vbox, "SCTP Chunk Counter", hs->filter);
+
+ /* init a scrolled window*/
+ hs->scrolled_window = scrolled_window_new(NULL, NULL);
+
+ hs->table = create_stat_table(hs->scrolled_window, hs->vbox, 20, titles);
+
+ error_string=register_tap_listener("sctp", hs, hs->filter, 0,
+ sctpstat_reset,
+ sctpstat_packet,
+ sctpstat_draw);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(hs->filter);
+ g_free(hs);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_end(GTK_BOX(hs->vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(hs->win, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(hs->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(hs->win, "destroy", G_CALLBACK(win_destroy_cb), hs);
+
+ gtk_widget_show_all(hs->win);
+ window_present(hs->win);
+
+ cf_retap_packets(&cfile);
+}
+
+void
+register_tap_listener_sctpstat(void)
+{
+ register_dfilter_stat(&sctp_stat_dlg, "S_CTP/Chunk Counter",
+ REGISTER_STAT_GROUP_TELEPHONY);
+}
+
+void sctp_chunk_counter_cb(GtkAction *action _U_, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &sctp_stat_dlg);
+}
+
diff --git a/ui/gtk/sctp_chunk_stat_dlg.c b/ui/gtk/sctp_chunk_stat_dlg.c
new file mode 100644
index 0000000000..a44741de68
--- /dev/null
+++ b/ui/gtk/sctp_chunk_stat_dlg.c
@@ -0,0 +1,812 @@
+/*
+ * Copyright 2004, Irene Ruengeler <i.ruengeler [AT] fh-muenster.de>
+ * Copyright 2009, Varun Notibala <nbvarun [AT] gmail.com>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/filesystem.h"
+
+#include "../globals.h"
+
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/sctp_stat.h"
+
+static GtkWidget *clist = NULL;
+static GList *last_list = NULL;
+static sctp_assoc_info_t* selected_stream = NULL; /* current selection */
+
+#define NUM_COLS 14
+#define FRAME_LIMIT 8
+
+enum chunk_types {
+ DATA = 0,
+ INIT = 1,
+ INIT_ACK = 2,
+ SACK = 3,
+ HEARTBEAT = 4,
+ HEARTBEAT_ACK = 5,
+ ABORT = 6,
+ SHUTDOWN = 7,
+ SHUTDOWN_ACK = 8,
+ SCTP_ERROR = 9,
+ COOKIE_ECHO = 10,
+ COOKIE_ACK = 11,
+ ECNE = 12,
+ CWR = 13,
+ SHUT_COMPLETE = 14,
+ AUTH = 15,
+ NR_SACK = 16,
+ ASCONF_ACK = 0x80,
+ PKTDROP = 0x81,
+ FORWARD_TSN = 0xC0,
+ ASCONF = 0xC1
+};
+enum
+{
+ IP_ADDR_COLUMN,
+ DATA_COLUMN,
+ INIT_COLUMN,
+ INIT_ACK_COLUMN,
+ SACK_COLUMN,
+ HEARTBEAT_COLUMN,
+ HEARTBEAT_ACK_COLUMN,
+ ABORT_COLUMN,
+ SHUTDOWN_COLUMN,
+ SHUTDOWN_ACK_COLUMN,
+ ERROR_COLUMN,
+ COOKIE_ECHO_COLUMN,
+ COOKIE_ACK_COLUMN,
+ ECNE_COLUMN,
+ CWR_COLUMN,
+ SHUT_COMPLETE_COLUMN,
+ AUTH_COLUMN,
+ NR_SACK_COLUMN,
+ ASCONF_ACK_COLUMN,
+ PKTDROP_COLUMN,
+ FORWARD_TSN_COLUMN,
+ ASCONF_COLUMN,
+ OTHERS_COLUMN,
+ N_COLUMN /* The number of columns */
+};
+
+/* Create list */
+static
+GtkWidget* create_list(void)
+{
+
+ GtkListStore *list_store;
+ GtkWidget *list;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSortable *sortable;
+ GtkTreeView *list_view;
+ GtkTreeSelection *selection;
+
+ /* Create the store */
+ list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX */
+ G_TYPE_STRING, /* IP Address */
+ G_TYPE_INT, /* DATA */
+ G_TYPE_INT, /* INIT */
+ G_TYPE_INT, /* INIT_ACK */
+ G_TYPE_INT, /* SACK */
+ G_TYPE_INT, /* HEARTBEAT */
+ G_TYPE_INT, /* HEARTBEAT_ACK */
+ G_TYPE_INT, /* ABORT */
+ G_TYPE_INT, /* SHUTDOWN */
+ G_TYPE_INT, /* SHUTDOWN_ACK */
+ G_TYPE_INT, /* ERROR */
+ G_TYPE_INT, /* COOKIE_ECHO */
+ G_TYPE_INT, /* COOKIE_ACK */
+ G_TYPE_INT, /* ECNE */
+ G_TYPE_INT, /* CWR */
+ G_TYPE_INT, /* SHUT_COMPLETE */
+ G_TYPE_INT, /* AUTH */
+ G_TYPE_INT, /* NR_SACK */
+ G_TYPE_INT, /* ASCONF_ACK */
+ G_TYPE_INT, /* PKTDROP */
+ G_TYPE_INT, /* FORWARD_TSN */
+ G_TYPE_INT, /* ASCONF */
+ G_TYPE_INT); /* Others */
+ /* Create a view */
+ list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
+
+ list_view = GTK_TREE_VIEW(list);
+ sortable = GTK_TREE_SORTABLE(list_store);
+
+ /* Speed up the list display */
+ gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
+
+ /* Setup the sortable columns */
+ gtk_tree_sortable_set_sort_column_id(sortable, IP_ADDR_COLUMN, GTK_SORT_ASCENDING);
+ gtk_tree_view_set_headers_clickable(list_view, FALSE);
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref (G_OBJECT (list_store));
+
+ /*
+ * Create the first column packet, associating the "text" attribute of the
+ * cell_renderer to the first column of the model
+ */
+ /* 1:st column */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("IP Address", renderer,
+ "text", IP_ADDR_COLUMN,
+ NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, IP_ADDR_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 135);
+
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 2:nd column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("DATA", renderer,
+ "text", DATA_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, DATA_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 70);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 3:d column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("INIT", renderer,
+ "text", INIT_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, INIT_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 70);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 4:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("INIT-ACK", renderer,
+ "text", INIT_ACK_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, INIT_ACK_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 90);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 5:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("SACK", renderer,
+ "text", SACK_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, SACK_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 70);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 6:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("HEARTBEAT", renderer,
+ "text", HEARTBEAT_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, HEARTBEAT_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 110);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 7:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("HEARTBEAT-ACK", renderer,
+ "text", HEARTBEAT_ACK_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, HEARTBEAT_ACK_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 140);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 8:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("ABORT", renderer,
+ "text", ABORT_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, ABORT_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_append_column (list_view, column);
+
+
+ /* 9:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("SHUTDOWN", renderer,
+ "text", SHUTDOWN_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, SHUTDOWN_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 120);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 10:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("SHUTDOWN-ACK", renderer,
+ "text", SHUTDOWN_ACK_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, SHUTDOWN_ACK_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 150);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 11:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("ERROR", renderer,
+ "text", ERROR_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, ERROR_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 12:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("COOKIE-ECHO", renderer,
+ "text", COOKIE_ECHO_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, COOKIE_ECHO_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 130);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 13:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("COOKIE-ACK", renderer,
+ "text", COOKIE_ACK_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, COOKIE_ACK_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 130);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 14:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("ECNE", renderer,
+ "text", ECNE_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, ECNE_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 70);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 15:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("CWR", renderer,
+ "text", CWR_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, CWR_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 70);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 16:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("SHUT-COMPLETE", renderer,
+ "text", SHUT_COMPLETE_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, SHUT_COMPLETE_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 150);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 17:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("AUTH", renderer,
+ "text", AUTH_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, AUTH_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 18:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("NR-SACK", renderer,
+ "text", NR_SACK_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, NR_SACK_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 19:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("ASCONF-ACK", renderer,
+ "text", ASCONF_ACK_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, ASCONF_ACK_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 120);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 20:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("PKTDROP", renderer,
+ "text", PKTDROP_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, PKTDROP_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 21:st column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("FORWARD-TSN", renderer,
+ "text", FORWARD_TSN_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, FORWARD_TSN_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 140);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 22:nd column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("ASCONF", renderer,
+ "text", ASCONF_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, ASCONF_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 90);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 23:rd column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Others", renderer,
+ "text", OTHERS_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, OTHERS_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Now enable the sorting of each column */
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
+
+ /* Setup the selection handler */
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+
+ return list;
+
+}
+
+static const char *
+chunk_name(int type)
+{
+#define CASE(x) case x: s=#x; break
+ const char *s = "unknown";
+ switch (type)
+ {
+ CASE(DATA);
+ CASE(INIT);
+ CASE(INIT_ACK);
+ CASE(SACK);
+ CASE(HEARTBEAT);
+ CASE(HEARTBEAT_ACK);
+ CASE(ABORT);
+ CASE(SHUTDOWN);
+ CASE(SHUTDOWN_ACK);
+ CASE(SCTP_ERROR);
+ CASE(COOKIE_ECHO);
+ CASE(COOKIE_ACK);
+ CASE(ECNE);
+ CASE(CWR);
+ CASE(SHUT_COMPLETE);
+ CASE(AUTH);
+ CASE(NR_SACK);
+ CASE(ASCONF_ACK);
+ CASE(PKTDROP);
+ CASE(FORWARD_TSN);
+ CASE(ASCONF);
+ }
+ return s;
+}
+
+typedef struct column_arrows {
+ GtkWidget *table;
+ GtkWidget *ascend_pm;
+ GtkWidget *descend_pm;
+} column_arrows;
+
+
+static void
+chunk_dlg_destroy(GObject *object _U_, gpointer user_data)
+{
+ struct sctp_udata *u_data=(struct sctp_udata*)user_data;
+ decrease_childcount(u_data->parent);
+ remove_child(u_data, u_data->parent);
+ g_free(u_data->io);
+ g_free(u_data);
+}
+
+static void
+on_destroy(GObject *object _U_, gpointer user_data)
+{
+ struct sctp_udata *u_data=(struct sctp_udata*)user_data;
+ decrease_childcount(u_data->parent);
+ remove_child(u_data, u_data->parent);
+ g_free(u_data->io);
+ g_free(u_data);
+}
+
+
+static void
+add_to_clist(sctp_addr_chunk* sac)
+{
+ GtkListStore *list_store = NULL;
+ GtkTreeIter iter;
+ gchar field[1][MAX_ADDRESS_LEN];
+
+ list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (clist))); /* Get store */
+
+ if (sac->addr->type==AT_IPv4) {
+ g_snprintf(field[0], MAX_ADDRESS_LEN, "%s", ip_to_str((const guint8 *)(sac->addr->data)));
+ } else if (sac->addr->type==AT_IPv6) {
+ g_snprintf(field[0], MAX_ADDRESS_LEN, "%s", ip6_to_str((const struct e_in6_addr *)(sac->addr->data)));
+ }
+
+ gtk_list_store_insert_with_values( list_store , &iter, G_MAXINT,
+ IP_ADDR_COLUMN, field[0],
+ DATA_COLUMN, sac->addr_count[SCTP_DATA_CHUNK_ID],
+ INIT_COLUMN, sac->addr_count[SCTP_INIT_CHUNK_ID],
+ INIT_ACK_COLUMN, sac->addr_count[SCTP_INIT_ACK_CHUNK_ID],
+ SACK_COLUMN, sac->addr_count[SCTP_SACK_CHUNK_ID],
+ HEARTBEAT_COLUMN, sac->addr_count[SCTP_HEARTBEAT_CHUNK_ID],
+ HEARTBEAT_ACK_COLUMN, sac->addr_count[SCTP_HEARTBEAT_ACK_CHUNK_ID],
+ ABORT_COLUMN, sac->addr_count[SCTP_ABORT_CHUNK_ID],
+ SHUTDOWN_COLUMN, sac->addr_count[SCTP_SHUTDOWN_CHUNK_ID],
+ SHUTDOWN_ACK_COLUMN, sac->addr_count[SCTP_SHUTDOWN_ACK_CHUNK_ID],
+ ERROR_COLUMN, sac->addr_count[SCTP_ERROR_CHUNK_ID],
+ COOKIE_ECHO_COLUMN, sac->addr_count[SCTP_COOKIE_ECHO_CHUNK_ID],
+ COOKIE_ACK_COLUMN, sac->addr_count[SCTP_COOKIE_ACK_CHUNK_ID],
+ ECNE_COLUMN, sac->addr_count[SCTP_ECNE_CHUNK_ID],
+ CWR_COLUMN, sac->addr_count[SCTP_CWR_CHUNK_ID],
+ SHUT_COMPLETE_COLUMN, sac->addr_count[SCTP_SHUTDOWN_COMPLETE_CHUNK_ID],
+ AUTH_COLUMN, sac->addr_count[SCTP_AUTH_CHUNK_ID],
+ NR_SACK_COLUMN, sac->addr_count[SCTP_NR_SACK_CHUNK_ID],
+ ASCONF_ACK_COLUMN, sac->addr_count[SCTP_ASCONF_ACK_CHUNK_ID],
+ PKTDROP_COLUMN, sac->addr_count[SCTP_PKTDROP_CHUNK_ID],
+ FORWARD_TSN_COLUMN, sac->addr_count[SCTP_FORWARD_TSN_CHUNK_ID],
+ ASCONF_COLUMN, sac->addr_count[SCTP_ASCONF_CHUNK_ID],
+ OTHERS_COLUMN, sac->addr_count[OTHER_CHUNKS_INDEX],
+ -1);
+}
+
+void sctp_chunk_stat_dlg_update(struct sctp_udata* udata, unsigned int direction)
+{
+ GList *list=NULL;
+ sctp_addr_chunk* sac;
+
+ if (udata->io->window != NULL)
+ {
+ gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(clist))));
+ if (udata->assoc->addr_chunk_count!=NULL)
+ {
+ list = g_list_first(udata->assoc->addr_chunk_count);
+
+ while (list)
+ {
+ sac = (sctp_addr_chunk*)(list->data);
+ if (sac->direction==direction)
+ {
+ add_to_clist(sac);
+ list = g_list_next(list);
+ }
+ else
+ list = g_list_next(list);
+ }
+ }
+ }
+ last_list = list;
+}
+
+
+
+static void
+sctp_chunk_stat_on_close (GtkButton *button _U_, gpointer user_data)
+{
+ struct sctp_udata *udata;
+
+ udata = (struct sctp_udata *)user_data;
+ gtk_grab_remove(udata->io->window);
+ gtk_widget_destroy(udata->io->window);
+}
+
+static void
+on_close_dlg (GtkButton *button _U_, gpointer user_data)
+{
+ struct sctp_udata *udata;
+
+ udata = (struct sctp_udata *)user_data;
+ gtk_grab_remove(udata->io->window);
+ gtk_widget_destroy(udata->io->window);
+}
+
+
+static void
+path_window_set_title(struct sctp_udata *u_data, unsigned int direction)
+{
+ char *title;
+ if(!u_data->io->window){
+ return;
+ }
+ title = g_strdup_printf("SCTP Path Chunk Statistics for Endpoint %u: %s Port1 %u Port2 %u",direction,
+ cf_get_display_name(&cfile), u_data->assoc->port1, u_data->assoc->port2);
+ gtk_window_set_title(GTK_WINDOW(u_data->io->window), title);
+ g_free(title);
+}
+
+static void
+gtk_sctpstat_dlg(struct sctp_udata *u_data, unsigned int direction)
+{
+ GtkWidget *vbox1;
+ GtkWidget *scrolledwindow1;
+ GtkWidget *hbuttonbox2;
+ GtkWidget *bt_close;
+
+
+ sctp_graph_t* io=g_malloc(sizeof(sctp_graph_t));
+ io->window=NULL;
+ u_data->io=io;
+ u_data->io->window= gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_default_size(GTK_WINDOW(u_data->io->window), 850, 200);
+ gtk_window_set_position (GTK_WINDOW (u_data->io->window), GTK_WIN_POS_CENTER);
+ path_window_set_title(u_data, direction);
+ g_signal_connect(u_data->io->window, "destroy", G_CALLBACK(chunk_dlg_destroy), u_data);
+
+ /* Container for each row of widgets */
+ vbox1 = gtk_vbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox1), 8);
+ gtk_container_add(GTK_CONTAINER(u_data->io->window), vbox1);
+ gtk_widget_show(vbox1);
+
+ scrolledwindow1 = scrolled_window_new (NULL, NULL);
+ gtk_widget_show (scrolledwindow1);
+ gtk_box_pack_start (GTK_BOX (vbox1), scrolledwindow1, TRUE, TRUE, 0);
+
+ clist = create_list();
+ gtk_widget_show (clist);
+ gtk_container_add (GTK_CONTAINER (scrolledwindow1), clist);
+
+ gtk_widget_show(u_data->io->window);
+
+
+ hbuttonbox2 = gtk_hbutton_box_new();
+ gtk_box_pack_start(GTK_BOX(vbox1), hbuttonbox2, FALSE, FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(hbuttonbox2), 10);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX (hbuttonbox2), GTK_BUTTONBOX_SPREAD);
+ gtk_box_set_spacing(GTK_BOX (hbuttonbox2), 0);
+ gtk_widget_show(hbuttonbox2);
+
+ bt_close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_close);
+ gtk_widget_show (bt_close);
+
+ g_signal_connect(bt_close, "clicked", G_CALLBACK(sctp_chunk_stat_on_close), u_data);
+
+ cf_retap_packets(&cfile);
+
+}
+
+
+static void
+chunk_window_set_title(struct sctp_udata *u_data)
+{
+ char *title;
+ if(!u_data->io->window){
+ return;
+ }
+ title = g_strdup_printf("SCTP Association Chunk Statistics: %s Port1 %u Port2 %u",
+ cf_get_display_name(&cfile), u_data->assoc->port1, u_data->assoc->port2);
+ gtk_window_set_title(GTK_WINDOW(u_data->io->window), title);
+ g_free(title);
+}
+
+static void
+sctp_chunk_dlg(struct sctp_udata *u_data)
+{
+ GtkWidget *main_vb, *table;
+ GtkWidget *label, *h_button_box;
+ GtkWidget *close_bt;
+ gchar label_txt[50];
+ int i, row;
+
+ sctp_graph_t* io=g_malloc(sizeof(sctp_graph_t));
+ io->window=NULL;
+ u_data->io=io;
+ u_data->io->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_position (GTK_WINDOW (u_data->io->window), GTK_WIN_POS_CENTER);
+ gtk_widget_set_size_request(u_data->io->window, 500, 650);
+ g_signal_connect(u_data->io->window, "destroy", G_CALLBACK(on_destroy), u_data);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 12);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 12);
+ gtk_container_add(GTK_CONTAINER(u_data->io->window), main_vb);
+
+ /* table */
+ table = gtk_table_new(1, 4, FALSE);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 6);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 3);
+ gtk_container_add(GTK_CONTAINER(main_vb), table);
+ row = 0;
+
+ label = gtk_label_new("ChunkType");
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, row, row+1);
+ label = gtk_label_new("Association");
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, row, row+1);
+ label = gtk_label_new("Endpoint 1");
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 2, 3, row, row+1);
+ label = gtk_label_new("Endpoint 2");
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 3, 4, row, row+1);
+ row ++;
+ label = gtk_label_new("");
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, row, row+1);
+ label = gtk_label_new("");
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, row, row+1);
+ label = gtk_label_new("");
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 2, 3, row, row+1);
+ label = gtk_label_new("");
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 3, 4, row, row+1);
+ row ++;
+
+ for (i=0; i<NUM_CHUNKS; i++)
+ {
+ if (IS_SCTP_CHUNK_TYPE(i))
+ {
+ label = gtk_label_new(chunk_name(i));
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, row, row+1);
+ g_snprintf(label_txt, 10, "%u", selected_stream->chunk_count[i]);
+ label = gtk_label_new(label_txt);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, row, row+1);
+ g_snprintf(label_txt, 10, "%u", selected_stream->ep1_chunk_count[i]);
+ label = gtk_label_new(label_txt);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 2, 3, row, row+1);
+ g_snprintf(label_txt, 10, "%u", selected_stream->ep2_chunk_count[i]);
+ label = gtk_label_new(label_txt);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 3, 4, row, row+1);
+ row ++;
+ }
+ }
+
+ label = gtk_label_new("Others");
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, row, row+1);
+ g_snprintf(label_txt, 10, "%u", selected_stream->chunk_count[OTHER_CHUNKS_INDEX]);
+ label = gtk_label_new(label_txt);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, row, row+1);
+ g_snprintf(label_txt, 10, "%u", selected_stream->ep1_chunk_count[OTHER_CHUNKS_INDEX]);
+ label = gtk_label_new(label_txt);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 2, 3, row, row+1);
+ g_snprintf(label_txt, 10, "%u", selected_stream->ep2_chunk_count[OTHER_CHUNKS_INDEX]);
+ label = gtk_label_new(label_txt);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(table), label, 3, 4, row, row+1);
+
+ h_button_box=gtk_hbutton_box_new();
+ gtk_box_pack_start(GTK_BOX(main_vb), h_button_box, FALSE, FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(h_button_box), 10);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX (h_button_box), GTK_BUTTONBOX_SPREAD);
+ gtk_box_set_spacing(GTK_BOX (h_button_box), 0);
+ gtk_widget_show(h_button_box);
+
+ close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_box_pack_start(GTK_BOX(h_button_box), close_bt, FALSE, FALSE, 0);
+ gtk_widget_show(close_bt);
+ g_signal_connect(close_bt, "clicked", G_CALLBACK(on_close_dlg), u_data);
+
+ gtk_widget_show_all(u_data->io->window);
+ chunk_window_set_title(u_data);
+}
+
+void
+sctp_chunk_dlg_show(struct sctp_analyse* userdata)
+{
+ gint i;
+ struct sctp_udata *u_data;
+
+ u_data=g_malloc(sizeof(struct sctp_udata));
+ u_data->assoc=g_malloc(sizeof(sctp_assoc_info_t));
+ u_data->assoc=userdata->assoc;
+ u_data->io=NULL;
+ u_data->parent = userdata;
+
+ if (selected_stream==NULL)
+ selected_stream=g_malloc(sizeof(sctp_assoc_info_t));
+
+ selected_stream=u_data->assoc;
+ for (i=0; i<NUM_CHUNKS; i++)
+ {
+ if (IS_SCTP_CHUNK_TYPE(i) || i == OTHER_CHUNKS_INDEX)
+ selected_stream->chunk_count[i]=u_data->assoc->chunk_count[i];
+ }
+ set_child(u_data, u_data->parent);
+ increase_childcount(u_data->parent);
+ sctp_chunk_dlg(u_data);
+}
+
+void
+sctp_chunk_stat_dlg_show(unsigned int direction, struct sctp_analyse* userdata)
+{
+ struct sctp_udata *u_data;
+
+ u_data=g_malloc(sizeof(struct sctp_udata));
+ u_data->assoc=g_malloc(sizeof(sctp_assoc_info_t));
+ u_data->assoc=userdata->assoc;
+ u_data->io=NULL;
+ u_data->parent = userdata;
+
+ if (selected_stream==NULL)
+ selected_stream=g_malloc(sizeof(sctp_assoc_info_t));
+ selected_stream=u_data->assoc;
+ selected_stream->addr_chunk_count=u_data->assoc->addr_chunk_count;
+
+ set_child(u_data, u_data->parent);
+ increase_childcount(u_data->parent);
+ gtk_sctpstat_dlg(u_data, direction);
+ sctp_chunk_stat_dlg_update(u_data,direction);
+}
diff --git a/ui/gtk/sctp_error_dlg.c b/ui/gtk/sctp_error_dlg.c
new file mode 100644
index 0000000000..cca516785a
--- /dev/null
+++ b/ui/gtk/sctp_error_dlg.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright 2004, Irene Ruengeler <i.ruengeler [AT] fh-muenster.de>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <gtk/gtk.h>
+
+#include "epan/filesystem.h"
+
+#include "../globals.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/sctp_stat.h"
+
+
+static GtkWidget *sctp_error_dlg=NULL;
+static GtkWidget *clist = NULL;
+static GList *last_list = NULL;
+static sctp_error_info_t* selected_packet = NULL;/* current selection */
+
+enum
+{
+ FRAME_COLUMN,
+ INFO_COLUMN,
+ TEXT_COLUMN,
+ N_COLUMN
+};
+
+
+static void
+dlg_destroy(GtkWidget *w _U_, gpointer user_data _U_)
+{
+ sctp_error_dlg=NULL;
+}
+
+static void
+sctp_error_on_select_row(GtkTreeSelection *sel, gpointer user_data _U_)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ if (gtk_tree_selection_get_selected (sel, &model, &iter)) {
+ gtk_tree_model_get(model, &iter,
+ FRAME_COLUMN, &(selected_packet->frame_number),
+ TEXT_COLUMN, &(selected_packet->chunk_info),
+ INFO_COLUMN, &(selected_packet->info_text),
+ -1);
+ }
+}
+
+static
+GtkWidget *create_list(void)
+{
+ GtkListStore *list_store;
+ GtkWidget * list;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeView *list_view;
+ GtkTreeSelection *selection;
+
+ list_store = gtk_list_store_new(N_COLUMN,
+ G_TYPE_UINT, /* Frame number*/
+ G_TYPE_STRING, /* Chunk type*/
+ G_TYPE_STRING );/* Info */
+
+ /* Create a view */
+ list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
+
+ list_view = GTK_TREE_VIEW(list);
+
+ /* Speed up the list display */
+ gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
+
+ gtk_tree_view_set_headers_clickable(list_view, TRUE);
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref (G_OBJECT (list_store));
+
+ /*
+ * Create the first column packet, associating the "text" attribute of the
+ * cell_renderer to the first column of the model
+ */
+ /* 1:st column */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Framenumber", renderer,
+ "text", FRAME_COLUMN,
+ NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, FRAME_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 2:nd column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Chunk Types", renderer,
+ "text", TEXT_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, INFO_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 200);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 3:d column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Info", renderer,
+ "text", INFO_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, TEXT_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 200);
+ gtk_tree_view_append_column (list_view, column);
+
+
+ /* Now enable the sorting of each column */
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
+
+ /* Setup the selection handler */
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+ g_signal_connect(selection, "changed", G_CALLBACK(sctp_error_on_select_row), NULL);
+ return list;
+}
+
+
+static void add_to_clist(sctp_error_info_t* errinfo)
+{
+ GtkListStore *list_store = NULL;
+ GtkTreeIter iter;
+
+ list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (clist))); /* Get store */
+
+ gtk_list_store_insert_with_values( list_store , &iter, G_MAXINT,
+ FRAME_COLUMN, errinfo->frame_number,
+ TEXT_COLUMN, errinfo->chunk_info,
+ INFO_COLUMN, errinfo->info_text,
+ -1);
+}
+
+static void
+sctp_error_on_unselect(GtkButton *button _U_, gpointer user_data _U_)
+{
+ gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(clist)));
+}
+
+static void sctp_error_dlg_update(GList *list)
+{
+ GList *ilist=NULL;
+
+ if (sctp_error_dlg != NULL)
+ {
+ gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(clist))));
+ ilist=list;
+
+ while (ilist)
+ {
+ add_to_clist((sctp_error_info_t*)(ilist->data));
+ ilist = g_list_next(ilist);
+ }
+
+ sctp_error_on_unselect(NULL, NULL);
+ }
+ last_list = ilist;
+}
+
+
+
+static void
+sctp_error_on_frame (GtkButton *button _U_, gpointer user_data _U_)
+{
+
+ if (selected_packet==NULL)
+ return;
+
+ if (selected_packet)
+ cf_goto_frame(&cfile, selected_packet->frame_number);
+}
+
+
+static void
+sctp_error_on_close (GtkButton *button _U_, gpointer user_data _U_)
+{
+ gtk_grab_remove(sctp_error_dlg);
+ gtk_widget_destroy(sctp_error_dlg);
+}
+
+static void
+gtk_sctperror_dlg(void)
+{
+ GtkWidget *sctp_error_dlg_w;
+ GtkWidget *vbox1;
+ GtkWidget *scrolledwindow1;
+ GtkWidget *hbuttonbox2;
+ GtkWidget *bt_unselect;
+ GtkWidget *bt_frame;
+ GtkWidget *bt_close;
+
+ sctp_error_dlg_w = window_new (GTK_WINDOW_TOPLEVEL, "Wireshark: SCTP Associations");
+ gtk_window_set_position (GTK_WINDOW (sctp_error_dlg_w), GTK_WIN_POS_CENTER);
+ g_signal_connect(sctp_error_dlg_w, "destroy", G_CALLBACK(dlg_destroy), NULL);
+
+ /* Container for each row of widgets */
+ vbox1 = gtk_vbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox1), 8);
+ gtk_container_add(GTK_CONTAINER(sctp_error_dlg_w), vbox1);
+ gtk_widget_show(vbox1);
+
+ scrolledwindow1 = scrolled_window_new (NULL, NULL);
+ gtk_widget_show (scrolledwindow1);
+ gtk_box_pack_start (GTK_BOX (vbox1), scrolledwindow1, TRUE, TRUE, 0);
+
+ clist = create_list();
+ gtk_widget_show (clist);
+ gtk_container_add (GTK_CONTAINER (scrolledwindow1), clist);
+ gtk_widget_set_size_request(clist, 500, 200);
+
+ gtk_widget_show(sctp_error_dlg_w);
+
+
+ hbuttonbox2 = gtk_hbutton_box_new ();
+ gtk_widget_show (hbuttonbox2);
+ gtk_box_pack_start (GTK_BOX (vbox1), hbuttonbox2, FALSE, FALSE, 0);
+ gtk_box_set_spacing (GTK_BOX (hbuttonbox2), 5);
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox2), GTK_BUTTONBOX_SPREAD);
+
+
+ bt_unselect = gtk_button_new_with_label ("Unselect");
+ gtk_widget_show (bt_unselect);
+ gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_unselect);
+
+ bt_frame = gtk_button_new_with_label ("Go to Frame");
+ gtk_widget_show (bt_frame);
+ gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_frame);
+
+ bt_close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_widget_show (bt_close);
+ gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_close);
+
+ g_signal_connect(sctp_error_dlg_w, "destroy", G_CALLBACK(dlg_destroy), NULL);
+ g_signal_connect(bt_unselect, "clicked", G_CALLBACK(sctp_error_on_unselect), NULL);
+ g_signal_connect(bt_frame, "clicked", G_CALLBACK(sctp_error_on_frame), NULL);
+ g_signal_connect(bt_close, "clicked", G_CALLBACK(sctp_error_on_close), NULL);
+
+ sctp_error_dlg = sctp_error_dlg_w;
+
+}
+
+
+void sctp_error_dlg_show(sctp_assoc_info_t* assoc)
+{
+ GList *list;
+
+ list =assoc->error_info_list;
+ if (list != NULL)
+ {
+ if (sctp_error_dlg != NULL) {
+ /* There's already a dialog box; reactivate it. */
+ reactivate_window(sctp_error_dlg);
+ /* Another list since last call? */
+ if (list != last_list) {
+ sctp_error_dlg_update(list);
+ }
+ }
+ else {
+ /* Create and show the dialog box */
+ gtk_sctperror_dlg();
+ sctp_error_dlg_update(list);
+ }
+ }
+ else
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "No errors found!");
+}
diff --git a/ui/gtk/sctp_graph_dlg.c b/ui/gtk/sctp_graph_dlg.c
new file mode 100644
index 0000000000..149fe9ce71
--- /dev/null
+++ b/ui/gtk/sctp_graph_dlg.c
@@ -0,0 +1,1867 @@
+/*
+ * Copyright 2004, Irene Ruengeler <i.ruengeler [AT] fh-muenster.de>
+ * Copyright 2009, Varun Notibala <nbvarun [AT] gmail.com>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/filesystem.h"
+#include <epan/strutil.h>
+
+#include "../globals.h"
+#include "../ui_util.h"
+#include "../simple_dialog.h"
+
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/sctp_stat.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+#define DEFAULT_PIXELS_PER_TICK 2
+#define MAX_PIXELS_PER_TICK 4
+#define AUTO_MAX_YSCALE 0
+#define MAX_TICK_VALUES 5
+#define DEFAULT_TICK_VALUE 3
+#define MAX_YSCALE 22
+#define MAX_COUNT_TYPES 3
+
+#define COUNT_TYPE_FRAMES 0
+#define COUNT_TYPE_BYTES 1
+#define COUNT_TYPE_ADVANCED 2
+
+#define LEFT_BORDER 60
+#define RIGHT_BORDER 10
+#define TOP_BORDER 10
+#define BOTTOM_BORDER 50
+
+#define SUB_32(a, b) ((a)-(b))
+#define POINT_SIZE 3
+
+static GtkWidget * sack_bt;
+
+/*
+ * Global variables that help in redrawing graph
+ * for SACK and NRSACK
+ */
+static guint8 gIsSackChunkPresent = 0;
+static guint8 gIsNRSackChunkPresent = 0;
+
+struct chunk_header {
+ guint8 type;
+ guint8 flags;
+ guint16 length;
+};
+
+struct data_chunk_header {
+ guint8 type;
+ guint8 flags;
+ guint16 length;
+ guint32 tsn;
+ guint16 sid;
+ guint16 ssn;
+ guint32 ppi;
+};
+
+struct init_chunk_header {
+ guint8 type;
+ guint8 flags;
+ guint16 length;
+ guint32 initiate_tag;
+ guint32 a_rwnd;
+ guint16 mos;
+ guint16 mis;
+ guint32 initial_tsn;
+};
+
+struct gaps {
+ guint16 start;
+ guint16 end;
+};
+
+struct sack_chunk_header {
+ guint8 type;
+ guint8 flags;
+ guint16 length;
+ guint32 cum_tsn_ack;
+ guint32 a_rwnd;
+ guint16 nr_of_gaps;
+ guint16 nr_of_dups;
+ struct gaps gaps[1];
+};
+
+struct nr_sack_chunk_header {
+ guint8 type;
+ guint8 flags;
+ guint16 length;
+ guint32 cum_tsn_ack;
+ guint32 a_rwnd;
+ guint16 nr_of_gaps;
+ guint16 nr_of_nr_gaps;
+ guint16 nr_of_dups;
+ guint16 reserved;
+ struct gaps gaps[1];
+};
+
+static gboolean label_set = FALSE;
+static guint32 max_tsn=0, min_tsn=0;
+static void sctp_graph_set_title(struct sctp_udata *u_data);
+static void create_draw_area(GtkWidget *box, struct sctp_udata *u_data);
+static GtkWidget *zoomout_bt;
+#if defined(_WIN32) && !defined(__MINGW32__)
+static int rint (double ); /* compiler template for Windows */
+#endif
+
+static void
+draw_sack_graph(struct sctp_udata *u_data)
+{
+ tsn_t *sack;
+ GList *list=NULL, *tlist;
+ guint16 gap_start=0, gap_end=0, i, j, nr, dup_nr;
+ guint8 type;
+ guint32 tsnumber, dup;
+ gint xvalue, yvalue;
+ GdkColor red_color = {0, 65535, 0, 0};
+ GdkColor green_color = {0, 0, 65535, 0};
+ GdkColor cyan_color = {0, 0, 65535, 65535};
+ struct sack_chunk_header *sack_header;
+ struct gaps *gap;
+ guint32 /*max_num,*/ diff;
+ guint32 *dup_list;
+ cairo_t * cr = NULL;
+
+ if (u_data->dir==2)
+ {
+
+ list = g_list_last(u_data->assoc->sack2);
+ if (u_data->io->tmp==FALSE)
+ {
+ min_tsn=u_data->assoc->min_tsn2;
+ max_tsn=u_data->assoc->max_tsn2;
+ }
+ else
+ {
+ min_tsn=u_data->assoc->min_tsn2+u_data->io->tmp_min_tsn2;
+ max_tsn=u_data->assoc->min_tsn2+u_data->io->tmp_max_tsn2;
+ }
+ }
+ else if (u_data->dir==1)
+ {
+ list = g_list_last(u_data->assoc->sack1);
+ if (u_data->io->tmp==FALSE)
+ {
+ min_tsn=u_data->assoc->min_tsn1;
+ max_tsn=u_data->assoc->max_tsn1;
+ }
+ else
+ {
+ min_tsn=u_data->assoc->min_tsn1+u_data->io->tmp_min_tsn1;
+ max_tsn=u_data->assoc->min_tsn1+u_data->io->tmp_max_tsn1;
+ }
+ }
+
+ while (list)
+ {
+ sack = (tsn_t*) (list->data);
+ tlist = g_list_first(sack->tsns);
+ while (tlist)
+ {
+ type = ((struct chunk_header *)tlist->data)->type;
+
+ if (type == SCTP_SACK_CHUNK_ID)
+ {
+ gIsSackChunkPresent = 1;
+ sack_header =(struct sack_chunk_header *)tlist->data;
+ nr=g_ntohs(sack_header->nr_of_gaps);
+ tsnumber = g_ntohl(sack_header->cum_tsn_ack);
+ dup_nr=g_ntohs(sack_header->nr_of_dups);
+
+ if (sack->secs>=u_data->io->x1_tmp_sec)
+ {
+ if (nr>0)
+ {
+ gap = &sack_header->gaps[0];
+ for(i=0;i<nr; i++)
+ {
+ gap_start=g_ntohs(gap->start);
+ gap_end = g_ntohs(gap->end);
+ /* max_num=gap_end+tsnumber; */
+ for (j=gap_start; j<=gap_end; j++)
+ {
+ if (u_data->io->uoff)
+ diff = sack->secs - u_data->io->min_x;
+ else
+ diff=sack->secs*1000000+sack->usecs-u_data->io->min_x;
+ xvalue = (guint32)(LEFT_BORDER+u_data->io->offset+u_data->io->x_interval*diff);
+ yvalue = (guint32)(u_data->io->surface_height-BOTTOM_BORDER-POINT_SIZE-u_data->io->offset-((SUB_32(j+tsnumber,min_tsn))*u_data->io->y_interval));
+ if (xvalue >= LEFT_BORDER+u_data->io->offset &&
+ xvalue <= u_data->io->surface_width-RIGHT_BORDER+u_data->io->offset &&
+ yvalue >= TOP_BORDER-u_data->io->offset-POINT_SIZE &&
+ yvalue <= u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset) {
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ gdk_cairo_set_source_color (cr, &green_color);
+ cairo_arc(cr,
+ xvalue,
+ yvalue,
+ POINT_SIZE,
+ 0,
+ 2 * G_PI);
+ cairo_fill(cr);
+ cairo_destroy(cr);
+ }
+ }
+ if (i < nr-1)
+ gap++;
+ }
+ }
+ /*
+ else
+ max_num=tsnumber;
+ */
+ if (tsnumber>=min_tsn)
+ {
+ if (u_data->io->uoff)
+ diff = sack->secs - u_data->io->min_x;
+ else
+ diff=sack->secs*1000000+sack->usecs-u_data->io->min_x;
+ xvalue = (guint32)(LEFT_BORDER+u_data->io->offset+u_data->io->x_interval*diff);
+ yvalue = (guint32)(u_data->io->surface_height-BOTTOM_BORDER-POINT_SIZE -u_data->io->offset-((SUB_32(tsnumber,min_tsn))*u_data->io->y_interval));
+ if (xvalue >= LEFT_BORDER+u_data->io->offset &&
+ xvalue <= u_data->io->surface_width-RIGHT_BORDER+u_data->io->offset &&
+ yvalue >= TOP_BORDER-u_data->io->offset-POINT_SIZE &&
+ yvalue <= u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset)
+ {
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ gdk_cairo_set_source_color (cr, &red_color);
+ cairo_arc(cr,
+ xvalue,
+ yvalue,
+ POINT_SIZE,
+ 0,
+ 2 * G_PI);
+ cairo_fill(cr);
+ cairo_destroy(cr);
+
+ }
+ }
+ if (dup_nr > 0)
+ {
+ dup_list = &sack_header->a_rwnd + 2 + nr;
+ for (i = 0; i < dup_nr; i++)
+ {
+ dup = g_ntohl(dup_list[i]);
+ if (dup >= min_tsn)
+ {
+ if (u_data->io->uoff)
+ diff = sack->secs - u_data->io->min_x;
+ else
+ diff=sack->secs*1000000+sack->usecs-u_data->io->min_x;
+ xvalue = (guint32)(LEFT_BORDER+u_data->io->offset+u_data->io->x_interval*diff);
+ yvalue = (guint32)(u_data->io->surface_height-BOTTOM_BORDER-POINT_SIZE -u_data->io->offset-((SUB_32(dup,min_tsn))*u_data->io->y_interval));
+ if (xvalue >= LEFT_BORDER+u_data->io->offset &&
+ xvalue <= u_data->io->surface_width-RIGHT_BORDER+u_data->io->offset &&
+ yvalue >= TOP_BORDER-u_data->io->offset-POINT_SIZE &&
+ yvalue <= u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset)
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ gdk_cairo_set_source_color (cr, &cyan_color);
+ cairo_arc(cr,
+ xvalue,
+ yvalue,
+ POINT_SIZE,
+ 0,
+ 2 * G_PI);
+ cairo_fill(cr);
+ cairo_destroy(cr);
+ }
+ }
+ }
+ }
+ }
+ tlist = g_list_next(tlist);
+ }
+ list = g_list_previous(list);
+ }
+}
+
+/*
+ * This function plots the NR_SACK gap ack and
+ * nr gap acks.
+ * Red dot - Cumulative TSN ack
+ * Green dot - Gap ack
+ * Blue circle - NR Gap ack
+ */
+static void
+draw_nr_sack_graph(struct sctp_udata *u_data)
+{
+ tsn_t *sack;
+ GList *list=NULL, *tlist;
+ guint16 gap_start=0, gap_end=0, i, numberOf_gaps, numberOf_nr_gaps;
+ guint8 type;
+ guint32 tsnumber, j;
+ gint xvalue, yvalue;
+ GdkColor red_color = {0, 65535, 0, 0};
+ GdkColor blue_color = {0, 0, 0, 65535};
+ GdkColor green_color = {0, 0, 65535, 0};
+ struct nr_sack_chunk_header *nr_sack_header;
+ struct gaps *nr_gap;
+ guint32 /*max_num,*/ diff;
+ /* This holds the sum of gap acks and nr gap acks */
+ guint16 total_gaps = 0;
+ cairo_t *cr = NULL;
+
+ if (u_data->dir==2)
+ {
+ list = g_list_last(u_data->assoc->sack2);
+ if (u_data->io->tmp==FALSE)
+ {
+ min_tsn=u_data->assoc->min_tsn2;
+ max_tsn=u_data->assoc->max_tsn2;
+ }
+ else
+ {
+ min_tsn=u_data->assoc->min_tsn2+u_data->io->tmp_min_tsn2;
+ max_tsn=u_data->assoc->min_tsn2+u_data->io->tmp_max_tsn2;
+ }
+ }
+ else if (u_data->dir==1)
+ {
+ list = g_list_last(u_data->assoc->sack1);
+ if (u_data->io->tmp==FALSE)
+ {
+ min_tsn=u_data->assoc->min_tsn1;
+ max_tsn=u_data->assoc->max_tsn1;
+ }
+ else
+ {
+ min_tsn=u_data->assoc->min_tsn1+u_data->io->tmp_min_tsn1;
+ max_tsn=u_data->assoc->min_tsn1+u_data->io->tmp_max_tsn1;
+ }
+ }
+ while (list)
+ {
+ sack = (tsn_t*) (list->data);
+ tlist = g_list_first(sack->tsns);
+ while (tlist)
+ {
+ type = ((struct chunk_header *)tlist->data)->type;
+ /*
+ * The tlist->data is memcpy ied to the appropriate structure
+ * They entire raw tvb bytes are copied on to one of the *_chunk_header
+ * structures in sctp_stat.c
+ */
+ if (type == SCTP_NR_SACK_CHUNK_ID)
+ {
+ gIsNRSackChunkPresent = 1;
+ nr_sack_header =(struct nr_sack_chunk_header *)tlist->data;
+ numberOf_nr_gaps=g_ntohs(nr_sack_header->nr_of_nr_gaps);
+ numberOf_gaps=g_ntohs(nr_sack_header->nr_of_gaps);
+ tsnumber = g_ntohl(nr_sack_header->cum_tsn_ack);
+ total_gaps = numberOf_gaps + numberOf_nr_gaps;
+ if (sack->secs>=u_data->io->x1_tmp_sec)
+ {
+ /* If the number of nr_gaps is greater than 0 */
+ if ( total_gaps > 0 )
+ {
+ nr_gap = &nr_sack_header->gaps[0];
+ for ( i=0; i < total_gaps; i++ )
+ {
+ gap_start=g_ntohs(nr_gap->start);
+ gap_end = g_ntohs(nr_gap->end);
+ /* max_num= gap_end + tsnumber; */
+ for ( j = gap_start; j <= gap_end; j++)
+ {
+ if (u_data->io->uoff)
+ diff = sack->secs - u_data->io->min_x;
+ else
+ diff=sack->secs*1000000+sack->usecs-u_data->io->min_x;
+ xvalue = (guint32)(LEFT_BORDER+u_data->io->offset+u_data->io->x_interval*diff);
+ yvalue = (guint32)(u_data->io->surface_height-BOTTOM_BORDER-POINT_SIZE-u_data->io->offset-((SUB_32(j+tsnumber,min_tsn))*u_data->io->y_interval));
+ if (xvalue >= LEFT_BORDER+u_data->io->offset &&
+ xvalue <= u_data->io->surface_width-RIGHT_BORDER+u_data->io->offset &&
+ yvalue >= TOP_BORDER-u_data->io->offset-POINT_SIZE &&
+ yvalue <= u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset)
+ {
+ /* Check if this is an GAP ACK or NR GAP ACK */
+ if ( i >= numberOf_gaps)
+ {
+ /* This is a nr gap ack */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ gdk_cairo_set_source_color (cr, &blue_color);
+ cairo_arc(cr,
+ xvalue,
+ yvalue,
+ POINT_SIZE,
+ 0,
+ 2 * G_PI);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+
+ /* All NR GAP Acks are also gap acks, so plot these as
+ * gap acks - green dot.
+ * These will be shown as points with a green dot - GAP ACK
+ * surrounded by a blue circle - NR GAP ack
+ */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ gdk_cairo_set_source_color (cr, &green_color);
+ cairo_arc(cr,
+ xvalue,
+ yvalue,
+ POINT_SIZE,
+ 0,
+ 2 * G_PI);
+ cairo_fill(cr);
+ cairo_destroy(cr);
+ }
+ else
+ {
+ /* This is just a gap ack */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ gdk_cairo_set_source_color (cr, &green_color);
+ cairo_arc(cr,
+ xvalue,
+ yvalue,
+ POINT_SIZE,
+ 0,
+ 2 * G_PI);
+ cairo_fill(cr);
+ cairo_destroy(cr);
+ }
+ }
+ }
+ if (i < total_gaps-1)
+ nr_gap++;
+ }
+ }
+ /*
+ else
+ max_num=tsnumber;
+ */
+ if (tsnumber>=min_tsn)
+ {
+ if (u_data->io->uoff)
+ diff = sack->secs - u_data->io->min_x;
+ else
+ diff=sack->secs*1000000+sack->usecs-u_data->io->min_x;
+ xvalue = (guint32)(LEFT_BORDER+u_data->io->offset+u_data->io->x_interval*diff);
+ yvalue = (guint32)(u_data->io->surface_height-BOTTOM_BORDER-POINT_SIZE -u_data->io->offset-((SUB_32(tsnumber,min_tsn))*u_data->io->y_interval));
+ if (xvalue >= LEFT_BORDER+u_data->io->offset &&
+ xvalue <= u_data->io->surface_width-RIGHT_BORDER+u_data->io->offset &&
+ yvalue >= TOP_BORDER-u_data->io->offset-POINT_SIZE &&
+ yvalue <= u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset)
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ gdk_cairo_set_source_color (cr, &red_color);
+ cairo_arc(cr,
+ xvalue,
+ yvalue,
+ POINT_SIZE,
+ 0,
+ 2 * G_PI);
+ cairo_fill(cr);
+ cairo_destroy(cr);
+ }
+ }
+ }
+ tlist = g_list_next(tlist);
+ }
+ list = g_list_previous(list);
+ }
+}
+
+static void
+draw_tsn_graph(struct sctp_udata *u_data)
+{
+ tsn_t *tsn;
+ GList *list=NULL, *tlist;
+ guint8 type;
+ guint32 tsnumber=0;
+ guint32 min_secs=0, diff;
+ gint xvalue, yvalue;
+ cairo_t *cr = NULL;
+
+ if (u_data->dir == 1)
+ {
+ list = g_list_last(u_data->assoc->tsn1);
+ if (u_data->io->tmp == FALSE)
+ {
+ min_tsn = u_data->assoc->min_tsn1;
+ max_tsn = u_data->assoc->max_tsn1;
+ }
+ else
+ {
+ min_tsn = u_data->assoc->min_tsn1 + u_data->io->tmp_min_tsn1;
+ max_tsn = u_data->assoc->min_tsn1 + u_data->io->tmp_max_tsn1;
+ }
+ }
+ else if (u_data->dir == 2)
+ {
+ list = g_list_last(u_data->assoc->tsn2);
+ if (u_data->io->tmp == FALSE)
+ {
+ min_tsn = u_data->assoc->min_tsn2;
+ max_tsn = u_data->assoc->max_tsn2;
+ }
+ else
+ {
+ min_tsn = u_data->assoc->min_tsn2 + u_data->io->tmp_min_tsn2;
+ max_tsn = u_data->assoc->min_tsn2 + u_data->io->tmp_max_tsn2;
+ }
+ }
+
+ while (list)
+ {
+ tsn = (tsn_t*) (list->data);
+ tlist = g_list_first(tsn->tsns);
+ while (tlist)
+ {
+ type = ((struct chunk_header *)tlist->data)->type;
+ if (type == SCTP_DATA_CHUNK_ID)
+ tsnumber = g_ntohl(((struct data_chunk_header *)tlist->data)->tsn);
+ if (tsnumber >= min_tsn && tsnumber <= max_tsn && tsn->secs >= min_secs)
+ {
+ if (u_data->io->uoff) {
+ diff = tsn->secs - u_data->io->min_x;
+ } else {
+ diff = tsn->secs * 1000000 + tsn->usecs - u_data->io->min_x;
+ }
+ xvalue = (guint32)(LEFT_BORDER + u_data->io->offset + u_data->io->x_interval * diff);
+ yvalue = (guint32)(u_data->io->surface_height - BOTTOM_BORDER - POINT_SIZE - u_data->io->offset - ((SUB_32(tsnumber,min_tsn))*u_data->io->y_interval));
+ if (xvalue >= LEFT_BORDER+u_data->io->offset &&
+ xvalue <= u_data->io->surface_width - RIGHT_BORDER + u_data->io->offset &&
+ yvalue >= TOP_BORDER - u_data->io->offset - POINT_SIZE &&
+ yvalue <= u_data->io->surface_height - BOTTOM_BORDER - u_data->io->offset) {
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_arc(cr,
+ xvalue,
+ yvalue,
+ POINT_SIZE,
+ 0,
+ 2 * G_PI);
+ cairo_fill(cr);
+ cairo_destroy(cr);
+ }
+
+ }
+ tlist = g_list_next(tlist);
+ }
+ list = g_list_previous(list);
+ }
+}
+
+
+static void
+sctp_graph_draw(struct sctp_udata *u_data)
+{
+ int length, lwidth;
+ guint32 distance=5, i, e, sec, w, start, a, b, j;
+ gint label_width, label_height;
+ char label_string[15];
+ gfloat dis;
+ gboolean write_label = FALSE;
+ PangoLayout *layout;
+ GtkAllocation widget_alloc;
+ cairo_t *cr;
+
+ if (u_data->io->x1_tmp_sec==0 && u_data->io->x1_tmp_usec==0)
+ u_data->io->offset=0;
+ else
+ u_data->io->offset=5;
+
+ if (u_data->io->x2_tmp_sec - u_data->io->x1_tmp_sec > 1500)
+ {
+ u_data->io->min_x=u_data->io->x1_tmp_sec;
+ u_data->io->max_x=u_data->io->x2_tmp_sec;
+ u_data->io->uoff = TRUE;
+ }
+ else
+ {
+ u_data->io->min_x=(guint32)(u_data->io->x1_tmp_sec*1000000.0+u_data->io->x1_tmp_usec);
+ u_data->io->max_x=(guint32)(u_data->io->x2_tmp_sec*1000000.0+u_data->io->x2_tmp_usec);
+ u_data->io->uoff = FALSE;
+ }
+
+ u_data->io->tmp_width=u_data->io->max_x-u_data->io->min_x;
+
+ if (u_data->dir==1)
+ {
+ if (u_data->io->tmp==FALSE)
+ {
+ if (u_data->assoc->tsn1!=NULL || u_data->assoc->sack1!=NULL)
+ u_data->io->max_y=u_data->io->tmp_max_tsn1 - u_data->io->tmp_min_tsn1;
+ else
+ u_data->io->max_y= 0;
+ u_data->io->min_y = 0;
+ }
+ else
+ {
+ u_data->io->max_y = u_data->io->tmp_max_tsn1;
+ u_data->io->min_y = u_data->io->tmp_min_tsn1;
+ }
+ }
+ else if (u_data->dir==2)
+ {
+ if (u_data->io->tmp==FALSE)
+ {
+ if (u_data->assoc->tsn2!=NULL || u_data->assoc->sack2!=NULL)
+ u_data->io->max_y=u_data->io->tmp_max_tsn2 -u_data->io->tmp_min_tsn2;
+ else
+ u_data->io->max_y= 0;
+ u_data->io->min_y = 0;
+ }
+ else
+ {
+ u_data->io->max_y = u_data->io->tmp_max_tsn2;
+ u_data->io->min_y = u_data->io->tmp_min_tsn2;
+ }
+ }
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ gtk_widget_get_allocation(u_data->io->draw_area, &widget_alloc);
+ cairo_rectangle (cr,
+ 0,
+ 0,
+ widget_alloc.width,
+ widget_alloc.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+ distance=5;
+ /* x_axis */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, LEFT_BORDER+u_data->io->offset+0.5, u_data->io->surface_height - BOTTOM_BORDER+0.5);
+ cairo_line_to(cr, u_data->io->surface_width - RIGHT_BORDER + u_data->io->offset+0.5, u_data->io->surface_height - BOTTOM_BORDER+0.5);
+
+ cairo_move_to(cr, u_data->io->surface_width - RIGHT_BORDER + u_data->io->offset+0.5, u_data->io->surface_height - BOTTOM_BORDER+0.5);
+ cairo_line_to(cr, u_data->io->surface_width - RIGHT_BORDER + u_data->io->offset - 5+0.5, u_data->io->surface_height - BOTTOM_BORDER - 5+0.5);
+
+ cairo_move_to(cr, u_data->io->surface_width - RIGHT_BORDER + u_data->io->offset + 0.5, u_data->io->surface_height - BOTTOM_BORDER + 0.5);
+ cairo_line_to(cr, u_data->io->surface_width - RIGHT_BORDER + u_data->io->offset - 5.5, u_data->io->surface_height - BOTTOM_BORDER + 5.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+ u_data->io->axis_width=u_data->io->surface_width-LEFT_BORDER-RIGHT_BORDER-u_data->io->offset;
+
+ /* try to avoid dividing by zero */
+ if(u_data->io->tmp_width>0){
+ u_data->io->x_interval = (float)((u_data->io->axis_width*1.0)/u_data->io->tmp_width); /*distance in pixels between 2 data points*/
+ } else {
+ u_data->io->x_interval = (float)(u_data->io->axis_width);
+ }
+
+ e=0; /*number of decimals of x_interval*/
+ if (u_data->io->x_interval<1)
+ {
+ dis=1/u_data->io->x_interval;
+ while (dis >1)
+ {
+ dis/=10;
+ e++;
+ }
+ distance=1;
+ for (i=0; i<=e+1; i++)
+ distance*=10; /*distance per 100 pixels*/
+ }
+ else
+ distance=5;
+
+ g_snprintf(label_string, sizeof(label_string), "%d", 0);
+ memcpy(label_string,(gchar *)g_locale_to_utf8(label_string, -1 , NULL, NULL, NULL), sizeof(label_string));
+ layout = gtk_widget_create_pango_layout(u_data->io->draw_area, label_string);
+ pango_layout_get_pixel_size(layout, &label_width, &label_height);
+
+ if (u_data->io->x1_tmp_usec==0)
+ sec=u_data->io->x1_tmp_sec;
+ else
+ sec=u_data->io->x1_tmp_sec+1;
+
+
+ if (u_data->io->offset!=0)
+ {
+ g_snprintf(label_string, sizeof(label_string), "%u", u_data->io->x1_tmp_sec);
+
+ memcpy(label_string,(gchar *)g_locale_to_utf8(label_string, -1 , NULL, NULL, NULL), sizeof(label_string));
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_move_to (cr,
+ LEFT_BORDER-25,
+ u_data->io->surface_height-BOTTOM_BORDER+20);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+
+ }
+
+ w=(guint32)(500/(guint32)(distance*u_data->io->x_interval)); /*there will be a label for every w_th tic*/
+
+ if (w==0)
+ w=1;
+
+ if (w==4 || w==3 || w==2)
+ {
+ w=5;
+ a=distance/10; /*distance between two tics*/
+ b = (guint32)((u_data->io->min_x/100000))%10; /* start for labels*/
+ }
+ else
+ {
+ a=distance/5;
+ b=0;
+ }
+
+
+ if (!u_data->io->uoff)
+ {
+ if (a>=1000000)
+ {
+ start=u_data->io->min_x/1000000*1000000;
+ if (a==1000000)
+ b = 0;
+ }
+ else
+ {
+ start=u_data->io->min_x/100000;
+ if (start%2!=0)
+ start--;
+ start*=100000;
+ b = (guint32)((start/100000))%10;
+ }
+ }
+ else
+ {
+ start = u_data->io->min_x;
+ if (start%2!=0)
+ start--;
+ b = 0;
+
+ }
+
+ for (i=start, j=b; i<=u_data->io->max_x; i+=a, j++)
+ {
+ if (!u_data->io->uoff)
+ if (i>=u_data->io->min_x && i%1000000!=0)
+ {
+ length=5;
+ g_snprintf(label_string, sizeof(label_string), "%d", i%1000000);
+ if (j%w==0)
+ {
+ length=10;
+ memcpy(label_string,(gchar *)g_locale_to_utf8(label_string, -1 , NULL, NULL, NULL), sizeof(label_string));
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_move_to (cr,
+ LEFT_BORDER+u_data->io->offset+(i-u_data->io->min_x)*u_data->io->x_interval-lwidth/2,
+ u_data->io->surface_height-BOTTOM_BORDER+10);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+
+ }
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr,
+ LEFT_BORDER + u_data->io->offset + (i - u_data->io->min_x) * u_data->io->x_interval + 0.5,
+ u_data->io->surface_height - BOTTOM_BORDER + 0.5);
+ cairo_line_to(cr,
+ LEFT_BORDER + u_data->io->offset + (i - u_data->io->min_x) * u_data->io->x_interval + 0.5,
+ u_data->io->surface_height - BOTTOM_BORDER + length + 0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ }
+
+ if (!u_data->io->uoff)
+ {
+ if (i%1000000==0 && j%w==0)
+ {
+ sec=i/1000000;
+ write_label = TRUE;
+ }
+ }
+ else
+ {
+ if (j%w == 0)
+ {
+ sec = i;
+ write_label = TRUE;
+ }
+ }
+ if (write_label)
+ {
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr,
+ LEFT_BORDER + u_data->io->offset + (i - u_data->io->min_x) * u_data->io->x_interval + 0.5,
+ u_data->io->surface_height - BOTTOM_BORDER + 0.5);
+ cairo_line_to(cr,
+ LEFT_BORDER + u_data->io->offset + (i - u_data->io->min_x) * u_data->io->x_interval + 0.5,
+ u_data->io->surface_height - BOTTOM_BORDER + 10 + 0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+ g_snprintf(label_string, sizeof(label_string), "%d", sec);
+ memcpy(label_string,(gchar *)g_locale_to_utf8(label_string, -1 , NULL, NULL, NULL), sizeof(label_string));
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_move_to (cr,
+ LEFT_BORDER+u_data->io->offset+(i-u_data->io->min_x)*u_data->io->x_interval-10,
+ u_data->io->surface_height-BOTTOM_BORDER+20);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+
+ write_label = FALSE;
+ }
+
+ }
+
+ g_strlcpy(label_string, "sec", sizeof(label_string));
+
+ memcpy(label_string,(gchar *)g_locale_to_utf8(label_string, -1 , NULL, NULL, NULL), sizeof(label_string));
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_move_to (cr,
+ u_data->io->surface_width-RIGHT_BORDER-10,
+ u_data->io->surface_height-BOTTOM_BORDER+30);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+
+
+ distance=5;
+
+ /* y-axis */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, LEFT_BORDER + 0.5, TOP_BORDER - u_data->io->offset + 0.5);
+ cairo_line_to(cr, LEFT_BORDER + 0.5, u_data->io->surface_height - BOTTOM_BORDER - u_data->io->offset + 0.5);
+
+ cairo_move_to(cr, LEFT_BORDER + 0.5, TOP_BORDER - u_data->io->offset + 0.5);
+ cairo_line_to(cr, LEFT_BORDER - 5 + 0.5, TOP_BORDER - u_data->io->offset + 5 + 0.5);
+
+ cairo_move_to(cr, LEFT_BORDER + 0.5, TOP_BORDER - u_data->io->offset + 0.5);
+ cairo_line_to(cr, LEFT_BORDER +5 + 0.5, TOP_BORDER - u_data->io->offset + 5 + 0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+ u_data->io->y_interval = (float)(((u_data->io->surface_height-TOP_BORDER-BOTTOM_BORDER)*1.0)/(u_data->io->max_y-u_data->io->min_y));
+
+ e=0;
+ if (u_data->io->y_interval<1)
+ {
+ dis=1/u_data->io->y_interval;
+ while (dis >1)
+ {
+ dis/=10;
+ e++;
+ }
+ distance=1;
+ for (i=0; i<=e; i++)
+ distance=distance*10;
+ }
+ else if (u_data->io->y_interval<2)
+ distance = 10;
+
+ if (u_data->io->max_y>0)
+ {
+ for (i=u_data->io->min_y/distance*distance; i<=u_data->io->max_y; i+=distance/5)
+ {
+ if (i>=u_data->io->min_y)
+ {
+ length=5;
+ g_snprintf(label_string, sizeof(label_string), "%d", i);
+ if (i%distance==0 || (distance<=5 && u_data->io->y_interval>10))
+ {
+ length=10;
+
+ memcpy(label_string,(gchar *)g_locale_to_utf8(label_string, -1 , NULL, NULL, NULL), sizeof(label_string));
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_move_to (cr,
+ LEFT_BORDER-length-lwidth-5,
+ u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset-(i-u_data->io->min_y)*u_data->io->y_interval-POINT_SIZE);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+
+ }
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr,
+ LEFT_BORDER - length + 0.5,
+ u_data->io->surface_height - BOTTOM_BORDER - u_data->io->offset - (i - u_data->io->min_y) * u_data->io->y_interval + 0.5);
+ cairo_line_to(cr,
+ LEFT_BORDER + 0.5,
+ u_data->io->surface_height - BOTTOM_BORDER - u_data->io->offset - (i - u_data->io->min_y) * u_data->io->y_interval + 0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+ }
+ }
+ }
+ else if ((u_data->dir==1 && u_data->assoc->n_array_tsn1==0) || (u_data->dir==2 && u_data->assoc->n_array_tsn2==0))
+ simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK, "No Data Chunks sent");
+
+ g_object_unref(G_OBJECT(layout));
+}
+
+/* This function is used to change the title
+ * in the graph dialogue to NR_SACK or SACK based on the
+ * association
+ * If an association has both SAKC and NR_SACK PDU's
+ * a warning is popped
+ */
+static void
+updateLabels(void)
+{
+ if (gIsSackChunkPresent && gIsNRSackChunkPresent)
+ {
+ simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK, "This data set contains both SACK and NR SACK PDUs.");
+ gtk_button_set_label( (GtkButton*) sack_bt, "Show both Sack and NR Sack");
+ }
+ else if (gIsSackChunkPresent)
+ gtk_button_set_label( (GtkButton*) sack_bt, "Show Only Sack");
+ else
+ /* gIsNRSackChunkPresent will be true here */
+ gtk_button_set_label( (GtkButton*) sack_bt, "Show Only NR Sack");
+}
+
+static void
+sctp_graph_redraw(struct sctp_udata *u_data)
+{
+ sctp_graph_t *ios;
+ GtkAllocation widget_alloc;
+ cairo_t *cr;
+
+ u_data->io->needs_redraw=TRUE;
+
+ sctp_graph_draw(u_data);
+ switch (u_data->io->graph_type)
+ {
+ case 0:
+ /* Show both TSN and SACK information
+ * Reset the global sack variable
+ * for sack and nr sack cases
+ */
+ gIsSackChunkPresent = 0;
+ gIsNRSackChunkPresent = 0;
+ draw_sack_graph(u_data);
+ draw_nr_sack_graph(u_data);
+ draw_tsn_graph(u_data);
+ break;
+ case 1:
+ /* Show only TSN */
+ draw_tsn_graph(u_data);
+ break;
+ case 2:
+ /* Show only SACK information
+ * Reset the global sack variable
+ * for sack and nr sack cases
+ */
+ gIsSackChunkPresent = 0;
+ gIsNRSackChunkPresent = 0;
+ draw_sack_graph(u_data);
+ draw_nr_sack_graph(u_data);
+ break;
+ }
+
+ /* Updates the sack / nr sack buttons */
+ updateLabels();
+
+ ios=(sctp_graph_t *)g_object_get_data(G_OBJECT(u_data->io->draw_area), "sctp_graph_t");
+ g_assert(ios != NULL);
+
+ cr = gdk_cairo_create (gtk_widget_get_window(u_data->io->draw_area));
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, ios->surface, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, ios->pixmap, 0, 0);
+#endif
+ gtk_widget_get_allocation(u_data->io->draw_area, &widget_alloc);
+ cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+}
+
+
+static void
+on_sack_bt(GtkWidget *widget _U_, gpointer user_data)
+{
+ struct sctp_udata *u_data = user_data;
+
+ u_data->io->graph_type=2;
+ sctp_graph_redraw(u_data);
+}
+
+static void
+on_tsn_bt(GtkWidget *widget _U_, gpointer user_data)
+{
+ struct sctp_udata *u_data = user_data;
+
+ u_data->io->graph_type=1;
+ sctp_graph_redraw(u_data);
+}
+
+static void
+on_both_bt(GtkWidget *widget _U_, gpointer user_data)
+{
+ struct sctp_udata *u_data = user_data;
+
+ u_data->io->graph_type=0;
+ sctp_graph_redraw(u_data);
+}
+
+static void
+sctp_graph_close_cb(GtkWidget* widget _U_, gpointer user_data)
+{
+ struct sctp_udata *u_data = user_data;
+
+ gtk_grab_remove(GTK_WIDGET(u_data->io->window));
+ gtk_widget_destroy(GTK_WIDGET(u_data->io->window));
+
+}
+
+static gboolean
+configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer user_data)
+{
+ struct sctp_udata *u_data = user_data;
+ GtkAllocation widget_alloc;
+ cairo_t *cr;
+
+ g_assert(u_data->io != NULL);
+
+#if GTK_CHECK_VERSION(2,22,0)
+ if(u_data->io->surface){
+ cairo_surface_destroy (u_data->io->surface);
+ u_data->io->surface=NULL;
+ }
+ gtk_widget_get_allocation(widget, &widget_alloc);
+ u_data->io->surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
+ CAIRO_CONTENT_COLOR,
+ widget_alloc.width,
+ widget_alloc.height);
+#else
+ if(u_data->io->pixmap){
+ g_object_unref(u_data->io->pixmap);
+ u_data->io->pixmap=NULL;
+ }
+ gtk_widget_get_allocation(widget, &widget_alloc);
+ u_data->io->pixmap=gdk_pixmap_new(gtk_widget_get_window(widget),
+ widget_alloc.width,
+ widget_alloc.height,
+ -1);
+#endif
+
+ u_data->io->surface_width=widget_alloc.width;
+ u_data->io->surface_height=widget_alloc.height;
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+ sctp_graph_redraw(u_data);
+ return TRUE;
+}
+
+#if GTK_CHECK_VERSION(3,0,0)
+static gboolean
+draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data)
+{
+ sctp_graph_t *ios = user_data;
+ GtkAllocation allocation;
+
+ gtk_widget_get_allocation (widget, &allocation);
+ cairo_set_source_surface (cr, ios->surface, 0, 0);
+ cairo_rectangle (cr, 0, 0, allocation.width, allocation.width);
+ cairo_fill (cr);
+
+ return FALSE;
+}
+#else
+static gboolean
+expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
+{
+ sctp_graph_t *ios = user_data;
+ cairo_t *cr;
+
+ g_assert(ios != NULL);
+
+ cr = gdk_cairo_create (gtk_widget_get_window(widget));
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, ios->surface, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, ios->pixmap, 0, 0);
+#endif
+ cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+#endif
+
+static void
+on_zoomin_bt (GtkWidget *widget _U_, gpointer user_data)
+{
+ struct sctp_udata *u_data = user_data;
+ sctp_min_max_t *tmp_minmax;
+
+ if (u_data->io->rectangle_present==TRUE)
+ {
+ tmp_minmax = g_malloc(sizeof(sctp_min_max_t));
+
+ u_data->io->tmp_min_tsn1=u_data->io->y1_tmp+u_data->io->min_y;
+ u_data->io->tmp_max_tsn1=u_data->io->y2_tmp+1+u_data->io->min_y;
+ u_data->io->tmp_min_tsn2=u_data->io->tmp_min_tsn1;
+ u_data->io->tmp_max_tsn2=u_data->io->tmp_max_tsn1;
+ tmp_minmax->tmp_min_secs=u_data->io->x1_tmp_sec;
+ tmp_minmax->tmp_min_usecs= u_data->io->x1_tmp_usec;
+ tmp_minmax->tmp_max_secs= u_data->io->x2_tmp_sec;
+ tmp_minmax->tmp_max_usecs= u_data->io->x2_tmp_usec;
+ tmp_minmax->tmp_min_tsn1=u_data->io->tmp_min_tsn1;
+ tmp_minmax->tmp_max_tsn1=u_data->io->tmp_max_tsn1;
+ tmp_minmax->tmp_min_tsn2=u_data->io->tmp_min_tsn2;
+ tmp_minmax->tmp_max_tsn2=u_data->io->tmp_max_tsn2;
+ u_data->assoc->min_max = g_slist_prepend(u_data->assoc->min_max, tmp_minmax);
+ u_data->io->length = g_slist_length(u_data->assoc->min_max);
+ u_data->io->tmp=TRUE;
+ u_data->io->rectangle=FALSE;
+ u_data->io->rectangle_present=FALSE;
+ gtk_widget_set_sensitive(zoomout_bt, TRUE);
+ sctp_graph_redraw(u_data);
+ }
+ else
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Please draw a rectangle around the area you want to zoom in!");
+ }
+}
+
+static void
+zoomin_bt_fcn (struct sctp_udata *u_data)
+{
+ sctp_min_max_t *tmp_minmax;
+
+ tmp_minmax = g_malloc(sizeof(sctp_min_max_t));
+
+ u_data->io->tmp_min_tsn1=u_data->io->y1_tmp+u_data->io->min_y;
+ u_data->io->tmp_max_tsn1=u_data->io->y2_tmp+1+u_data->io->min_y;
+ u_data->io->tmp_min_tsn2=u_data->io->tmp_min_tsn1;
+ u_data->io->tmp_max_tsn2=u_data->io->tmp_max_tsn1;
+ tmp_minmax->tmp_min_secs=u_data->io->x1_tmp_sec;
+ tmp_minmax->tmp_min_usecs= u_data->io->x1_tmp_usec;
+ tmp_minmax->tmp_max_secs= u_data->io->x2_tmp_sec;
+ tmp_minmax->tmp_max_usecs= u_data->io->x2_tmp_usec;
+ tmp_minmax->tmp_min_tsn1=u_data->io->tmp_min_tsn1;
+ tmp_minmax->tmp_max_tsn1=u_data->io->tmp_max_tsn1;
+ tmp_minmax->tmp_min_tsn2=u_data->io->tmp_min_tsn2;
+ tmp_minmax->tmp_max_tsn2=u_data->io->tmp_max_tsn2;
+ u_data->assoc->min_max = g_slist_prepend(u_data->assoc->min_max, tmp_minmax);
+ u_data->io->length = g_slist_length(u_data->assoc->min_max);
+ u_data->io->tmp=TRUE;
+ u_data->io->rectangle=FALSE;
+ u_data->io->rectangle_present=FALSE;
+ gtk_widget_set_sensitive(zoomout_bt, TRUE);
+ sctp_graph_redraw(u_data);
+
+}
+
+
+
+static void
+on_zoomout_bt (GtkWidget *widget _U_, gpointer user_data)
+{
+ struct sctp_udata *u_data = user_data;
+ sctp_min_max_t *tmp_minmax, *mm;
+ gint l;
+
+ l = g_slist_length(u_data->assoc->min_max);
+
+ if (u_data->assoc->min_max!=NULL)
+ {
+ mm=(sctp_min_max_t *)((u_data->assoc->min_max)->data);
+ u_data->assoc->min_max=g_slist_remove(u_data->assoc->min_max, mm);
+ g_free(mm);
+ if (l>2)
+ {
+ tmp_minmax = (sctp_min_max_t *)u_data->assoc->min_max->data;
+ u_data->io->x1_tmp_sec=tmp_minmax->tmp_min_secs;
+ u_data->io->x1_tmp_usec=tmp_minmax->tmp_min_usecs;
+ u_data->io->x2_tmp_sec=tmp_minmax->tmp_max_secs;
+ u_data->io->x2_tmp_usec=tmp_minmax->tmp_max_usecs;
+ u_data->io->tmp_min_tsn1=tmp_minmax->tmp_min_tsn1;
+ u_data->io->tmp_max_tsn1=tmp_minmax->tmp_max_tsn1;
+ u_data->io->tmp_min_tsn2=tmp_minmax->tmp_min_tsn2;
+ u_data->io->tmp_max_tsn2=tmp_minmax->tmp_max_tsn2;
+ u_data->io->tmp=TRUE;
+ }
+ else
+ {
+ u_data->io->x1_tmp_sec=u_data->assoc->min_secs;
+ u_data->io->x1_tmp_usec=u_data->assoc->min_usecs;
+ u_data->io->x2_tmp_sec=u_data->assoc->max_secs;
+ u_data->io->x2_tmp_usec=u_data->assoc->max_usecs;
+ u_data->io->tmp_min_tsn1=u_data->assoc->min_tsn1;
+ u_data->io->tmp_max_tsn1=u_data->assoc->max_tsn1;
+ u_data->io->tmp_min_tsn2=u_data->assoc->min_tsn2;
+ u_data->io->tmp_max_tsn2=u_data->assoc->max_tsn2;
+ u_data->io->tmp=FALSE;
+ }
+ }
+ else
+ {
+ u_data->io->x1_tmp_sec=u_data->assoc->min_secs;
+ u_data->io->x1_tmp_usec=u_data->assoc->min_usecs;
+ u_data->io->x2_tmp_sec=u_data->assoc->max_secs;
+ u_data->io->x2_tmp_usec=u_data->assoc->max_usecs;
+ u_data->io->tmp_min_tsn1=u_data->assoc->min_tsn1;
+ u_data->io->tmp_max_tsn1=u_data->assoc->max_tsn1;
+ u_data->io->tmp_min_tsn2=u_data->assoc->min_tsn2;
+ u_data->io->tmp_max_tsn2=u_data->assoc->max_tsn2;
+ u_data->io->tmp=FALSE;
+ }
+ if (g_slist_length(u_data->assoc->min_max)==1)
+ gtk_widget_set_sensitive(zoomout_bt, FALSE);
+ sctp_graph_redraw(u_data);
+}
+
+static gboolean
+on_button_press_event (GtkWidget *widget _U_, GdkEventButton *event, gpointer user_data)
+{
+ struct sctp_udata *u_data = user_data;
+ sctp_graph_t *ios;
+ cairo_t *cr;
+
+ if (u_data->io->rectangle==TRUE)
+ {
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_rectangle (cr,
+ floor(MIN(u_data->io->x_old,u_data->io->x_new)),
+ floor(MIN(u_data->io->y_old,u_data->io->y_new)),
+ floor(abs((long)(u_data->io->x_new-u_data->io->x_old))),
+ floor(abs((long)(u_data->io->y_new-u_data->io->y_old))));
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_stroke (cr);
+ cairo_destroy (cr);
+
+ ios=(sctp_graph_t *)g_object_get_data(G_OBJECT(u_data->io->draw_area), "sctp_graph_t");
+
+ g_assert(ios != NULL);
+
+ cr = gdk_cairo_create (gtk_widget_get_window(u_data->io->draw_area));
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, ios->surface, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, ios->pixmap, 0, 0);
+#endif
+ cairo_rectangle (cr, 0, 0, abs((long)(u_data->io->x_new-u_data->io->x_old)), abs((long)(u_data->io->y_new-u_data->io->y_old)));
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+
+ sctp_graph_redraw(u_data);
+ }
+ u_data->io->x_old=event->x;
+ u_data->io->y_old=event->y;
+ if (u_data->io->y_old>u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset-POINT_SIZE)
+ u_data->io->y_old=u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset-POINT_SIZE;
+ if (u_data->io->x_old<LEFT_BORDER+u_data->io->offset)
+ u_data->io->x_old=LEFT_BORDER+u_data->io->offset;
+ u_data->io->rectangle=FALSE;
+
+ return TRUE;
+}
+
+
+static gboolean
+on_button_release_event (GtkWidget *widget _U_, GdkEventButton *event, gpointer user_data)
+{
+ struct sctp_udata *u_data = user_data;
+ sctp_graph_t *ios;
+ guint32 helpx, helpy, x1_tmp, x2_tmp, y_value, t_size=0, s_size=0, i, y_tolerance;
+ gint label_width, label_height;
+ gdouble x_value, position, s_diff=0, t_diff=0, x_tolerance=0.0001;
+ gint lwidth;
+ char label_string[30];
+ GPtrArray *tsnlist = NULL, *sacklist=NULL;
+ struct tsn_sort *tsn, *sack=NULL;
+ gboolean sack_found = FALSE;
+ GtkAllocation widget_alloc;
+ PangoLayout *layout;
+ cairo_t *cr;
+
+ g_snprintf(label_string, 15, "%d", 0);
+ memcpy(label_string,(gchar *)g_locale_to_utf8(label_string, -1 , NULL, NULL, NULL), 15);
+ layout = gtk_widget_create_pango_layout(u_data->io->draw_area, label_string);
+ pango_layout_get_pixel_size(layout, &label_width, &label_height);
+
+ if (event->y>u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset)
+ event->y = u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset;
+ if (event->x < LEFT_BORDER+u_data->io->offset)
+ event->x = LEFT_BORDER+u_data->io->offset;
+ if (abs((long)(event->x-u_data->io->x_old))>10 || abs((long)(event->y-u_data->io->y_old))>10)
+ {
+ u_data->io->rect_x_min = (gint)floor(MIN(u_data->io->x_old,event->x));
+ u_data->io->rect_x_max = (gint)ceil(MAX(u_data->io->x_old,event->x));
+ u_data->io->rect_y_min = (gint)floor(MIN(u_data->io->y_old,event->y));
+ u_data->io->rect_y_max = (gint)ceil(MAX(u_data->io->y_old,event->y))+POINT_SIZE;
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_rectangle (cr,
+ u_data->io->rect_x_min+0.5,
+ u_data->io->rect_y_min+0.5,
+ u_data->io->rect_x_max - u_data->io->rect_x_min,
+ u_data->io->rect_y_max - u_data->io->rect_y_min);
+ cairo_set_line_width (cr, 1.0);
+ cairo_stroke (cr);
+ cairo_destroy (cr);
+
+ ios=(sctp_graph_t *)g_object_get_data(G_OBJECT(u_data->io->draw_area), "sctp_graph_t");
+
+ g_assert(ios != NULL);
+
+ cr = gdk_cairo_create (gtk_widget_get_window(u_data->io->draw_area));
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, ios->surface, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, ios->pixmap, 0, 0);
+#endif
+ gtk_widget_get_allocation(u_data->io->draw_area, &widget_alloc);
+ cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+
+ x1_tmp=(unsigned int)floor(u_data->io->min_x+((u_data->io->x_old-LEFT_BORDER-u_data->io->offset)*u_data->io->tmp_width/u_data->io->axis_width));
+ x2_tmp=(unsigned int)floor(u_data->io->min_x+((event->x-LEFT_BORDER-u_data->io->offset)*u_data->io->tmp_width/u_data->io->axis_width));
+ helpx=MIN(x1_tmp, x2_tmp);
+ if (helpx==x2_tmp)
+ {
+ x2_tmp=x1_tmp;
+ x1_tmp=helpx;
+ }
+ if (u_data->io->uoff)
+ {
+ if (x2_tmp - x1_tmp <= 1500)
+ u_data->io->uoff = FALSE;
+ u_data->io->x1_tmp_sec=(guint32)x1_tmp;
+ u_data->io->x1_tmp_usec=0;
+ u_data->io->x2_tmp_sec=(guint32)x2_tmp;
+ u_data->io->x2_tmp_usec=0;
+ }
+ else
+ {
+ u_data->io->x1_tmp_sec=(guint32)x1_tmp/1000000;
+ u_data->io->x1_tmp_usec=x1_tmp%1000000;
+ u_data->io->x2_tmp_sec=(guint32)x2_tmp/1000000;
+ u_data->io->x2_tmp_usec=x2_tmp%1000000;
+ }
+ u_data->io->x1_akt_sec = u_data->io->x1_tmp_sec;
+ u_data->io->x1_akt_usec = u_data->io->x1_tmp_usec;
+ u_data->io->x2_akt_sec = u_data->io->x2_tmp_sec;
+ u_data->io->x2_akt_usec = u_data->io->x2_tmp_usec;
+
+ u_data->io->y1_tmp=(guint32)((u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset-u_data->io->y_old)/u_data->io->y_interval);
+ u_data->io->y2_tmp=(guint32)((u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset-event->y)/u_data->io->y_interval);
+ helpy = MIN(u_data->io->y1_tmp, u_data->io->y2_tmp);
+ u_data->io->y2_tmp = MAX(u_data->io->y1_tmp, u_data->io->y2_tmp);
+ u_data->io->y1_tmp = helpy;
+ u_data->io->x_new=event->x;
+ u_data->io->y_new=event->y;
+ u_data->io->rectangle=TRUE;
+ u_data->io->rectangle_present=TRUE;
+ }
+ else
+ {
+ if (u_data->io->rectangle_present==TRUE)
+ {
+ u_data->io->rectangle_present=FALSE;
+ if (event->x >= u_data->io->rect_x_min && event->x <= u_data->io->rect_x_max &&
+ event->y >= u_data->io->rect_y_min && event->y <= u_data->io->rect_y_max)
+ zoomin_bt_fcn(u_data);
+ else
+ {
+ u_data->io->x1_tmp_sec = u_data->io->x1_akt_sec;
+ u_data->io->x1_tmp_usec = u_data->io->x1_akt_usec;
+ u_data->io->x2_tmp_sec = u_data->io->x2_akt_sec;
+ u_data->io->x2_tmp_usec = u_data->io->x2_akt_usec;
+ sctp_graph_redraw(u_data);
+ }
+ }
+ else if (label_set)
+ {
+ label_set = FALSE;
+ sctp_graph_redraw(u_data);
+ }
+ else
+ {
+ x_value = ((event->x-LEFT_BORDER-u_data->io->offset) * ((u_data->io->x2_tmp_sec+u_data->io->x2_tmp_usec/1000000.0)-(u_data->io->x1_tmp_sec+u_data->io->x1_tmp_usec/1000000.0)) / (u_data->io->surface_width-LEFT_BORDER-RIGHT_BORDER-u_data->io->offset))+u_data->io->x1_tmp_sec+u_data->io->x1_tmp_usec/1000000.0;
+ y_value = (gint)rint((u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset-event->y) * (max_tsn - min_tsn) / (u_data->io->surface_height-BOTTOM_BORDER-u_data->io->offset)) + min_tsn;
+
+ if (u_data->dir == 1)
+ {
+ tsnlist = u_data->assoc->sort_tsn1;
+ t_size = u_data->assoc->n_data_chunks_ep1;
+ sacklist = u_data->assoc->sort_sack1;
+ s_size = u_data->assoc->n_sack_chunks_ep1;
+ }
+ else
+ {
+ tsnlist = u_data->assoc->sort_tsn2;
+ t_size = u_data->assoc->n_data_chunks_ep2;
+ sacklist = u_data->assoc->sort_sack2;
+ s_size = u_data->assoc->n_sack_chunks_ep2;
+ }
+ x_tolerance = (gdouble)((u_data->io->tmp_width / u_data->io->axis_width*1.0))*5/1000000.0;
+ y_tolerance = (guint32)(((u_data->io->max_y - u_data->io->min_y) / (u_data->io->surface_height-TOP_BORDER-BOTTOM_BORDER-u_data->io->offset)) * 2.0);
+ if (y_tolerance==0)
+ y_tolerance = 2;
+ else if (y_tolerance > 5)
+ y_tolerance = 5;
+
+ for (i=0; i<s_size; i++)
+ {
+ sack = (struct tsn_sort*)(g_ptr_array_index(sacklist, i));
+ if ((guint32)abs(sack->tsnumber - y_value)<y_tolerance)
+ {
+ s_diff = fabs((sack->secs+sack->usecs/1000000.0)- x_value);
+ if (s_diff < x_tolerance)
+ sack_found = TRUE;
+ break;
+ }
+ }
+
+ for (i=0; i<t_size; i++)
+ {
+ tsn = (struct tsn_sort*)(g_ptr_array_index(tsnlist, i));
+ if ((guint32)abs(tsn->tsnumber - y_value)<y_tolerance)
+ {
+ t_diff = fabs((tsn->secs+tsn->usecs/1000000.0)- x_value);
+ if (sack_found && s_diff < t_diff)
+ {
+ cf_goto_frame(&cfile, sack->framenumber);
+ x_value = sack->secs+sack->usecs/1000000.0;
+ y_value = sack->tsnumber;
+ }
+ else if (t_diff < x_tolerance)
+ {
+ cf_goto_frame(&cfile, tsn->framenumber);
+ x_value = tsn->secs+tsn->usecs/1000000.0;
+ y_value = tsn->tsnumber;
+ }
+ break;
+ }
+ }
+
+ g_snprintf(label_string, sizeof(label_string), "(%.6lf, %u)", x_value, y_value);
+
+ label_set = TRUE;
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr,
+ (event->x-2)+0.5,
+ (event->y)+0.5);
+ cairo_line_to(cr,
+ (event->x+2)+0.5,
+ (event->y)+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr,
+ (event->x)+0.5,
+ (event->y-2)+0.5);
+ cairo_line_to(cr,
+ (event->x)+0.5,
+ (event->y+2)+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+ if (event->x+150>=u_data->io->surface_width)
+ position = event->x - 150;
+ else
+ position = event->x + 5;
+
+
+ memcpy(label_string,(gchar *)g_locale_to_utf8(label_string, -1 , NULL, NULL, NULL), 15);
+ pango_layout_set_text(layout, label_string, -1);
+ pango_layout_get_pixel_size(layout, &lwidth, NULL);
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (u_data->io->surface);
+#else
+ cr = gdk_cairo_create (u_data->io->pixmap);
+#endif
+ cairo_move_to (cr,
+ position,
+ event->y-10);
+ pango_cairo_show_layout (cr, layout);
+ cairo_destroy (cr);
+ cr = NULL;
+
+ ios=(sctp_graph_t *)g_object_get_data(G_OBJECT(u_data->io->draw_area), "sctp_graph_t");
+ g_assert(ios != NULL);
+
+ cr = gdk_cairo_create (gtk_widget_get_window(u_data->io->draw_area));
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, ios->surface, 0, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, ios->pixmap, 0, 0);
+#endif
+ gtk_widget_get_allocation(u_data->io->draw_area, &widget_alloc);
+ cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+ }
+ }
+
+ g_object_unref(G_OBJECT(layout));
+
+ return TRUE;
+}
+
+
+static void
+init_sctp_graph_window(struct sctp_udata *u_data)
+{
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *bt_close, *tsn_bt, *both_bt, *zoomin_bt;
+
+ /* create the main window */
+ u_data->io->window= dlg_window_new("WSCTP Graphics"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(u_data->io->window), TRUE);
+
+ vbox=gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(u_data->io->window), vbox);
+ gtk_widget_show(vbox);
+
+ create_draw_area(vbox, u_data);
+
+ sctp_graph_set_title(u_data);
+
+ hbox = gtk_hbutton_box_new();
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 10);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX (hbox), GTK_BUTTONBOX_SPREAD);
+ gtk_box_set_spacing(GTK_BOX (hbox), 0);
+ gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
+ gtk_widget_show(hbox);
+ sack_bt = gtk_button_new_with_label ("Show Only Sacks");
+ gtk_box_pack_start(GTK_BOX(hbox), sack_bt, FALSE, FALSE, 0);
+ gtk_widget_show(sack_bt);
+ g_signal_connect(sack_bt, "clicked", G_CALLBACK(on_sack_bt), u_data);
+
+ tsn_bt = gtk_button_new_with_label ("Show Only TSNs");
+ gtk_box_pack_start(GTK_BOX(hbox), tsn_bt, FALSE, FALSE, 0);
+ gtk_widget_show(tsn_bt);
+ g_signal_connect(tsn_bt, "clicked", G_CALLBACK(on_tsn_bt), u_data);
+
+ both_bt = gtk_button_new_with_label ("Show both");
+ gtk_box_pack_start(GTK_BOX(hbox), both_bt, FALSE, FALSE, 0);
+ gtk_widget_show(both_bt);
+ g_signal_connect(both_bt, "clicked", G_CALLBACK(on_both_bt), u_data);
+
+ zoomin_bt = gtk_button_new_with_label ("Zoom in");
+ gtk_box_pack_start(GTK_BOX(hbox), zoomin_bt, FALSE, FALSE, 0);
+ gtk_widget_show(zoomin_bt);
+ g_signal_connect(zoomin_bt, "clicked", G_CALLBACK(on_zoomin_bt), u_data);
+ gtk_widget_set_tooltip_text(zoomin_bt, "Zoom in the area you have selected");
+
+ zoomout_bt = gtk_button_new_with_label ("Zoom out");
+ gtk_box_pack_start(GTK_BOX(hbox), zoomout_bt, FALSE, FALSE, 0);
+ gtk_widget_show(zoomout_bt);
+ g_signal_connect(zoomout_bt, "clicked", G_CALLBACK(on_zoomout_bt), u_data);
+ gtk_widget_set_tooltip_text(zoomout_bt, "Zoom out one step");
+ gtk_widget_set_sensitive(zoomout_bt, FALSE);
+
+ bt_close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_box_pack_start(GTK_BOX(hbox), bt_close, FALSE, FALSE, 0);
+ gtk_widget_show(bt_close);
+ g_signal_connect(bt_close, "clicked", G_CALLBACK(sctp_graph_close_cb), u_data);
+
+ g_signal_connect(u_data->io->draw_area,"button_press_event",G_CALLBACK(on_button_press_event), u_data);
+ g_signal_connect(u_data->io->draw_area,"button_release_event",G_CALLBACK(on_button_release_event), u_data);
+ gtk_widget_set_events(u_data->io->draw_area, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_EXPOSURE_MASK);
+
+ gtk_widget_show(u_data->io->window);
+}
+
+static void
+sctp_graph_set_title(struct sctp_udata *u_data)
+{
+ char *title;
+
+ if(!u_data->io->window)
+ {
+ return;
+ }
+ title = g_strdup_printf("SCTP TSNs and Sacks over Time: %s Port1 %u Port2 %u Endpoint %u",
+ cf_get_display_name(&cfile), u_data->parent->assoc->port1, u_data->parent->assoc->port2, u_data->dir);
+ gtk_window_set_title(GTK_WINDOW(u_data->io->window), title);
+ g_free(title);
+}
+
+static void
+gtk_sctpgraph_init(struct sctp_udata *u_data)
+{
+ sctp_graph_t *io;
+ sctp_min_max_t* tmp_minmax;
+
+ io=g_malloc(sizeof(sctp_graph_t));
+ io->needs_redraw=TRUE;
+ io->x_interval=1000;
+ io->window=NULL;
+ io->draw_area=NULL;
+#if GTK_CHECK_VERSION(2,22,0)
+ io->surface=NULL;
+#else
+ io->pixmap=NULL;
+#endif
+ io->surface_width=800;
+ io->surface_height=600;
+ io->graph_type=0;
+ u_data->io=io;
+ u_data->io->x1_tmp_sec=u_data->assoc->min_secs;
+ u_data->io->x1_tmp_usec=u_data->assoc->min_usecs;
+ u_data->io->x2_tmp_sec=u_data->assoc->max_secs;
+ u_data->io->x2_tmp_usec=u_data->assoc->max_usecs;
+ u_data->io->tmp_min_tsn1=u_data->assoc->min_tsn1;
+ u_data->io->tmp_max_tsn1=u_data->assoc->max_tsn1;
+ u_data->io->tmp_min_tsn2=u_data->assoc->min_tsn2;
+ u_data->io->tmp_max_tsn2=u_data->assoc->max_tsn2;
+ u_data->io->tmp=FALSE;
+
+ tmp_minmax = g_malloc(sizeof(sctp_min_max_t));
+ tmp_minmax->tmp_min_secs = u_data->assoc->min_secs;
+ tmp_minmax->tmp_min_usecs=u_data->assoc->min_usecs;
+ tmp_minmax->tmp_max_secs=u_data->assoc->max_secs;
+ tmp_minmax->tmp_max_usecs=u_data->assoc->max_usecs;
+ tmp_minmax->tmp_min_tsn2=u_data->assoc->min_tsn2;
+ tmp_minmax->tmp_min_tsn1=u_data->assoc->min_tsn1;
+ tmp_minmax->tmp_max_tsn1=u_data->assoc->max_tsn1;
+ tmp_minmax->tmp_max_tsn2=u_data->assoc->max_tsn2;
+ u_data->assoc->min_max = g_slist_prepend(u_data->assoc->min_max, tmp_minmax);
+
+ /* build the GUI */
+ init_sctp_graph_window(u_data);
+ sctp_graph_redraw(u_data);
+
+}
+
+
+static void
+quit(GObject *object _U_, gpointer user_data)
+{
+ struct sctp_udata *u_data=user_data;
+
+ decrease_childcount(u_data->parent);
+ remove_child(u_data, u_data->parent);
+
+ g_free(u_data->io);
+
+ u_data->assoc->min_max = NULL;
+ g_free(u_data);
+}
+
+
+static void
+create_draw_area(GtkWidget *box, struct sctp_udata *u_data)
+{
+
+ u_data->io->draw_area=gtk_drawing_area_new();
+ g_object_set_data(G_OBJECT(u_data->io->draw_area), "sctp_graph_t", u_data->io);
+ g_signal_connect(u_data->io->draw_area, "destroy", G_CALLBACK(quit), u_data);
+
+ gtk_widget_set_size_request(u_data->io->draw_area, u_data->io->surface_width, u_data->io->surface_height);
+
+ /* signals needed to handle backing pixmap */
+#if GTK_CHECK_VERSION(3,0,0)
+ g_signal_connect(u_data->io->draw_area, "draw", G_CALLBACK(draw_event), u_data->io);
+#else
+ g_signal_connect(u_data->io->draw_area, "expose_event", G_CALLBACK(expose_event), u_data->io);
+#endif
+ g_signal_connect(u_data->io->draw_area, "configure_event", G_CALLBACK(configure_event), u_data);
+
+ gtk_widget_show(u_data->io->draw_area);
+ gtk_box_pack_start(GTK_BOX(box), u_data->io->draw_area, TRUE, TRUE, 0);
+}
+
+
+
+void
+create_graph(guint16 dir, struct sctp_analyse* userdata)
+{
+ struct sctp_udata *u_data;
+
+ u_data=g_malloc(sizeof(struct sctp_udata));
+ u_data->assoc=userdata->assoc;
+ u_data->io=NULL;
+ u_data->dir = dir;
+ u_data->parent = userdata;
+ if ((u_data->dir==1 && u_data->assoc->n_array_tsn1==0)|| (u_data->dir==2 && u_data->assoc->n_array_tsn2==0))
+ simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK, "No Data Chunks sent");
+ else
+ {
+ set_child(u_data, u_data->parent);
+ increase_childcount(u_data->parent);
+ gtk_sctpgraph_init(u_data);
+ }
+}
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+/* replacement of Unix rint() for Windows */
+static int
+rint (double x)
+{
+ char *buf;
+ int i,dec,sig;
+
+ buf = _fcvt(x, 0, &dec, &sig);
+ i = atoi(buf);
+ if(sig == 1) {
+ i = i * -1;
+ }
+ return(i);
+}
+#endif
diff --git a/ui/gtk/sctp_stat.c b/ui/gtk/sctp_stat.c
new file mode 100644
index 0000000000..1f4cf849a1
--- /dev/null
+++ b/ui/gtk/sctp_stat.c
@@ -0,0 +1,1337 @@
+/*
+ * Copyright 2004, Irene Ruengeler <i.ruengeler [AT] fh-muenster.de>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/packet_info.h"
+#include <epan/tap.h>
+#include "epan/address.h"
+#include <epan/strutil.h>
+
+#include "../simple_dialog.h"
+
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/sctp_stat.h"
+#include "ui/gtk/main.h"
+
+#define SCTP_ABORT_CHUNK_T_BIT 0x01
+
+#define PARAMETER_TYPE_LENGTH 2
+#define PARAMETER_LENGTH_LENGTH 2
+#define PARAMETER_HEADER_LENGTH (PARAMETER_TYPE_LENGTH + PARAMETER_LENGTH_LENGTH)
+
+#define PARAMETER_HEADER_OFFSET 0
+#define PARAMETER_TYPE_OFFSET PARAMETER_HEADER_OFFSET
+#define PARAMETER_LENGTH_OFFSET (PARAMETER_TYPE_OFFSET + PARAMETER_TYPE_LENGTH)
+#define PARAMETER_VALUE_OFFSET (PARAMETER_LENGTH_OFFSET + PARAMETER_LENGTH_LENGTH)
+
+#define IPV6_ADDRESS_LENGTH 16
+#define IPV6_ADDRESS_OFFSET PARAMETER_VALUE_OFFSET
+#define IPV4_ADDRESS_LENGTH 4
+#define IPV4_ADDRESS_OFFSET PARAMETER_VALUE_OFFSET
+#define IPV4ADDRESS_PARAMETER_ID 0x0005
+#define IPV6ADDRESS_PARAMETER_ID 0x0006
+
+#define SACK_CHUNK_CUMULATIVE_TSN_ACK_LENGTH 4
+#define SACK_CHUNK_CUMULATIVE_TSN_ACK_OFFSET (CHUNK_VALUE_OFFSET + 0)
+#define SACK_CHUNK_ADV_REC_WINDOW_CREDIT_LENGTH 4
+#define SACK_CHUNK_ADV_REC_WINDOW_CREDIT_OFFSET (SACK_CHUNK_CUMULATIVE_TSN_ACK_OFFSET + \
+ SACK_CHUNK_CUMULATIVE_TSN_ACK_LENGTH)
+
+#define INIT_CHUNK_INITIAL_TSN_LENGTH 4
+#define INIT_CHUNK_FIXED_PARAMTERS_LENGTH (INIT_CHUNK_INITIATE_TAG_LENGTH + \
+ INIT_CHUNK_ADV_REC_WINDOW_CREDIT_LENGTH + \
+ INIT_CHUNK_NUMBER_OF_OUTBOUND_STREAMS_LENGTH + \
+ INIT_CHUNK_NUMBER_OF_INBOUND_STREAMS_LENGTH + \
+ INIT_CHUNK_INITIAL_TSN_LENGTH)
+#define CHUNK_HEADER_LENGTH (CHUNK_TYPE_LENGTH + \
+ CHUNK_FLAGS_LENGTH + \
+ CHUNK_LENGTH_LENGTH)
+#define INIT_CHUNK_VARIABLE_LENGTH_PARAMETER_OFFSET (INIT_CHUNK_INITIAL_TSN_OFFSET + \
+ INIT_CHUNK_INITIAL_TSN_LENGTH )
+
+static const value_string chunk_type_values[] = {
+ { SCTP_DATA_CHUNK_ID, "DATA" },
+ { SCTP_INIT_CHUNK_ID, "INIT" },
+ { SCTP_INIT_ACK_CHUNK_ID, "INIT_ACK" },
+ { SCTP_SACK_CHUNK_ID, "SACK" },
+ { SCTP_HEARTBEAT_CHUNK_ID, "HEARTBEAT" },
+ { SCTP_HEARTBEAT_ACK_CHUNK_ID, "HEARTBEAT_ACK" },
+ { SCTP_ABORT_CHUNK_ID, "ABORT" },
+ { SCTP_SHUTDOWN_CHUNK_ID, "SHUTDOWN" },
+ { SCTP_SHUTDOWN_ACK_CHUNK_ID, "SHUTDOWN_ACK" },
+ { SCTP_ERROR_CHUNK_ID, "ERROR" },
+ { SCTP_COOKIE_ECHO_CHUNK_ID, "COOKIE_ECHO" },
+ { SCTP_COOKIE_ACK_CHUNK_ID, "COOKIE_ACK" },
+ { SCTP_ECNE_CHUNK_ID, "ECNE" },
+ { SCTP_CWR_CHUNK_ID, "CWR" },
+ { SCTP_SHUTDOWN_COMPLETE_CHUNK_ID, "SHUTDOWN_COMPLETE" },
+ { SCTP_FORWARD_TSN_CHUNK_ID, "FORWARD TSN" },
+ { SCTP_ASCONF_ACK_CHUNK_ID, "ASCONF_ACK" },
+ { SCTP_PKTDROP_CHUNK_ID, "PKTDROP" },
+ { SCTP_ASCONF_CHUNK_ID, "ASCONF" },
+ { SCTP_IETF_EXT, "IETF_EXTENSION" },
+ { SCTP_NR_SACK_CHUNK_ID, "NR_SACK" },
+ { SCTP_AUTH_CHUNK_ID, "AUTH" },
+ { 0, NULL } };
+
+
+#define FORWARD_STREAM 0
+#define BACKWARD_STREAM 1
+#define FORWARD_ADD_FORWARD_VTAG 2
+#define BACKWARD_ADD_FORWARD_VTAG 3
+#define BACKWARD_ADD_BACKWARD_VTAG 4
+#define ADDRESS_FORWARD_STREAM 5
+#define ADDRESS_BACKWARD_STREAM 6
+#define ADDRESS_FORWARD_ADD_FORWARD_VTAG 7
+#define ADDRESS_BACKWARD_ADD_FORWARD_VTAG 8
+#define ADDRESS_BACKWARD_ADD_BACKWARD_VTAG 9
+#define ASSOC_NOT_FOUND 10
+
+static sctp_allassocs_info_t sctp_tapinfo_struct = {0, NULL, FALSE, NULL};
+
+static
+void free_first(gpointer data, gpointer user_data _U_)
+{
+ g_free(data);
+}
+
+static void tsn_free(gpointer data, gpointer user_data _U_)
+{
+ tsn_t *tsn;
+
+ tsn = (tsn_t *) data;
+ if (tsn->tsns != NULL)
+ {
+ g_list_foreach(tsn->tsns, free_first, NULL);
+ g_list_free(tsn->tsns);
+ tsn->tsns=NULL;
+ }
+}
+
+
+static void reset(void *arg)
+{
+ sctp_allassocs_info_t *tapdata = arg;
+ GList* list;
+ sctp_assoc_info_t * info;
+
+ list = g_list_first(tapdata->assoc_info_list);
+ while (list)
+ {
+ info = (sctp_assoc_info_t *) (list->data);
+
+ if (info->addr1 != NULL)
+ {
+ g_list_foreach(info->addr1, free_first, NULL);
+ g_list_free(info->addr1);
+ info->addr1 = NULL;
+ }
+
+ if (info->addr2 != NULL)
+ {
+ g_list_foreach(info->addr2,free_first, NULL);
+ g_list_free(info->addr2);
+ info->addr2 = NULL;
+ }
+
+ if (info->error_info_list != NULL)
+ {
+ g_list_foreach(info->error_info_list, free_first, NULL);
+ g_list_free(info->error_info_list);
+ info->error_info_list = NULL;
+ }
+
+ if (info->frame_numbers != NULL)
+ {
+ g_list_free(info->frame_numbers);
+ info->frame_numbers = NULL;
+ }
+
+ if (info->tsn1 != NULL)
+ {
+ g_list_foreach(info->tsn1, tsn_free, NULL);
+ g_list_free(info->tsn1);
+ info->tsn1 = NULL;
+ }
+
+ if (info->tsn2 != NULL)
+ {
+ g_list_foreach(info->tsn2, tsn_free, NULL);
+ g_list_free(info->tsn2);
+ info->tsn2 = NULL;
+ }
+
+ if (info->sack1 != NULL)
+ {
+ g_list_foreach(info->sack1, tsn_free, NULL);
+ g_list_free(info->sack1);
+ info->sack1 = NULL;
+ }
+
+ if (info->sack2 != NULL)
+ {
+ g_list_foreach(info->sack2, tsn_free, NULL);
+ g_list_free(info->sack2);
+ info->sack2 = NULL;
+ }
+
+ if (info->sort_tsn1 != NULL)
+ g_ptr_array_free(info->sort_tsn1, TRUE);
+
+ if (info->sort_tsn2 != NULL)
+ g_ptr_array_free(info->sort_tsn2, TRUE);
+
+ if (info->sort_sack1 != NULL)
+ g_ptr_array_free(info->sort_sack1, TRUE);
+
+ if (info->sort_sack2 != NULL)
+ g_ptr_array_free(info->sort_sack2, TRUE);
+
+ if (info->min_max != NULL)
+ {
+ g_slist_foreach(info->min_max, free_first, NULL);
+ info->min_max = NULL;
+ }
+
+ g_free(list->data);
+ list = g_list_next(list);
+ }
+ g_list_free(tapdata->assoc_info_list);
+ tapdata->sum_tvbs = 0;
+ tapdata->assoc_info_list = NULL;
+}
+
+
+static sctp_assoc_info_t *calc_checksum(struct _sctp_info *check_data, sctp_assoc_info_t *data)
+{
+ gboolean ok = FALSE;
+
+ if (check_data->adler32_calculated)
+ {
+ data->n_adler32_calculated++;
+ if (check_data->adler32_correct)
+ data->n_adler32_correct++;
+ }
+ if (check_data->crc32c_calculated)
+ {
+ data->n_crc32c_calculated++;
+ if (check_data->crc32c_correct)
+ data->n_crc32c_correct++;
+ }
+ if (data->n_adler32_calculated > 0)
+ {
+ if ((float)(data->n_adler32_correct*1.0/data->n_adler32_calculated) > 0.5)
+ {
+ g_strlcpy(data->checksum_type,"ADLER32",8);
+ data->n_checksum_errors=(data->n_adler32_calculated-data->n_adler32_correct);
+ ok = TRUE;
+ }
+ }
+
+ if (data->n_crc32c_calculated>0)
+ {
+ if ((float)(data->n_crc32c_correct*1.0/data->n_crc32c_calculated) > 0.5)
+ {
+ g_strlcpy(data->checksum_type,"CRC32C",8);
+ data->n_checksum_errors=data->n_crc32c_calculated-data->n_crc32c_correct;
+ ok = TRUE;
+ }
+ }
+
+ if (!ok)
+ {
+ g_strlcpy(data->checksum_type,"UNKNOWN",8);
+ data->n_checksum_errors=0;
+ }
+
+ return(data);
+
+}
+
+
+/* XXX: Some versions of gcc warn about "breaking strict aliasing rules"
+ for 'a' in the following (given the way this function is called).
+ As a workaround we'll define the function parameters to match
+ how this function is actually called. */
+/*******
+static gint sctp_assoc_vtag_cmp(gconstpointer aa, gconstpointer bb)
+{
+ const struct _sctp_assoc_info* a = aa;
+ const struct _sctp_assoc_info* b = bb;
+ if (a == b)
+ return(FORWARD_STREAM);
+********/
+
+static gint sctp_assoc_vtag_cmp(const sctp_tmp_info_t *a, const sctp_assoc_info_t *b)
+{
+
+ if (a == NULL || b == NULL)
+ return(ASSOC_NOT_FOUND);
+
+ if ((a->port1 == b->port1) &&
+ (a->port2 == b->port2) &&
+ (a->verification_tag1 == b->verification_tag1) && a->verification_tag1==0 && a->initiate_tag != 0 &&
+ (a->initiate_tag != b->initiate_tag ))
+ return(ASSOC_NOT_FOUND); /* two INITs that belong to different assocs */
+
+ /* assoc known*/
+ if ((a->port1 == b->port1) &&
+ (a->port2 == b->port2) &&
+ (a->verification_tag1 == b->verification_tag1) &&
+ ((a->verification_tag1 != 0 ||
+ (b->verification_tag2 != 0))))
+ return(FORWARD_STREAM);
+
+ /* ABORT, vtag reflected */
+ if ((a->port1 == b->port1) &&
+ (a->port2 == b->port2) &&
+ (a->verification_tag2 == b->verification_tag2) &&
+ (a->verification_tag1 == 0 && b->verification_tag1 != 0))
+ return(FORWARD_STREAM);
+
+ if ((a->port1 == b->port2) &&
+ (a->port2 == b->port1) &&
+ (a->verification_tag1 == b->verification_tag2) &&
+ (a->verification_tag1 != 0))
+ return(BACKWARD_STREAM);
+
+ if ((a->port1 == b->port2) &&
+ (a->port2 == b->port1) &&
+ (a->verification_tag2 == b->verification_tag1) &&
+ (a->verification_tag2 != 0))
+ return(BACKWARD_STREAM);
+
+ /* ABORT, vtag reflected */
+ if ((a->port1 == b->port2) &&
+ (a->port2 == b->port1) &&
+ (a->verification_tag2 == b->verification_tag1) &&
+ (a->verification_tag1 == 0 && b->verification_tag2 != 0))
+ return(BACKWARD_STREAM);
+
+ /*forward stream verifivation tag can be added*/
+ if ((a->port1 == b->port1) &&
+ (a->port2 == b->port2) &&
+ (a->verification_tag1 != 0) &&
+ (b->verification_tag1 == 0) &&
+ (b->verification_tag2 !=0))
+ return (FORWARD_ADD_FORWARD_VTAG);
+
+ if ((a->port1 == b->port2) &&
+ (a->port2 == b->port1) &&
+ (a->verification_tag1 == b->verification_tag2) &&
+ (b->verification_tag1 == 0))
+ return (BACKWARD_ADD_FORWARD_VTAG);
+
+ /*backward stream verification tag can be added */
+ if ((a->port1 == b->port2) &&
+ (a->port2 == b->port1) &&
+ (a->verification_tag1 !=0) &&
+ (b->verification_tag1 != 0) &&
+ (b->verification_tag2 == 0))
+ return(BACKWARD_ADD_BACKWARD_VTAG);
+
+ return(ASSOC_NOT_FOUND);
+}
+
+static sctp_assoc_info_t * find_assoc(sctp_tmp_info_t * needle)
+{
+ sctp_allassocs_info_t *assoc_info;
+ sctp_assoc_info_t *info = NULL;
+ GList* list;
+ guint8 cmp;
+
+ assoc_info = &sctp_tapinfo_struct;
+ if ((list = g_list_last(assoc_info->assoc_info_list))!=NULL)
+ {
+ while (list)
+ {
+ cmp=sctp_assoc_vtag_cmp(needle, (sctp_assoc_info_t*)(list->data));
+
+ /*if (cmp==ASSOC_NOT_FOUND)
+ {
+ cmp=sctp_assoc_address_cmp(needle, (sctp_assoc_info_t*)(list->data));
+ }*/
+ switch (cmp)
+ {
+ case FORWARD_STREAM:
+ info = (sctp_assoc_info_t*)(list->data);
+ info->direction = 1;
+ return info;
+ case BACKWARD_STREAM:
+ info = (sctp_assoc_info_t*)(list->data);
+ info->direction = 2;
+ return info;
+ case FORWARD_ADD_FORWARD_VTAG:
+ info = (sctp_assoc_info_t*)(list->data);
+ info->verification_tag1=needle->verification_tag1;
+ info->direction = 1;
+ return info;
+ case BACKWARD_ADD_FORWARD_VTAG:
+ info = (sctp_assoc_info_t*)(list->data);
+ info->verification_tag1=needle->verification_tag1;
+ info->direction = 2;
+ return info;
+ case BACKWARD_ADD_BACKWARD_VTAG:
+ info = (sctp_assoc_info_t*)(list->data);
+ info->verification_tag2=needle->verification_tag1;
+ info->direction = 2;
+ return info;
+ case ADDRESS_FORWARD_STREAM:
+ info = (sctp_assoc_info_t*)(list->data);
+ info->direction = 1;
+ info->check_address=TRUE;
+ return info;
+ case ADDRESS_BACKWARD_STREAM:
+ info = (sctp_assoc_info_t*)(list->data);
+ info->direction = 2;
+ info->check_address=TRUE;
+ return info;
+ case ADDRESS_FORWARD_ADD_FORWARD_VTAG:
+ info = (sctp_assoc_info_t*)(list->data);
+ info->verification_tag1=needle->verification_tag1;
+ info->direction = 1;
+ info->check_address=TRUE;
+ return info;
+ case ADDRESS_BACKWARD_ADD_FORWARD_VTAG:
+ info = (sctp_assoc_info_t*)(list->data);
+ info->verification_tag1=needle->verification_tag1;
+ info->direction = 2;
+ info->check_address=TRUE;
+ return info;
+ case ADDRESS_BACKWARD_ADD_BACKWARD_VTAG:
+ info = (sctp_assoc_info_t*)(list->data);
+ info->verification_tag2=needle->verification_tag1;
+ info->direction = 2;
+ info->check_address=TRUE;
+ return info;
+ }
+
+ list = g_list_previous(list);
+ }
+ }
+ return NULL;
+}
+
+static sctp_assoc_info_t * add_chunk_count(address * vadd, sctp_assoc_info_t * info, guint32 direction, guint32 type)
+{
+ GList *list;
+ address *v=NULL;
+ sctp_addr_chunk *ch=NULL;
+ guint8 * dat;
+ int i;
+
+ list = g_list_first(info->addr_chunk_count);
+
+ while (list)
+ {
+ ch = (sctp_addr_chunk *)(list->data);
+ if (ch->direction == direction)
+ {
+ v = (address *) (ch->addr);
+ if (ADDRESSES_EQUAL(vadd, v))
+ {
+ if (IS_SCTP_CHUNK_TYPE(type))
+ ch->addr_count[type]++;
+ else
+ ch->addr_count[OTHER_CHUNKS_INDEX]++;
+ return info;
+ }
+ else
+ {
+ list = g_list_next(list);
+ }
+ }
+ else
+ list = g_list_next(list);
+ }
+ ch = g_malloc(sizeof(sctp_addr_chunk));
+ ch->direction = direction;
+ ch->addr = g_malloc(sizeof(address));
+ ch->addr->type = vadd->type;
+ ch->addr->len = vadd->len;
+ dat = g_malloc(vadd->len);
+ memcpy(dat, vadd->data, vadd->len);
+ ch->addr->data = dat;
+ for (i=0; i < NUM_CHUNKS; i++)
+ ch->addr_count[i] = 0;
+
+ if (IS_SCTP_CHUNK_TYPE(type))
+ ch->addr_count[type]++;
+ else
+ ch->addr_count[OTHER_CHUNKS_INDEX]++;
+
+ info->addr_chunk_count = g_list_append(info->addr_chunk_count, ch);
+ return info;
+}
+
+static sctp_assoc_info_t * add_address(address * vadd, sctp_assoc_info_t *info, guint8 direction)
+{
+ GList *list;
+ address *v=NULL;
+
+ if (direction == 1)
+ list = g_list_first(info->addr1);
+ else
+ list = g_list_first(info->addr2);
+
+ while (list)
+ {
+ v = (address *) (list->data);
+ if (ADDRESSES_EQUAL(vadd, v)) {
+ g_free(vadd);
+ return info;
+ }
+ list = g_list_next(list);
+ }
+
+ if (direction == 1)
+ info->addr1 = g_list_append(info->addr1, vadd);
+ else if (direction==2)
+ info->addr2 = g_list_append(info->addr2, vadd);
+
+ return info;
+}
+
+static int
+packet(void *tapdata _U_, packet_info *pinfo , epan_dissect_t *edt _U_ , const void *data)
+{
+ struct _sctp_info *sctp_info;
+ guint32 chunk_number = 0, tsnumber,framenumber;
+ sctp_tmp_info_t tmp_info;
+ sctp_assoc_info_t *info = NULL;
+ sctp_error_info_t *error = NULL;
+ guint16 type, length;
+ address *store = NULL;
+ tsn_t *tsn = NULL;
+ tsn_t *sack = NULL;
+ guint8 *t_s_n = NULL;
+ gboolean sackchunk = FALSE;
+ gboolean datachunk = FALSE;
+ struct tsn_sort *tsn_s;
+ guint8* addr = NULL;
+ int i;
+ guint8 idx = 0;
+
+ sctp_info = (struct _sctp_info *) data;
+
+ framenumber=pinfo->fd->num;
+
+ type = sctp_info->ip_src.type;
+
+ if (type == AT_IPv4)
+ {
+ tmp_info.src.type = AT_IPv4;
+ tmp_info.src.len = 4;
+ }
+ else if (type == AT_IPv6)
+ {
+ tmp_info.src.type = AT_IPv6;
+ tmp_info.src.len = 16;
+ }
+ else
+ {
+ tmp_info.src.type = AT_NONE;
+ tmp_info.src.len = 0;
+ }
+
+ addr = g_malloc(tmp_info.src.len);
+ memcpy(addr, sctp_info->ip_src.data, tmp_info.src.len);
+ tmp_info.src.data = addr;
+
+ type = sctp_info->ip_dst.type;
+
+ if (type == AT_IPv4)
+ {
+ tmp_info.dst.type = AT_IPv4;
+ tmp_info.dst.len = 4;
+ }
+ else if (type == AT_IPv6)
+ {
+ tmp_info.dst.type = AT_IPv6;
+ tmp_info.dst.len = 16;
+ }
+ else
+ {
+ tmp_info.dst.type = AT_NONE;
+ tmp_info.dst.len = 0;
+ }
+
+ addr = g_malloc(tmp_info.dst.len);
+ memcpy(addr, sctp_info->ip_dst.data, tmp_info.dst.len);
+ tmp_info.dst.data = addr;
+
+ tmp_info.port1 = sctp_info->sport;
+ tmp_info.port2 = sctp_info->dport;
+
+ if (sctp_info->vtag_reflected)
+ {
+ tmp_info.verification_tag2 = sctp_info->verification_tag;
+ tmp_info.verification_tag1 = 0;
+ }
+ else
+ {
+ tmp_info.verification_tag1 = sctp_info->verification_tag;
+ tmp_info.verification_tag2 = 0;
+ }
+ tmp_info.n_tvbs = 0;
+ if (tvb_get_guint8(sctp_info->tvb[0],0) == SCTP_INIT_CHUNK_ID)
+ {
+ tmp_info.initiate_tag = tvb_get_ntohl(sctp_info->tvb[0], 4);
+ }
+ else
+ {
+ tmp_info.initiate_tag = 0;
+ }
+
+ info = find_assoc(&tmp_info);
+ if (!info)
+ {
+ tmp_info.n_tvbs = sctp_info->number_of_tvbs;
+ sctp_tapinfo_struct.sum_tvbs+=sctp_info->number_of_tvbs;
+
+ if (sctp_info->number_of_tvbs > 0)
+ {
+ info = g_malloc(sizeof(sctp_assoc_info_t));
+ memset(info, 0, sizeof(sctp_assoc_info_t));
+ info->src.type = tmp_info.src.type;
+ info->src.len = tmp_info.src.len;
+ addr = g_malloc(tmp_info.dst.len);
+ memcpy(addr,(tmp_info.src.data), tmp_info.src.len);
+ info->src.data = addr;
+ info->dst.type = tmp_info.dst.type;
+ info->dst.len = tmp_info.dst.len;
+ addr = g_malloc(tmp_info.dst.len);
+ memcpy(addr, (tmp_info.dst.data), tmp_info.dst.len);
+ info->dst.data = addr;
+ info->port1 = tmp_info.port1;
+ info->port2 = tmp_info.port2;
+ info->verification_tag1 = tmp_info.verification_tag1;
+ info->verification_tag2 = tmp_info.verification_tag2;
+ info->initiate_tag = tmp_info.initiate_tag;
+ info->n_tvbs = tmp_info.n_tvbs;
+ info->init = FALSE;
+ info->initack = FALSE;
+ info->check_address = FALSE;
+ info->direction = 0;
+ info = calc_checksum(sctp_info, info);
+ info->n_packets = 1;
+ info->error_info_list = NULL;
+ info->min_secs = 0xffffffff;
+ info->min_usecs = 0xffffffff;
+ info->max_secs = 0;
+ info->max_usecs = 0;
+ info->min_tsn2 = 0xFFFFFFFF;
+ info->min_tsn1 = 0xffffffff;
+ info->max_tsn1 = 0;
+ info->max_tsn2 = 0;
+ info->max_bytes1 = 0;
+ info->max_bytes2 = 0;
+ info->n_data_chunks = 0;
+ info->n_data_bytes = 0;
+ info->n_data_chunks_ep1 = 0;
+ info->n_data_bytes_ep1 = 0;
+ info->n_data_chunks_ep2 = 0;
+ info->n_data_bytes_ep2 = 0;
+ info->n_sack_chunks_ep1 = 0;
+ info->n_sack_chunks_ep2 = 0;
+ info->n_array_tsn1 = 0;
+ info->n_array_tsn2 = 0;
+ info->max_window1 = 0;
+ info->max_window2 = 0;
+ info->min_max = NULL;
+ info->sort_tsn1 = g_ptr_array_new();
+ info->sort_tsn2 = g_ptr_array_new();
+ info->sort_sack1 = g_ptr_array_new();
+ info->sort_sack2 = g_ptr_array_new();
+ for (i=0; i < NUM_CHUNKS; i++)
+ {
+ info->chunk_count[i] = 0;
+ info->ep1_chunk_count[i] = 0;
+ info->ep2_chunk_count[i] = 0;
+ }
+ info->addr_chunk_count = NULL;
+
+ if (((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_INIT_CHUNK_ID) ||
+ ((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_INIT_ACK_CHUNK_ID) ||
+ ((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_DATA_CHUNK_ID) ||
+ ((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_SACK_CHUNK_ID) ||
+ ((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_NR_SACK_CHUNK_ID))
+ {
+ tsn = g_malloc(sizeof(tsn_t));
+ sack = g_malloc(sizeof(tsn_t));
+ tsn->tsns = NULL;
+ tsn->first_tsn = 0;
+ sack->tsns = NULL;
+ sack->first_tsn = 0;
+ sack->src.type=tsn->src.type = tmp_info.src.type;
+ sack->src.len=tsn->src.len = tmp_info.src.len;
+ addr = g_malloc(tmp_info.src.len);
+ memcpy(addr, tmp_info.src.data, tmp_info.src.len);
+ tsn->src.data = addr;
+ addr = g_malloc(tmp_info.src.len);
+ memcpy(addr, tmp_info.src.data, tmp_info.src.len);
+ sack->src.data = addr;
+ sack->dst.type = tsn->dst.type = tmp_info.dst.type;
+ sack->dst.len =tsn->dst.len = tmp_info.dst.len;
+ addr = g_malloc(tmp_info.dst.len);
+ memcpy(addr, tmp_info.dst.data, tmp_info.dst.len);
+ tsn->dst.data = addr;
+ addr = g_malloc(tmp_info.dst.len);
+ memcpy(addr, tmp_info.dst.data, tmp_info.dst.len);
+ sack->dst.data = addr;
+ sack->secs=tsn->secs = (guint32)pinfo->fd->rel_ts.secs;
+ sack->usecs=tsn->usecs = (guint32)pinfo->fd->rel_ts.nsecs/1000;
+ if (((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_DATA_CHUNK_ID) ||
+ ((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_SACK_CHUNK_ID) ||
+ ((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_NR_SACK_CHUNK_ID))
+ {
+ if (tsn->secs < info->min_secs)
+ {
+ info->min_secs = tsn->secs;
+ info->min_usecs = tsn->usecs;
+ }
+ else if (tsn->secs == info->min_secs && tsn->usecs < info->min_usecs)
+ info->min_usecs = tsn->usecs;
+
+ if (tsn->secs > info->max_secs)
+ {
+ info->max_secs = tsn->secs;
+ info->max_usecs = tsn->usecs;
+ }
+ else if (tsn->secs == info->max_secs && tsn->usecs > info->max_usecs)
+ info->max_usecs = tsn->usecs;
+ }
+
+ sack->frame_number = tsn->frame_number = pinfo->fd->num;
+ }
+ if ((tvb_get_guint8(sctp_info->tvb[0],0) == SCTP_INIT_CHUNK_ID) || (tvb_get_guint8(sctp_info->tvb[0],0) == SCTP_INIT_ACK_CHUNK_ID))
+ {
+ info->min_tsn1 = tvb_get_ntohl(sctp_info->tvb[0],INIT_CHUNK_INITIAL_TSN_OFFSET);
+ info->verification_tag2 = tvb_get_ntohl(sctp_info->tvb[0], INIT_CHUNK_INITIATE_TAG_OFFSET);
+ info->instream1 = tvb_get_ntohs(sctp_info->tvb[0],INIT_CHUNK_NUMBER_OF_INBOUND_STREAMS_OFFSET);
+ info->outstream1 = tvb_get_ntohs(sctp_info->tvb[0],INIT_CHUNK_NUMBER_OF_OUTBOUND_STREAMS_OFFSET);
+ for (chunk_number = 1; chunk_number < sctp_info->number_of_tvbs; chunk_number++)
+ {
+ type = tvb_get_ntohs(sctp_info->tvb[chunk_number],0);
+ if (type == IPV4ADDRESS_PARAMETER_ID)
+ {
+ store = g_malloc(sizeof (address));
+ store->type = AT_IPv4;;
+ store->len = 4;
+ store->data = g_malloc(4);
+ tvb_memcpy(sctp_info->tvb[chunk_number], (guint8 *)(store->data),IPV4_ADDRESS_OFFSET, 4);
+ info = add_address(store, info, 1);
+ }
+ else if (type == IPV6ADDRESS_PARAMETER_ID)
+ {
+ store = g_malloc(sizeof (address));
+ store->type = AT_IPv6;;
+ store->len = 16;
+ store->data = g_malloc(16);
+ tvb_memcpy(sctp_info->tvb[chunk_number], (guint8 *)(store->data),IPV6_ADDRESS_OFFSET, IPV6_ADDRESS_LENGTH);
+ info = add_address(store, info, 1);
+ }
+ }
+
+ if (tvb_get_guint8(sctp_info->tvb[0],0) == SCTP_INIT_CHUNK_ID)
+ {
+ info->init = TRUE;
+ }
+ else
+ {
+ info->initack_dir = 1;
+ info->initack = TRUE;
+ }
+
+ idx = tvb_get_guint8(sctp_info->tvb[0],0);
+ if (!IS_SCTP_CHUNK_TYPE(idx))
+ idx = OTHER_CHUNKS_INDEX;
+
+ info->chunk_count[idx]++;
+ info->ep1_chunk_count[idx]++;
+ info = add_chunk_count(&tmp_info.src, info, 1, idx);
+ }
+ else
+ {
+ if (((tvb_get_guint8(sctp_info->tvb[0],0)) != SCTP_INIT_CHUNK_ID) &&
+ ((tvb_get_guint8(sctp_info->tvb[0],0)) != SCTP_INIT_ACK_CHUNK_ID) &&
+ ((tvb_get_guint8(sctp_info->tvb[0],0)) != SCTP_DATA_CHUNK_ID) &&
+ ((tvb_get_guint8(sctp_info->tvb[0],0)) != SCTP_SACK_CHUNK_ID) &&
+ ((tvb_get_guint8(sctp_info->tvb[0],0)) != SCTP_NR_SACK_CHUNK_ID))
+ {
+ tsn = g_malloc(sizeof(tsn_t));
+ sack = g_malloc(sizeof(tsn_t));
+ tsn->tsns = NULL;
+ sack->tsns = NULL;
+ tsn->first_tsn = 0;
+ sack->first_tsn = 0;
+ }
+ for (chunk_number = 0; chunk_number < sctp_info->number_of_tvbs; chunk_number++)
+ {
+ idx = tvb_get_guint8(sctp_info->tvb[0],0);
+ if (!IS_SCTP_CHUNK_TYPE(idx))
+ idx = OTHER_CHUNKS_INDEX;
+
+ info->chunk_count[idx]++;
+ info->ep1_chunk_count[idx]++;
+ info = add_chunk_count(&tmp_info.src, info, 1, idx);
+
+ if (tvb_get_guint8(sctp_info->tvb[chunk_number],0) == SCTP_DATA_CHUNK_ID)
+ {
+ length = tvb_get_ntohs(sctp_info->tvb[chunk_number], CHUNK_LENGTH_OFFSET)-DATA_CHUNK_HEADER_LENGTH;
+ info->n_data_chunks++;
+ info->n_data_bytes+=length;
+ info->outstream1 = tvb_get_ntohs((sctp_info->tvb)[chunk_number], DATA_CHUNK_STREAM_ID_OFFSET)+1;
+ tsnumber = tvb_get_ntohl((sctp_info->tvb)[chunk_number], DATA_CHUNK_TSN_OFFSET);
+ if (tsnumber < info->min_tsn1)
+ info->min_tsn1 = tsnumber;
+ if (tsnumber > info->max_tsn1)
+ {
+ length=tvb_get_ntohs(sctp_info->tvb[chunk_number], CHUNK_LENGTH_OFFSET)-DATA_CHUNK_HEADER_LENGTH;
+ info->n_data_chunks_ep1++;
+ info->n_data_bytes_ep1+=length;
+ info->max_tsn1 = tsnumber;
+ }
+ if (tsn->first_tsn == 0)
+ tsn->first_tsn = tsnumber;
+ t_s_n = g_malloc(16);
+ tvb_memcpy(sctp_info->tvb[chunk_number], (guint8 *)(t_s_n),0, 16);
+ tsn->tsns = g_list_append(tsn->tsns, t_s_n);
+ datachunk = TRUE;
+ tsn_s = g_malloc(sizeof(struct tsn_sort));
+ tsn_s->tsnumber = tsnumber;
+ tsn_s->secs = tsn->secs = (guint32)pinfo->fd->rel_ts.secs;
+ tsn_s->usecs = tsn->usecs = (guint32)pinfo->fd->rel_ts.nsecs/1000;
+ tsn_s->offset = 0;
+ tsn_s->framenumber = framenumber;
+ tsn_s->length = length-DATA_CHUNK_HEADER_LENGTH;
+ if (tsn->secs < info->min_secs)
+ {
+ info->min_secs = tsn->secs;
+ info->min_usecs = tsn->usecs;
+ }
+ else if (tsn->secs == info->min_secs && tsn->usecs < info->min_usecs)
+ info->min_usecs = tsn->usecs;
+
+ if (tsn->secs > info->max_secs)
+ {
+ info->max_secs = tsn->secs;
+ info->max_usecs = tsn->usecs;
+ }
+ else if (tsn->secs == info->max_secs && tsn->usecs > info->max_usecs)
+ info->max_usecs = tsn->usecs;
+ g_ptr_array_add(info->sort_tsn1, tsn_s);
+ info->n_array_tsn1++;
+ }
+ if ((tvb_get_guint8(sctp_info->tvb[chunk_number],0) == SCTP_SACK_CHUNK_ID) ||
+ (tvb_get_guint8(sctp_info->tvb[chunk_number],0) == SCTP_NR_SACK_CHUNK_ID) )
+ {
+ tsnumber = tvb_get_ntohl((sctp_info->tvb)[chunk_number], SACK_CHUNK_CUMULATIVE_TSN_ACK_OFFSET);
+ if (tsnumber < info->min_tsn2)
+ info->min_tsn2 = tsnumber;
+ if (tsnumber > info->max_tsn2)
+ info->max_tsn2 = tsnumber;
+ info->n_sack_chunks_ep2++;
+ length = tvb_get_ntohs(sctp_info->tvb[chunk_number], CHUNK_LENGTH_OFFSET);
+ if (sack->first_tsn == 0)
+ sack->first_tsn = tsnumber;
+ t_s_n = g_malloc(length);
+ tvb_memcpy(sctp_info->tvb[chunk_number], (guint8 *)(t_s_n),0, length);
+ sack->tsns = g_list_append(sack->tsns, t_s_n);
+ sackchunk = TRUE;
+ tsn_s = g_malloc(sizeof(struct tsn_sort));
+ tsn_s->tsnumber = tsnumber;
+ tsn_s->secs = tsn->secs = (guint32)pinfo->fd->rel_ts.secs;
+ tsn_s->usecs = tsn->usecs = (guint32)pinfo->fd->rel_ts.nsecs/1000;
+ tsn_s->offset = 0;
+ tsn_s->framenumber = framenumber;
+ tsn_s->length = tvb_get_ntohl(sctp_info->tvb[chunk_number], SACK_CHUNK_ADV_REC_WINDOW_CREDIT_OFFSET);
+ if (tsn_s->length > info->max_window1)
+ info->max_window1 = tsn_s->length;
+ if (tsn->secs < info->min_secs)
+ {
+ info->min_secs = tsn->secs;
+ info->min_usecs = tsn->usecs;
+ }
+ else if (tsn->secs == info->min_secs && tsn->usecs < info->min_usecs)
+ info->min_usecs = tsn->usecs;
+
+ if (tsn->secs > info->max_secs)
+ {
+ info->max_secs = tsn->secs;
+ info->max_usecs = tsn->usecs;
+ }
+ else if (tsn->secs == info->max_secs && tsn->usecs > info->max_usecs)
+ info->max_usecs = tsn->usecs;
+ g_ptr_array_add(info->sort_sack2, tsn_s);
+ info->n_sack_chunks_ep2++;
+ }
+ }
+ }
+ if (info->verification_tag1 != 0 || info->verification_tag2 != 0)
+ {
+ store = g_malloc(sizeof (address));
+ store->type = tmp_info.src.type;
+ store->len = tmp_info.src.len;
+ addr = g_malloc(tmp_info.src.len);
+ memcpy(addr,(tmp_info.src.data),tmp_info.src.len);
+ store->data = addr;
+ info = add_address(store, info, 1);
+ store = g_malloc(sizeof (address));
+ store->type = tmp_info.dst.type;
+ store->len = tmp_info.dst.len;
+ addr = g_malloc(tmp_info.dst.len);
+ memcpy(addr,(tmp_info.dst.data),tmp_info.dst.len);
+ store->data = addr;
+ info = add_address(store, info, 2);
+ info->frame_numbers=g_list_prepend(info->frame_numbers,&(pinfo->fd->num));
+ if (datachunk == TRUE)
+ info->tsn1 = g_list_prepend(info->tsn1, tsn);
+ if (sackchunk == TRUE)
+ info->sack2 = g_list_prepend(info->sack2, sack);
+ sctp_tapinfo_struct.assoc_info_list = g_list_append(sctp_tapinfo_struct.assoc_info_list, info);
+ }
+ else
+ {
+ error = g_malloc(sizeof(sctp_error_info_t));
+ error->frame_number = pinfo->fd->num;
+ error->chunk_info[0] = '\0';
+ if ((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_INIT_CHUNK_ID)
+ g_strlcpy(error->chunk_info, val_to_str(tvb_get_guint8(sctp_info->tvb[0],0),chunk_type_values,"Reserved"), 200);
+ else
+ for (chunk_number = 0; chunk_number < sctp_info->number_of_tvbs; chunk_number++)
+ g_strlcat(error->chunk_info, val_to_str(tvb_get_guint8(sctp_info->tvb[chunk_number],0),chunk_type_values,"Reserved"), 200);
+ error->info_text = "INFOS";
+ info->error_info_list = g_list_append(info->error_info_list, error);
+ }
+ }
+ } /* endif (!info) */
+ else
+ {
+ if (((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_INIT_CHUNK_ID) ||
+ ((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_INIT_ACK_CHUNK_ID) ||
+ ((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_DATA_CHUNK_ID) ||
+ ((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_SACK_CHUNK_ID) ||
+ ((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_NR_SACK_CHUNK_ID))
+ {
+
+ tsn = g_malloc(sizeof(tsn_t));
+ sack = g_malloc(sizeof(tsn_t));
+ tsn->tsns = NULL;
+ tsn->first_tsn = 0;
+ sack->tsns = NULL;
+ sack->first_tsn = 0;
+ sack->src.type = tsn->src.type = tmp_info.src.type;
+ sack->src.len = tsn->src.len = tmp_info.src.len;
+ addr = g_malloc(tmp_info.src.len);
+ memcpy(addr, tmp_info.src.data, tmp_info.src.len);
+ tsn->src.data = addr;
+ addr = g_malloc(tmp_info.src.len);
+ memcpy(addr, tmp_info.src.data, tmp_info.src.len);
+ sack->src.data = addr;
+ sack->dst.type = tsn->dst.type = tmp_info.dst.type;
+ sack->dst.len = tsn->dst.len = tmp_info.dst.len;
+ addr = g_malloc(tmp_info.dst.len);
+ memcpy(addr, tmp_info.dst.data, tmp_info.dst.len);
+ tsn->dst.data = addr;
+ addr = g_malloc(tmp_info.dst.len);
+ memcpy(addr, tmp_info.dst.data, tmp_info.dst.len);
+ sack->dst.data = addr;
+ sack->secs=tsn->secs = (guint32)pinfo->fd->rel_ts.secs;
+ sack->usecs=tsn->usecs = (guint32)pinfo->fd->rel_ts.nsecs/1000;
+ if (((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_DATA_CHUNK_ID) ||
+ ((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_SACK_CHUNK_ID) ||
+ ((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_NR_SACK_CHUNK_ID))
+ {
+ if (tsn->secs < info->min_secs)
+ {
+ info->min_secs = tsn->secs;
+ info->min_usecs = tsn->usecs;
+ }
+ else if (tsn->secs == info->min_secs && tsn->usecs < info->min_usecs)
+ info->min_usecs = tsn->usecs;
+
+ if (tsn->secs > info->max_secs)
+ {
+ info->max_secs = tsn->secs;
+ info->max_usecs = tsn->usecs;
+ }
+ else if (tsn->secs == info->max_secs && tsn->usecs > info->max_usecs)
+ info->max_usecs = tsn->usecs;
+ }
+ sack->frame_number = tsn->frame_number = pinfo->fd->num;
+ }
+ info->frame_numbers = g_list_prepend(info->frame_numbers,&(pinfo->fd->num));
+
+ store = g_malloc(sizeof (address));
+ store->type = tmp_info.src.type;
+ store->len = tmp_info.src.len;
+ addr = g_malloc(tmp_info.src.len);
+ memcpy(addr,(tmp_info.src.data),tmp_info.src.len);
+ store->data = addr;
+
+ if (info->direction == 1)
+ info = add_address(store, info, 1);
+ else if (info->direction == 2)
+ info = add_address(store, info, 2);
+
+ store = g_malloc(sizeof (address));
+ store->type = tmp_info.dst.type;
+ store->len = tmp_info.dst.len;
+ addr = g_malloc(tmp_info.dst.len);
+ memcpy(addr,(tmp_info.dst.data),tmp_info.dst.len);
+ store->data = addr;
+
+ if (info->direction == 1)
+ info = add_address(store, info, 2);
+ else if (info->direction == 2)
+ info = add_address(store, info, 1);
+
+ if (((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_INIT_ACK_CHUNK_ID) ||
+ ((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_INIT_CHUNK_ID))
+ {
+ tsnumber = tvb_get_ntohl((sctp_info->tvb)[chunk_number], INIT_CHUNK_INITIAL_TSN_OFFSET);
+
+ if (info->direction == 2)
+ {
+ if (tsnumber < info->min_tsn2)
+ info->min_tsn2 = tsnumber;
+ if (tsnumber > info->max_tsn2)
+ info->max_tsn2 = tsnumber;
+ info->instream2 = tvb_get_ntohs(sctp_info->tvb[0],INIT_CHUNK_NUMBER_OF_INBOUND_STREAMS_OFFSET);
+ info->outstream2 = tvb_get_ntohs(sctp_info->tvb[0],INIT_CHUNK_NUMBER_OF_OUTBOUND_STREAMS_OFFSET);
+ /*info->initack_dir=2;*/
+ info->tsn2 = g_list_prepend(info->tsn2, tsn);
+ }
+ else if (info->direction == 1)
+ {
+ if (tsnumber < info->min_tsn1)
+ info->min_tsn1 = tsnumber;
+ if (tsnumber > info->max_tsn1)
+ info->max_tsn1 = tsnumber;
+ info->instream1 = tvb_get_ntohs(sctp_info->tvb[0],INIT_CHUNK_NUMBER_OF_INBOUND_STREAMS_OFFSET);
+ info->outstream1 = tvb_get_ntohs(sctp_info->tvb[0],INIT_CHUNK_NUMBER_OF_OUTBOUND_STREAMS_OFFSET);
+ /*info->initack_dir=1;*/
+ info->tsn1 = g_list_prepend(info->tsn1, tsn);
+ }
+
+ idx = tvb_get_guint8(sctp_info->tvb[0],0);
+ if (!IS_SCTP_CHUNK_TYPE(idx))
+ idx = OTHER_CHUNKS_INDEX;
+ info->chunk_count[idx]++;
+ if (info->direction == 1)
+ info->ep1_chunk_count[idx]++;
+ else
+ info->ep2_chunk_count[idx]++;
+ info = add_chunk_count(&tmp_info.src, info, info->direction, idx);
+ for (chunk_number = 1; chunk_number < sctp_info->number_of_tvbs; chunk_number++)
+ {
+ type = tvb_get_ntohs(sctp_info->tvb[chunk_number],0);
+ if (type == IPV4ADDRESS_PARAMETER_ID)
+ {
+ store = g_malloc(sizeof (address));
+ store->type = AT_IPv4;;
+ store->len = 4;
+ store->data = g_malloc(4);
+ tvb_memcpy(sctp_info->tvb[chunk_number], (guint8 *)(store->data),IPV4_ADDRESS_OFFSET, 4);
+ info = add_address(store, info, info->direction);
+ }
+ else if (type == IPV6ADDRESS_PARAMETER_ID)
+ {
+ store = g_malloc(sizeof (address));
+ store->type = AT_IPv6;;
+ store->len = 16;
+ store->data = g_malloc(16);
+ tvb_memcpy(sctp_info->tvb[chunk_number], (guint8 *)(store->data),IPV6_ADDRESS_OFFSET, IPV6_ADDRESS_LENGTH);
+ info = add_address(store, info, info->direction);
+ }
+ }
+ if ((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_INIT_ACK_CHUNK_ID)
+ {
+ info->initack = TRUE;
+ info->initack_dir = info->direction;
+ }
+ else
+ if ((tvb_get_guint8(sctp_info->tvb[0],0)) == SCTP_INIT_CHUNK_ID)
+ {
+ info->init = TRUE;
+ }
+ }
+ else
+ {
+ if (((tvb_get_guint8(sctp_info->tvb[0],0)) != SCTP_INIT_ACK_CHUNK_ID) &&
+ ((tvb_get_guint8(sctp_info->tvb[0],0)) != SCTP_DATA_CHUNK_ID) &&
+ ((tvb_get_guint8(sctp_info->tvb[0],0)) != SCTP_SACK_CHUNK_ID) &&
+ ((tvb_get_guint8(sctp_info->tvb[0],0)) != SCTP_NR_SACK_CHUNK_ID))
+ {
+ sack = g_malloc(sizeof(tsn_t));
+ sack->tsns = NULL;
+ sack->first_tsn = 0;
+ tsn = g_malloc(sizeof(tsn_t));
+ tsn->tsns = NULL;
+ tsn->first_tsn = 0;
+ }
+ for (chunk_number = 0; chunk_number < sctp_info->number_of_tvbs; chunk_number++)
+ {
+ idx = tvb_get_guint8(sctp_info->tvb[chunk_number],0);
+ if (!IS_SCTP_CHUNK_TYPE(idx))
+ idx = OTHER_CHUNKS_INDEX;
+
+ info->chunk_count[idx]++;
+ if (info->direction == 1)
+ info->ep1_chunk_count[idx]++;
+ else
+ info->ep2_chunk_count[idx]++;
+ info = add_chunk_count(&tmp_info.src, info,info->direction, idx);
+
+ if ((tvb_get_guint8(sctp_info->tvb[chunk_number],0)) == SCTP_DATA_CHUNK_ID)
+ {
+ tsnumber = tvb_get_ntohl((sctp_info->tvb)[chunk_number], DATA_CHUNK_TSN_OFFSET);
+ if (tsn->first_tsn == 0)
+ tsn->first_tsn = tsnumber;
+ t_s_n = g_malloc(16);
+ tvb_memcpy(sctp_info->tvb[chunk_number], (guint8 *)(t_s_n),0, 16);
+ tsn->tsns = g_list_append(tsn->tsns, t_s_n);
+ datachunk = TRUE;
+ length=tvb_get_ntohs(sctp_info->tvb[chunk_number], CHUNK_LENGTH_OFFSET)-DATA_CHUNK_HEADER_LENGTH;
+ info->n_data_chunks++;
+ info->n_data_bytes+=length;
+ tsn_s = g_malloc(sizeof(struct tsn_sort));
+ tsn_s->tsnumber = tsnumber;
+ tsn_s->secs = tsn->secs = (guint32)pinfo->fd->rel_ts.secs;
+ tsn_s->usecs = tsn->usecs = (guint32)pinfo->fd->rel_ts.nsecs/1000;
+ tsn_s->offset = 0;
+ tsn_s->framenumber = framenumber;
+ tsn_s->length = length;
+
+ if (tsn->secs < info->min_secs)
+ {
+ info->min_secs = tsn->secs;
+ info->min_usecs = tsn->usecs;
+ }
+ else if (tsn->secs == info->min_secs && tsn->usecs < info->min_usecs)
+ info->min_usecs = tsn->usecs;
+
+ if (tsn->secs > info->max_secs)
+ {
+ info->max_secs = tsn->secs;
+ info->max_usecs = tsn->usecs;
+ }
+ else if (tsn->secs == info->max_secs && tsn->usecs > info->max_usecs)
+ info->max_usecs = tsn->usecs;
+
+ if (info->direction == 1)
+ {
+ if(tsnumber < info->min_tsn1)
+ info->min_tsn1 = tsnumber;
+ if ((info->init == TRUE || (info->initack == TRUE && info->initack_dir == 1))&& tsnumber >= info->min_tsn1 && tsnumber <= info->max_tsn1)
+ {
+ length = tvb_get_ntohs(sctp_info->tvb[chunk_number], CHUNK_LENGTH_OFFSET)-DATA_CHUNK_HEADER_LENGTH;
+ info->n_data_chunks_ep1++;
+ info->n_data_bytes_ep1 += length;
+ }
+ if(tsnumber > info->max_tsn1)
+ {
+ info->max_tsn1 = tsnumber;
+ length = tvb_get_ntohs(sctp_info->tvb[chunk_number], CHUNK_LENGTH_OFFSET)-DATA_CHUNK_HEADER_LENGTH;
+ info->n_data_chunks_ep1++;
+ info->n_data_bytes_ep1 += length;
+ }
+ if (info->init == FALSE)
+ info->outstream1 = tvb_get_ntohs((sctp_info->tvb)[chunk_number], DATA_CHUNK_STREAM_ID_OFFSET)+1;
+ if (info->initack == FALSE)
+ info->instream2 = tvb_get_ntohs((sctp_info->tvb)[chunk_number], DATA_CHUNK_STREAM_ID_OFFSET)+1;
+
+ g_ptr_array_add(info->sort_tsn1, tsn_s);
+ info->n_array_tsn1++;
+ }
+ else if (info->direction == 2)
+ {
+
+ if(tsnumber < info->min_tsn2)
+ info->min_tsn2 = tsnumber;
+
+ if ((info->initack == TRUE && info->initack_dir == 2)&& tsnumber >= info->min_tsn2 && tsnumber <= info->max_tsn2)
+ {
+ length = tvb_get_ntohs(sctp_info->tvb[chunk_number], CHUNK_LENGTH_OFFSET)-DATA_CHUNK_HEADER_LENGTH;
+ info->n_data_chunks_ep2++;
+ info->n_data_bytes_ep2+=length;
+ }
+ if(tsnumber > info->max_tsn2)
+ {
+ info->max_tsn2 = tsnumber;
+ length = tvb_get_ntohs(sctp_info->tvb[chunk_number], CHUNK_LENGTH_OFFSET)-DATA_CHUNK_HEADER_LENGTH;
+ info->n_data_chunks_ep2++;
+ info->n_data_bytes_ep2+=length;
+ }
+ if (info->init == FALSE)
+ info->instream1 = tvb_get_ntohs((sctp_info->tvb)[chunk_number], DATA_CHUNK_STREAM_ID_OFFSET)+1;
+ if (info->initack == FALSE)
+ info->outstream2 = tvb_get_ntohs((sctp_info->tvb)[chunk_number], DATA_CHUNK_STREAM_ID_OFFSET)+1;
+
+ g_ptr_array_add(info->sort_tsn2, tsn_s);
+ info->n_array_tsn2++;
+ }
+ }
+ else if ((tvb_get_guint8(sctp_info->tvb[chunk_number],0) == SCTP_SACK_CHUNK_ID) ||
+ (tvb_get_guint8(sctp_info->tvb[chunk_number],0) == SCTP_NR_SACK_CHUNK_ID))
+ {
+ tsnumber = tvb_get_ntohl((sctp_info->tvb)[chunk_number], SACK_CHUNK_CUMULATIVE_TSN_ACK_OFFSET);
+ length = tvb_get_ntohs(sctp_info->tvb[chunk_number], CHUNK_LENGTH_OFFSET);
+ if (sack->first_tsn == 0)
+ sack->first_tsn = tsnumber;
+ t_s_n = g_malloc(length);
+ tvb_memcpy(sctp_info->tvb[chunk_number], (guint8 *)(t_s_n),0, length);
+ sack->tsns = g_list_append(sack->tsns, t_s_n);
+ sackchunk = TRUE;
+ tsn_s = g_malloc(sizeof(struct tsn_sort));
+ tsn_s->tsnumber = tsnumber;
+ tsn_s->secs = tsn->secs = (guint32)pinfo->fd->rel_ts.secs;
+ tsn_s->usecs = tsn->usecs = (guint32)pinfo->fd->rel_ts.nsecs/1000;
+ tsn_s->offset = 0;
+ tsn_s->framenumber = framenumber;
+ tsn_s->length = tvb_get_ntohl(sctp_info->tvb[chunk_number], SACK_CHUNK_ADV_REC_WINDOW_CREDIT_OFFSET);
+
+ if (tsn->secs < info->min_secs)
+ {
+ info->min_secs = tsn->secs;
+ info->min_usecs = tsn->usecs;
+ }
+ else if (tsn->secs == info->min_secs && tsn->usecs < info->min_usecs)
+ info->min_usecs = tsn->usecs;
+
+ if (tsn->secs > info->max_secs)
+ {
+ info->max_secs = tsn->secs;
+ info->max_usecs = tsn->usecs;
+ }
+ else if (tsn->secs == info->max_secs && tsn->usecs > info->max_usecs)
+ info->max_usecs = tsn->usecs;
+
+
+ if (info->direction == 2)
+ {
+ if(tsnumber < info->min_tsn1)
+ info->min_tsn1 = tsnumber;
+ if(tsnumber > info->max_tsn1)
+ info->max_tsn1 = tsnumber;
+ if (tsn_s->length > info->max_window1)
+ info->max_window1 = tsn_s->length;
+ g_ptr_array_add(info->sort_sack1, tsn_s);
+ info->n_sack_chunks_ep1++;
+ }
+ else if (info->direction == 1)
+ {
+
+ if(tsnumber < info->min_tsn2)
+ info->min_tsn2 = tsnumber;
+ if(tsnumber > info->max_tsn2)
+ info->max_tsn2 = tsnumber;
+ if (tsn_s->length > info->max_window2)
+ info->max_window2 = tsn_s->length;
+ g_ptr_array_add(info->sort_sack2, tsn_s);
+ info->n_sack_chunks_ep2++;
+ }
+
+ }
+ }
+
+ }
+ if (datachunk == TRUE)
+ {
+ if (info->direction == 1)
+ info->tsn1 = g_list_prepend(info->tsn1, tsn);
+ else if (info->direction == 2)
+ info->tsn2 = g_list_prepend(info->tsn2, tsn);
+ }
+ if (sackchunk == TRUE)
+ {
+ if (info->direction == 1)
+ info->sack2 = g_list_prepend(info->sack2, sack);
+ else if(info->direction == 2)
+ info->sack1 = g_list_prepend(info->sack1, sack);
+ }
+ info->n_tvbs += sctp_info->number_of_tvbs;
+ sctp_tapinfo_struct.sum_tvbs += sctp_info->number_of_tvbs;
+ info = calc_checksum(sctp_info, info);
+ info->n_packets++;
+ }
+ return(1);
+}
+
+
+/****************************************************************************/
+void
+remove_tap_listener_sctp_stat(void)
+{
+ if (sctp_tapinfo_struct.is_registered) {
+ protect_thread_critical_region();
+ remove_tap_listener(&sctp_tapinfo_struct);
+ unprotect_thread_critical_region();
+ sctp_tapinfo_struct.is_registered = FALSE;
+ }
+}
+
+
+void sctp_stat_scan(void)
+{
+ if (!sctp_tapinfo_struct.is_registered)
+ register_tap_listener_sctp_stat();
+}
+
+const sctp_allassocs_info_t* sctp_stat_get_info(void)
+{
+ return &sctp_tapinfo_struct;
+}
+
+
+static void sctp_update(void *dummy _U_)
+{
+ if (get_stat_dlg()!=NULL)
+ sctp_stat_dlg_update();
+}
+
+void
+register_tap_listener_sctp_stat(void)
+{
+ GString *error_string;
+
+ if (!sctp_tapinfo_struct.is_registered)
+ {
+ if ((error_string = register_tap_listener("sctp", &sctp_tapinfo_struct, NULL, 0, reset, packet, sctp_update))) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ return;
+ }
+ sctp_tapinfo_struct.is_registered=TRUE;
+ }
+}
diff --git a/ui/gtk/sctp_stat.h b/ui/gtk/sctp_stat.h
new file mode 100644
index 0000000000..0c68154d03
--- /dev/null
+++ b/ui/gtk/sctp_stat.h
@@ -0,0 +1,408 @@
+/*
+ * Copyright 2004, Irene Ruengeler <i.ruengeler [AT] fh-muenster.de>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __SCTP_STAT_H__
+#define __SCTP_STAT_H__
+
+#include <epan/dissectors/packet-sctp.h>
+#include <epan/address.h>
+#ifndef _WIN32
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#else
+#ifdef HAVE_WINSOCK2_H
+#include <winsock2.h>
+#endif
+#endif
+
+#define SCTP_DATA_CHUNK_ID 0
+#define SCTP_INIT_CHUNK_ID 1
+#define SCTP_INIT_ACK_CHUNK_ID 2
+#define SCTP_SACK_CHUNK_ID 3
+#define SCTP_HEARTBEAT_CHUNK_ID 4
+#define SCTP_HEARTBEAT_ACK_CHUNK_ID 5
+#define SCTP_ABORT_CHUNK_ID 6
+#define SCTP_SHUTDOWN_CHUNK_ID 7
+#define SCTP_SHUTDOWN_ACK_CHUNK_ID 8
+#define SCTP_ERROR_CHUNK_ID 9
+#define SCTP_COOKIE_ECHO_CHUNK_ID 10
+#define SCTP_COOKIE_ACK_CHUNK_ID 11
+#define SCTP_ECNE_CHUNK_ID 12
+#define SCTP_CWR_CHUNK_ID 13
+#define SCTP_SHUTDOWN_COMPLETE_CHUNK_ID 14
+#define SCTP_AUTH_CHUNK_ID 15
+#define SCTP_NR_SACK_CHUNK_ID 16
+#define SCTP_FORWARD_TSN_CHUNK_ID 0xc0
+#define SCTP_ASCONF_ACK_CHUNK_ID 0x80
+#define SCTP_PKTDROP_CHUNK_ID 0x81
+#define SCTP_ASCONF_CHUNK_ID 0xc1
+#define SCTP_IETF_EXT 255
+
+#define IS_SCTP_CHUNK_TYPE(t) \
+ (((t) <= 16) || ((t) == 0xC0) || ((t) == 0xC1) || ((t) == 0x80) || ((t) == 0x81))
+
+#define CHUNK_TYPE_LENGTH 1
+#define CHUNK_FLAGS_LENGTH 1
+#define CHUNK_LENGTH_LENGTH 2
+
+#define CHUNK_HEADER_OFFSET 0
+#define CHUNK_TYPE_OFFSET CHUNK_HEADER_OFFSET
+#define CHUNK_FLAGS_OFFSET (CHUNK_TYPE_OFFSET + CHUNK_TYPE_LENGTH)
+#define CHUNK_LENGTH_OFFSET (CHUNK_FLAGS_OFFSET + CHUNK_FLAGS_LENGTH)
+#define CHUNK_VALUE_OFFSET (CHUNK_LENGTH_OFFSET + CHUNK_LENGTH_LENGTH)
+
+#define INIT_CHUNK_INITIATE_TAG_LENGTH 4
+#define INIT_CHUNK_ADV_REC_WINDOW_CREDIT_LENGTH 4
+#define INIT_CHUNK_NUMBER_OF_OUTBOUND_STREAMS_LENGTH 2
+#define INIT_CHUNK_NUMBER_OF_INBOUND_STREAMS_LENGTH 2
+
+
+#define INIT_CHUNK_INITIATE_TAG_OFFSET CHUNK_VALUE_OFFSET
+#define INIT_CHUNK_ADV_REC_WINDOW_CREDIT_OFFSET (INIT_CHUNK_INITIATE_TAG_OFFSET + \
+ INIT_CHUNK_INITIATE_TAG_LENGTH )
+#define INIT_CHUNK_NUMBER_OF_OUTBOUND_STREAMS_OFFSET (INIT_CHUNK_ADV_REC_WINDOW_CREDIT_OFFSET + \
+ INIT_CHUNK_ADV_REC_WINDOW_CREDIT_LENGTH )
+#define INIT_CHUNK_NUMBER_OF_INBOUND_STREAMS_OFFSET (INIT_CHUNK_NUMBER_OF_OUTBOUND_STREAMS_OFFSET + \
+ INIT_CHUNK_NUMBER_OF_OUTBOUND_STREAMS_LENGTH )
+#define INIT_CHUNK_INITIAL_TSN_OFFSET (INIT_CHUNK_NUMBER_OF_INBOUND_STREAMS_OFFSET + \
+ INIT_CHUNK_NUMBER_OF_INBOUND_STREAMS_LENGTH )
+
+#define DATA_CHUNK_TSN_LENGTH 4
+#define DATA_CHUNK_TSN_OFFSET (CHUNK_VALUE_OFFSET + 0)
+#define DATA_CHUNK_STREAM_ID_OFFSET (DATA_CHUNK_TSN_OFFSET + DATA_CHUNK_TSN_LENGTH)
+#define DATA_CHUNK_STREAM_ID_LENGTH 2
+#define DATA_CHUNK_STREAM_SEQ_NUMBER_LENGTH 2
+#define DATA_CHUNK_PAYLOAD_PROTOCOL_ID_LENGTH 4
+#define DATA_CHUNK_HEADER_LENGTH (CHUNK_HEADER_LENGTH + \
+ DATA_CHUNK_TSN_LENGTH + \
+ DATA_CHUNK_STREAM_ID_LENGTH + \
+ DATA_CHUNK_STREAM_SEQ_NUMBER_LENGTH + \
+ DATA_CHUNK_PAYLOAD_PROTOCOL_ID_LENGTH)
+#define MAX_ADDRESS_LEN 47
+
+/* The below value is 256 */
+#define NUM_CHUNKS 0xff
+
+/* This variable is used as an index into arrays
+ * which store the cumulative information corresponding
+ * all chunks with Chunk Type greater > 16
+ * The value for the below variable is 17
+ */
+#define OTHER_CHUNKS_INDEX 0xfe
+
+/* VNB */
+/* This variable stores the maximum chunk type value
+ * that can be associated with a sctp chunk.
+ */
+#define MAX_SCTP_CHUNK_TYPE 256
+
+typedef struct _tsn {
+ guint32 frame_number;
+ guint32 secs; /* Absolute seconds */
+ guint32 usecs;
+ address src;
+ address dst;
+ guint32 first_tsn;
+ GList *tsns;
+} tsn_t;
+
+typedef struct _sctp_tmp_info {
+ address src;
+ address dst;
+ guint16 port1;
+ guint16 port2;
+ guint32 verification_tag1;
+ guint32 verification_tag2;
+ guint32 initiate_tag;
+ guint32 n_tvbs;
+} sctp_tmp_info_t;
+
+typedef struct _sctp_min_max {
+ guint32 tmp_min_secs;
+ guint32 tmp_min_usecs;
+ guint32 tmp_max_secs;
+ guint32 tmp_max_usecs;
+ guint32 tmp_min_tsn1;
+ guint32 tmp_min_tsn2;
+ guint32 tmp_max_tsn1;
+ guint32 tmp_max_tsn2;
+ gint tmp_secs;
+} sctp_min_max_t;
+
+struct tsn_sort{
+ guint32 tsnumber;
+ guint32 secs;
+ guint32 usecs;
+ guint32 offset;
+ guint32 length;
+ guint32 framenumber;
+};
+
+typedef struct _sctp_addr_chunk {
+ guint32 direction;
+ address* addr;
+ /* The array is initialized to MAX_SCTP_CHUNK_TYPE
+ * so that there is no memory overwrite
+ * when accessed using sctp chunk type as index.
+ */
+ guint32 addr_count[MAX_SCTP_CHUNK_TYPE];
+} sctp_addr_chunk;
+
+typedef struct _sctp_assoc_info {
+ address src;
+ address dst;
+ guint16 port1;
+ guint16 port2;
+ guint32 verification_tag1;
+ guint32 verification_tag2;
+ guint32 initiate_tag;
+ guint32 n_tvbs;
+ GList *addr1;
+ GList *addr2;
+ guint16 instream1;
+ guint16 outstream1;
+ guint16 instream2;
+ guint16 outstream2;
+ guint32 n_adler32_calculated;
+ guint32 n_adler32_correct;
+ guint32 n_crc32c_calculated;
+ guint32 n_crc32c_correct;
+ gchar checksum_type[8];
+ guint32 n_checksum_errors;
+ guint32 n_bundling_errors;
+ guint32 n_padding_errors;
+ guint32 n_length_errors;
+ guint32 n_value_errors;
+ guint32 n_data_chunks;
+ guint32 n_data_bytes;
+ guint32 n_packets;
+ guint32 n_data_chunks_ep1;
+ guint32 n_data_bytes_ep1;
+ guint32 n_data_chunks_ep2;
+ guint32 n_data_bytes_ep2;
+ guint32 n_sack_chunks_ep1;
+ guint32 n_sack_chunks_ep2;
+ guint32 n_array_tsn1;
+ guint32 n_array_tsn2;
+ guint32 max_window1;
+ guint32 max_window2;
+ gboolean init;
+ gboolean initack;
+ guint8 initack_dir;
+ guint8 direction;
+ guint32 min_secs;
+ guint32 min_usecs;
+ guint32 max_secs;
+ guint32 max_usecs;
+ guint32 min_tsn1;
+ guint32 min_tsn2;
+ guint32 max_tsn1;
+ guint32 max_tsn2;
+ guint32 max_bytes1;
+ guint32 max_bytes2;
+ GSList *min_max;
+ GList *frame_numbers;
+ GList *tsn1;
+ GPtrArray *sort_tsn1;
+ GPtrArray *sort_sack1;
+ GList *sack1;
+ GList *tsn2;
+ GPtrArray *sort_tsn2;
+ GPtrArray *sort_sack2;
+ GList *sack2;
+ gboolean check_address;
+ GList* error_info_list;
+ /* The array is initialized to MAX_SCTP_CHUNK_TYPE
+ * so that there is no memory overwrite
+ * when accessed using sctp chunk type as index.
+ */
+ guint32 chunk_count[MAX_SCTP_CHUNK_TYPE];
+ guint32 ep1_chunk_count[MAX_SCTP_CHUNK_TYPE];
+ guint32 ep2_chunk_count[MAX_SCTP_CHUNK_TYPE];
+ GList* addr_chunk_count;
+} sctp_assoc_info_t;
+
+typedef struct _sctp_error_info {
+ guint32 frame_number;
+ gchar chunk_info[200];
+ const gchar *info_text;
+} sctp_error_info_t;
+
+
+typedef struct _sctp_allassocs_info {
+ guint32 sum_tvbs;
+ GList* assoc_info_list;
+ gboolean is_registered;
+ GList* children;
+} sctp_allassocs_info_t;
+
+
+
+struct notes {
+ GtkWidget *checktype;
+ GtkWidget *checksum;
+ GtkWidget *bundling;
+ GtkWidget *padding;
+ GtkWidget *length;
+ GtkWidget *value;
+ GtkWidget *chunks_ep1;
+ GtkWidget *bytes_ep1;
+ GtkWidget *chunks_ep2;
+ GtkWidget *bytes_ep2;
+ struct page *page2;
+ struct page *page3;
+};
+
+struct page {
+ GtkWidget *addr_frame;
+ GtkWidget *scrolled_window;
+ GtkWidget *clist;
+ GtkWidget *port;
+ GtkWidget *veritag;
+ GtkWidget *max_in;
+ GtkWidget *min_in;
+ GtkWidget *max_out;
+ GtkWidget *min_out;
+};
+
+struct sctp_analyse {
+ sctp_assoc_info_t *assoc;
+ GtkWidget* window;
+ struct notes *analyse_nb;
+ GList *children;
+ guint16 num_children;
+};
+
+typedef struct _sctp_graph_t {
+ gboolean needs_redraw;
+ gfloat x_interval;
+ gfloat y_interval;
+ GtkWidget *window;
+ GtkWidget *draw_area;
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_surface_t *surface;
+#else
+ GdkPixmap *pixmap;
+#endif
+ gint surface_width;
+ gint surface_height;
+ gint graph_type;
+ gdouble x_old;
+ gdouble y_old;
+ gdouble x_new;
+ gdouble y_new;
+ guint16 offset;
+ guint16 length;
+ gboolean tmp;
+ gboolean rectangle;
+ gboolean rectangle_present;
+ guint32 rect_x_min;
+ guint32 rect_x_max;
+ guint32 rect_y_min;
+ guint32 rect_y_max;
+ guint32 x1_tmp_sec;
+ guint32 x2_tmp_sec;
+ guint32 x1_tmp_usec;
+ guint32 x2_tmp_usec;
+ guint32 x1_akt_sec;
+ guint32 x2_akt_sec;
+ guint32 x1_akt_usec;
+ guint32 x2_akt_usec;
+ guint32 tmp_width;
+ guint32 axis_width;
+ guint32 y1_tmp;
+ guint32 y2_tmp;
+ guint32 tmp_min_tsn1;
+ guint32 tmp_max_tsn1;
+ guint32 tmp_min_tsn2;
+ guint32 tmp_max_tsn2;
+ guint32 min_x;
+ guint32 max_x;
+ guint32 min_y;
+ guint32 max_y;
+ gboolean uoff;
+} sctp_graph_t;
+
+
+
+struct sctp_udata {
+ sctp_assoc_info_t *assoc;
+ sctp_graph_t *io;
+ struct sctp_analyse *parent;
+ guint16 dir;
+};
+
+
+void register_tap_listener_sctp_stat(void);
+
+const sctp_allassocs_info_t* sctp_stat_get_info(void);
+
+void sctp_stat_scan(void);
+
+void remove_tap_listener_sctp_stat(void);
+
+void assoc_analyse(sctp_assoc_info_t* assoc);
+
+const sctp_assoc_info_t* get_selected_assoc(void);
+
+void create_graph(guint16 dir, struct sctp_analyse* u_data);
+
+void create_byte_graph(guint16 dir, struct sctp_analyse* u_data);
+
+void sctp_error_dlg_show(sctp_assoc_info_t* assoc);
+
+void sctp_stat_dlg_update(void);
+
+void sctp_chunk_stat_dlg_update(struct sctp_udata* udata, unsigned int direction);
+
+void sctp_chunk_dlg_show(struct sctp_analyse* userdata);
+
+void sctp_chunk_stat_dlg_show(unsigned int direction, struct sctp_analyse* userdata);
+
+GtkWidget *get_stat_dlg(void);
+
+GtkWidget *get_chunk_stat_dlg(void);
+
+void update_analyse_dlg(struct sctp_analyse* u_data);
+
+void increase_childcount(struct sctp_analyse *parent);
+
+void decrease_childcount(struct sctp_analyse *parent);
+
+void set_child(struct sctp_udata *child, struct sctp_analyse *parent);
+
+void remove_child(struct sctp_udata *child, struct sctp_analyse *parent);
+
+void decrease_analyse_childcount(void);
+
+void increase_analyse_childcount(void);
+
+void set_analyse_child(struct sctp_analyse *child);
+
+void remove_analyse_child(struct sctp_analyse *child);
+
+void sctp_set_assoc_filter(void);
+
+#endif /* __SCTP_STAT_H__ */
diff --git a/ui/gtk/sctp_stat_dlg.c b/ui/gtk/sctp_stat_dlg.c
new file mode 100644
index 0000000000..c66cf6767a
--- /dev/null
+++ b/ui/gtk/sctp_stat_dlg.c
@@ -0,0 +1,681 @@
+/*
+ * Copyright 2004, Irene Ruengeler <i.ruengeler [AT] fh-muenster.de>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/filesystem.h"
+
+#include "../globals.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/sctp_stat.h"
+#include "ui/gtk/gtkglobals.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+static GtkWidget *sctp_stat_dlg=NULL;
+static GtkWidget *clist = NULL;
+static GList *last_list = NULL;
+static gchar *filter_string = NULL;
+static sctp_assoc_info_t* selected_stream=NULL; /* current selection */
+static sctp_allassocs_info_t *sctp_assocs=NULL;
+static guint16 n_children=0;
+static GtkWidget *bt_afilter = NULL, *bt_unselect=NULL, *bt_analyse=NULL, *bt_filter=NULL;
+static gboolean prevent_update = FALSE, filter_applied = FALSE;
+
+enum
+{
+ PORT1_COLUMN,
+ PORT2_COLUMN,
+ PACKETS_COLUMN,
+ CHECKSUM_TYPE_COLUMN,
+ CHECKSUM_ERRORS_COLUMN,
+ DATA_CHUNKS_COLUMN,
+ DATA_BYTES_COLUMN,
+ VTAG1_COLUMN,
+ VTAG2_COLUMN,
+ N_COLUMN
+};
+
+
+static void
+sctp_stat_on_select_row(GtkTreeSelection *sel, gpointer user_data _U_)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GList *list;
+ sctp_assoc_info_t* assoc;
+ gboolean stream_found=FALSE;
+ guint32 port2, port1;
+ guint32 checksum, data_chunks, data_bytes, packets, vtag1, vtag2;
+
+ if (gtk_tree_selection_get_selected (sel, &model, &iter)) {
+ gtk_tree_model_get(model, &iter,
+ PORT1_COLUMN, &port1,
+ PORT2_COLUMN, &port2,
+ PACKETS_COLUMN, &packets,
+ CHECKSUM_ERRORS_COLUMN, &checksum,
+ DATA_CHUNKS_COLUMN, &data_chunks,
+ DATA_BYTES_COLUMN, &data_bytes,
+ VTAG1_COLUMN, &vtag1,
+ VTAG2_COLUMN, &vtag2,
+ -1);
+ } else {
+ /* Nothing selected */
+ return;
+ }
+
+ list = g_list_first(sctp_assocs->assoc_info_list);
+
+ while (list)
+ {
+ assoc = (sctp_assoc_info_t*)(list->data);
+ if (assoc->port1==port1 && assoc->port2==port2
+ && assoc->n_packets==packets && assoc->n_data_chunks==data_chunks && assoc->n_data_bytes==data_bytes
+ && assoc->verification_tag1==vtag1 && assoc->verification_tag2==vtag2)
+ {
+ selected_stream=assoc;
+ stream_found=TRUE;
+ break;
+ }
+ list=g_list_next(list);
+ }
+
+ if (!stream_found)
+ selected_stream = NULL;
+
+ gtk_widget_set_sensitive(bt_unselect,TRUE);
+ gtk_widget_set_sensitive(bt_analyse,TRUE);
+ gtk_widget_set_sensitive(bt_filter,TRUE);
+}
+
+static
+GtkWidget *create_list(void)
+{
+ GtkListStore *list_store;
+ GtkWidget * list;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeView *list_view;
+ GtkTreeSelection *selection;
+
+ list_store = gtk_list_store_new(N_COLUMN,
+ G_TYPE_UINT, /* Port1*/
+ G_TYPE_UINT, /* Port2*/
+ G_TYPE_UINT, /* number of packets */
+ G_TYPE_STRING, /* checksum type */
+ G_TYPE_UINT, /* number of checksum errors */
+ G_TYPE_UINT, /* number of data chunks */
+ G_TYPE_UINT, /* number of data bytes */
+ G_TYPE_UINT, /* vtag1 */
+ G_TYPE_UINT); /* vtag2 */
+
+ /* Create a view */
+ list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
+
+ list_view = GTK_TREE_VIEW(list);
+
+ /* Speed up the list display */
+ gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
+
+ gtk_tree_view_set_headers_clickable(list_view, TRUE);
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref (G_OBJECT (list_store));
+
+ /*
+ * Create the first column packet, associating the "text" attribute of the
+ * cell_renderer to the first column of the model
+ */
+ /* 1:st column */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Port 1", renderer,
+ "text", PORT1_COLUMN,
+ NULL);
+
+ gtk_tree_view_column_set_sort_column_id(column, PORT1_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+
+ /* Add the column to the view. */
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 2:nd column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Port 2", renderer,
+ "text", PORT2_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, PORT2_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 3:d column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("No of Packets", renderer,
+ "text", PACKETS_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, PACKETS_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 120);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 4:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Checksum", renderer,
+ "text", CHECKSUM_TYPE_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, CHECKSUM_TYPE_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 120);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 5:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("No of Errors", renderer,
+ "text", CHECKSUM_ERRORS_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, CHECKSUM_ERRORS_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 120);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 6:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Data Chunks", renderer,
+ "text", DATA_CHUNKS_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, DATA_CHUNKS_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 120);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 7:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Data Bytes", renderer,
+ "text", DATA_BYTES_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, DATA_BYTES_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 120);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* 8:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("VTag 1", renderer,
+ "text", VTAG1_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, VTAG1_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 120);
+ gtk_tree_view_append_column (list_view, column);
+
+
+ /* 9:th column... */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("VTag 2", renderer,
+ "text", VTAG2_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, VTAG2_COLUMN);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 120);
+ gtk_tree_view_append_column (list_view, column);
+
+ /* Now enable the sorting of each column */
+ gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
+ gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
+
+ /* Setup the selection handler */
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+ g_signal_connect(selection, "changed", G_CALLBACK(sctp_stat_on_select_row), NULL);
+ return list;
+}
+
+static void
+dlg_destroy(GtkWidget *w _U_, gpointer user_data _U_)
+{
+ guint32 i, j;
+ GList *list;
+ struct sctp_analyse *child_data;
+
+ j=n_children;
+ for (i=0; i<j; i++)
+ {
+ list=g_list_last(sctp_assocs->children);
+ child_data=(struct sctp_analyse *)list->data;
+ gtk_grab_remove(GTK_WIDGET(child_data->window));
+ gtk_widget_destroy(GTK_WIDGET(child_data->window));
+ }
+ g_list_free(sctp_assocs->children);
+ sctp_assocs->children = NULL;
+ sctp_stat_dlg = NULL;
+ prevent_update = FALSE;
+ filter_applied = FALSE;
+}
+
+void
+decrease_analyse_childcount(void)
+{
+ n_children--;
+}
+
+void
+increase_analyse_childcount(void)
+{
+ n_children++;
+}
+
+void
+set_analyse_child(struct sctp_analyse *child)
+{
+ sctp_assocs->children=g_list_append(sctp_assocs->children, child);
+}
+
+void
+remove_analyse_child(struct sctp_analyse *child)
+{
+ sctp_assocs->children=g_list_remove(sctp_assocs->children, child);
+}
+
+
+
+static void add_to_clist(sctp_assoc_info_t* assinfo)
+{
+ GtkListStore *list_store = NULL;
+ GtkTreeIter iter;
+
+ list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (clist))); /* Get store */
+
+ gtk_list_store_insert_with_values( list_store , &iter, G_MAXINT,
+ PORT1_COLUMN, (guint32)assinfo->port1,
+ PORT2_COLUMN, (guint32)assinfo->port2,
+ PACKETS_COLUMN, assinfo->n_packets,
+ CHECKSUM_TYPE_COLUMN, assinfo->checksum_type,
+ CHECKSUM_ERRORS_COLUMN, assinfo->n_checksum_errors,
+ DATA_CHUNKS_COLUMN, assinfo->n_data_chunks,
+ DATA_BYTES_COLUMN, assinfo->n_data_bytes,
+ VTAG1_COLUMN, assinfo->verification_tag1,
+ VTAG2_COLUMN, assinfo->verification_tag2,
+ -1);
+}
+
+static void
+sctp_stat_on_unselect(GtkButton *button _U_, gpointer user_data _U_)
+{
+ if (filter_string != NULL) {
+ g_free(filter_string);
+ filter_string = NULL;
+ }
+
+ selected_stream = NULL;
+ gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), "");
+ main_filter_packets(&cfile, "", FALSE);
+ gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(clist)));
+ gtk_widget_set_sensitive(bt_unselect,FALSE);
+ gtk_widget_set_sensitive(bt_filter,FALSE);
+ gtk_widget_set_sensitive(bt_analyse,FALSE);
+ gtk_widget_set_sensitive(bt_afilter,FALSE);
+ prevent_update = FALSE;
+ filter_applied = FALSE;
+}
+
+void sctp_stat_dlg_update(void)
+{
+ GList *list;
+
+ list=(sctp_stat_get_info()->assoc_info_list);
+ if (sctp_stat_dlg != NULL && !prevent_update)
+ {
+ gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(clist))));
+ list = g_list_first(sctp_stat_get_info()->assoc_info_list);
+
+ while (list)
+ {
+ add_to_clist((sctp_assoc_info_t*)(list->data));
+ list = g_list_next(list);
+ }
+ }
+ last_list = list;
+}
+
+
+
+static void
+sctp_stat_on_apply_filter (GtkButton *button _U_, gpointer user_data _U_)
+{
+ GList *list;
+ sctp_assoc_info_t* assoc;
+ guint16 port1, port2;
+ guint32 data_chunks, data_bytes, packets, vtag1, vtag2;
+
+ if (filter_string != NULL)
+ {
+ port1 = selected_stream->port1;
+ port2 = selected_stream->port2;
+ data_chunks = selected_stream->n_data_chunks;
+ data_bytes = selected_stream->n_data_bytes;
+ packets = selected_stream->n_packets;
+ vtag1 = selected_stream->verification_tag1;
+ vtag2 = selected_stream->verification_tag2;
+ main_filter_packets(&cfile, filter_string, FALSE);
+ list = g_list_first(sctp_assocs->assoc_info_list);
+
+ while (list)
+ {
+ assoc = (sctp_assoc_info_t*)(list->data);
+ if (assoc->port1==port1 && assoc->port2==port2
+ && assoc->n_packets==packets && assoc->n_data_chunks==data_chunks && assoc->n_data_bytes==data_bytes
+ && assoc->verification_tag1==vtag1 && assoc->verification_tag2==vtag2)
+ {
+ selected_stream=assoc;
+ break;
+ }
+ list=g_list_next(list);
+ }
+ gtk_widget_set_sensitive(bt_afilter,FALSE);
+ prevent_update=TRUE;
+ filter_applied = TRUE;
+ }
+}
+
+static void
+sctp_stat_on_filter (GtkButton *button _U_, gpointer user_data _U_)
+{
+ gchar *f_string = NULL;
+ GList *srclist, *dstlist;
+ gchar *str=NULL;
+ GString *gstring=NULL;
+ struct sockaddr_in *infosrc=NULL;
+ struct sockaddr_in *infodst=NULL;
+
+ if (selected_stream==NULL) {
+ gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), "");
+ return;
+ }
+
+ if (selected_stream->check_address==FALSE)
+ {
+ f_string = g_strdup_printf("((sctp.srcport==%u && sctp.dstport==%u && "
+ "((sctp.verification_tag==0x%x && sctp.verification_tag!=0x0) || "
+ "(sctp.verification_tag==0x0 && sctp.initiate_tag==0x%x) || "
+ "(sctp.verification_tag==0x%x && (sctp.abort_t_bit==1 || "
+ "sctp.shutdown_complete_t_bit==1)))) ||"
+ "(sctp.srcport==%u && sctp.dstport==%u && ((sctp.verification_tag==0x%x "
+ "&& sctp.verification_tag!=0x0) || "
+ "(sctp.verification_tag==0x0 && sctp.initiate_tag==0x%x) ||"
+ "(sctp.verification_tag==0x%x && (sctp.abort_t_bit==1 ||"
+ " sctp.shutdown_complete_t_bit==1)))))",
+ selected_stream->port1,
+ selected_stream->port2,
+ selected_stream->verification_tag1,
+ selected_stream->initiate_tag,
+ selected_stream->verification_tag2,
+ selected_stream->port2,
+ selected_stream->port1,
+ selected_stream->verification_tag2,
+ selected_stream->initiate_tag,
+ selected_stream->verification_tag1);
+ filter_string = f_string;
+ }
+ else
+ {
+
+ srclist = g_list_first(selected_stream->addr1);
+ infosrc=(struct sockaddr_in *) (srclist->data);
+ gstring = g_string_new(g_strdup_printf("((sctp.srcport==%u && sctp.dstport==%u && (ip.src==%s",
+ selected_stream->port1, selected_stream->port2, ip_to_str((const guint8 *)&(infosrc->sin_addr.s_addr))));
+ srclist= g_list_next(srclist);
+
+ while (srclist)
+ {
+ infosrc=(struct sockaddr_in *) (srclist->data);
+ str =g_strdup_printf("|| ip.src==%s",ip_to_str((const guint8 *)&(infosrc->sin_addr.s_addr)));
+ g_string_append(gstring, str);
+ srclist= g_list_next(srclist);
+ }
+ dstlist = g_list_first(selected_stream->addr2);
+ infodst=(struct sockaddr_in *) (dstlist->data);
+ str = g_strdup_printf(") && (ip.dst==%s",ip_to_str((const guint8 *)&(infodst->sin_addr.s_addr)));
+ g_string_append(gstring, str);
+ dstlist= g_list_next(dstlist);
+ while (dstlist)
+ {
+ infodst=(struct sockaddr_in *) (dstlist->data);
+ str =g_strdup_printf("|| ip.dst==%s",ip_to_str((const guint8 *)&(infodst->sin_addr.s_addr)));
+ g_string_append(gstring, str);
+ dstlist= g_list_next(dstlist);
+ }
+ srclist = g_list_first(selected_stream->addr1);
+ infosrc=(struct sockaddr_in *) (srclist->data);
+ str = g_strdup_printf(")) || (sctp.dstport==%u && sctp.srcport==%u && (ip.dst==%s",
+ selected_stream->port1, selected_stream->port2, ip_to_str((const guint8 *)&(infosrc->sin_addr.s_addr)));
+ g_string_append(gstring, str);
+ srclist= g_list_next(srclist);
+
+ while (srclist)
+ {
+ infosrc=(struct sockaddr_in *) (srclist->data);
+ str =g_strdup_printf("|| ip.dst==%s",ip_to_str((const guint8 *)&(infosrc->sin_addr.s_addr)));
+ g_string_append(gstring, str);
+ srclist= g_list_next(srclist);
+ }
+
+ dstlist = g_list_first(selected_stream->addr2);
+ infodst=(struct sockaddr_in *) (dstlist->data);
+ str = g_strdup_printf(") && (ip.src==%s",ip_to_str((const guint8 *)&(infodst->sin_addr.s_addr)));
+ g_string_append(gstring, str);
+ dstlist= g_list_next(dstlist);
+ while (dstlist)
+ {
+ infodst=(struct sockaddr_in *) (dstlist->data);
+ str =g_strdup_printf("|| ip.src==%s",ip_to_str((const guint8 *)&(infodst->sin_addr.s_addr)));
+ g_string_append(gstring, str);
+ dstlist= g_list_next(dstlist);
+ }
+ str = g_strdup_printf(")))");
+ g_string_append(gstring, str);
+ filter_string = gstring->str;
+ g_string_free(gstring,FALSE);
+ }
+
+ if (filter_string != NULL) {
+ gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), filter_string);
+ } else {
+ g_assert_not_reached();
+ }
+ gtk_widget_set_sensitive(bt_afilter,TRUE);
+ gtk_widget_set_sensitive(bt_filter,FALSE);
+ prevent_update = TRUE;
+ filter_applied = FALSE;
+}
+
+
+static void
+sctp_stat_on_close (GtkWidget *button _U_, gpointer user_data _U_)
+{
+ gtk_grab_remove(sctp_stat_dlg);
+ gtk_widget_destroy(sctp_stat_dlg);
+ prevent_update = FALSE;
+ filter_applied = FALSE;
+}
+
+static void
+sctp_stat_on_analyse (GtkButton *button _U_, gpointer user_data _U_)
+{
+ if (selected_stream==NULL)
+ return;
+ else
+ assoc_analyse(selected_stream);
+ gtk_widget_set_sensitive(bt_analyse,FALSE);
+ if (!filter_applied)
+ gtk_widget_set_sensitive(bt_filter,TRUE);
+ prevent_update = TRUE;
+}
+
+
+static void
+gtk_sctpstat_dlg(void)
+{
+ GtkWidget *sctp_stat_dlg_w;
+ GtkWidget *vbox1;
+ GtkWidget *scrolledwindow1;
+ GtkWidget *hbuttonbox2;
+ GtkWidget *bt_close;
+
+ sctp_stat_dlg_w = window_new (GTK_WINDOW_TOPLEVEL, "Wireshark: SCTP Associations");
+ gtk_window_set_position (GTK_WINDOW (sctp_stat_dlg_w), GTK_WIN_POS_CENTER);
+ g_signal_connect(sctp_stat_dlg_w, "destroy", G_CALLBACK(dlg_destroy), NULL);
+
+ /* Container for each row of widgets */
+ vbox1 = gtk_vbox_new(FALSE, 2);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox1), 8);
+ gtk_container_add(GTK_CONTAINER(sctp_stat_dlg_w), vbox1);
+ gtk_widget_show(vbox1);
+
+ scrolledwindow1 = scrolled_window_new (NULL, NULL);
+ gtk_widget_show (scrolledwindow1);
+ gtk_box_pack_start (GTK_BOX (vbox1), scrolledwindow1, TRUE, TRUE, 0);
+
+ clist = create_list();
+ gtk_widget_show (clist);
+ gtk_container_add (GTK_CONTAINER (scrolledwindow1), clist);
+ gtk_widget_set_size_request(clist, 1050, 200);
+
+ gtk_widget_show(sctp_stat_dlg_w);
+
+ hbuttonbox2 = gtk_hbutton_box_new();
+ gtk_box_pack_start(GTK_BOX(vbox1), hbuttonbox2, FALSE, FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(hbuttonbox2), 10);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX (hbuttonbox2), GTK_BUTTONBOX_SPREAD);
+ gtk_box_set_spacing(GTK_BOX (hbuttonbox2), 0);
+ gtk_widget_show(hbuttonbox2);
+
+ bt_unselect = gtk_button_new_with_label ("Unselect");
+ gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_unselect);
+ gtk_widget_show (bt_unselect);
+ gtk_widget_set_sensitive(bt_unselect,FALSE);
+
+ bt_filter = gtk_button_new_with_label ("Set filter");
+ gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_filter);
+ gtk_widget_show (bt_filter);
+ gtk_widget_set_sensitive(bt_filter,FALSE);
+
+ bt_afilter = gtk_button_new_with_label ("Apply filter");
+ gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_afilter);
+ gtk_widget_show (bt_afilter);
+ gtk_widget_set_sensitive(bt_afilter,FALSE);
+
+ bt_analyse = gtk_button_new_with_label ("Analyse");
+ gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_analyse);
+ gtk_widget_show (bt_analyse);
+ gtk_widget_set_sensitive(bt_analyse,FALSE);
+
+ bt_close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_container_add (GTK_CONTAINER (hbuttonbox2), bt_close);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(bt_close, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(bt_close, GTK_CAN_DEFAULT);
+#endif
+ window_set_cancel_button( sctp_stat_dlg_w, bt_close, sctp_stat_on_close);
+ gtk_widget_grab_focus(bt_close);
+ gtk_widget_show (bt_close);
+
+ g_signal_connect(sctp_stat_dlg_w, "destroy", G_CALLBACK(dlg_destroy), NULL);
+ g_signal_connect(bt_unselect, "clicked", G_CALLBACK(sctp_stat_on_unselect), NULL);
+ g_signal_connect(bt_filter, "clicked", G_CALLBACK(sctp_stat_on_filter), NULL);
+ g_signal_connect(bt_afilter, "clicked", G_CALLBACK(sctp_stat_on_apply_filter), NULL);
+ g_signal_connect(bt_analyse, "clicked", G_CALLBACK(sctp_stat_on_analyse), NULL);
+
+ sctp_stat_dlg = sctp_stat_dlg_w;
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(sctp_stat_dlg_w));
+
+}
+
+static void sctp_stat_dlg_show(void)
+{
+ if (sctp_stat_dlg != NULL)
+ {
+ /* There's already a dialog box; reactivate it. */
+ reactivate_window(sctp_stat_dlg);
+ /* Another list since last call? */
+ if ((sctp_stat_get_info()->assoc_info_list) != last_list)
+ sctp_stat_dlg_update();
+ }
+ else
+ {
+ /* Create and show the dialog box */
+ gtk_sctpstat_dlg();
+ sctp_stat_dlg_update();
+ }
+}
+
+
+void sctp_stat_start(GtkAction *action _U_, gpointer user_data _U_)
+{
+ prevent_update = FALSE;
+ filter_applied = FALSE;
+ sctp_assocs = g_malloc(sizeof(sctp_allassocs_info_t));
+ sctp_assocs = (sctp_allassocs_info_t*)sctp_stat_get_info();
+ /* Register the tap listener */
+ if (sctp_stat_get_info()->is_registered==FALSE)
+ register_tap_listener_sctp_stat();
+ /* (redissect all packets) */
+ sctp_stat_scan();
+
+ /* Show the dialog box with the list of streams */
+ sctp_stat_dlg_show();
+}
+
+/****************************************************************************/
+void
+register_tap_listener_sctp_stat_dlg(void)
+{
+}
+
+GtkWidget* get_stat_dlg(void)
+{
+ return sctp_stat_dlg;
+}
+
diff --git a/ui/gtk/service_response_time_table.c b/ui/gtk/service_response_time_table.c
new file mode 100644
index 0000000000..61392e9d60
--- /dev/null
+++ b/ui/gtk/service_response_time_table.c
@@ -0,0 +1,615 @@
+/* service_response_time_table.c
+ * service_response_time_table 2003 Ronnie Sahlberg
+ * Helper routines common to all service response time statistics
+ * tap.
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+#include <stdio.h>
+
+#include "epan/packet_info.h"
+
+#include "../simple_dialog.h"
+
+#include "ui/gtk/service_response_time_table.h"
+#include "ui/gtk/filter_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/utf8_entities.h"
+
+#define NANOSECS_PER_SEC 1000000000
+
+enum
+{
+ INDEX_COLUMN,
+ PROCEDURE_COLUMN,
+ CALLS_COLUMN,
+ MIN_SRT_COLUMN,
+ MAX_SRT_COLUMN,
+ AVG_SRT_COLUMN,
+ N_COLUMNS
+};
+
+
+static void
+srt_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callback_action)
+{
+ srt_stat_table *rst = (srt_stat_table *)callback_data;
+ char *str = NULL;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreeSelection *sel;
+ int selection;
+
+ if(rst->filter_string==NULL){
+ return;
+ }
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(rst->table));
+
+ if (!gtk_tree_selection_get_selected(sel, &model, &iter))
+ return;
+
+ gtk_tree_model_get (model, &iter, INDEX_COLUMN, &selection, -1);
+ if(selection>=(int)rst->num_procs){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No procedure selected");
+ return;
+ }
+
+ str = g_strdup_printf("%s==%d", rst->filter_string, selection);
+
+ apply_selected_filter (callback_action, str);
+
+ g_free(str);
+}
+
+static gboolean
+srt_show_popup_menu_cb(void *widg _U_, GdkEvent *event, srt_stat_table *rst)
+{
+ GdkEventButton *bevent = (GdkEventButton *)event;
+
+ if(event->type==GDK_BUTTON_PRESS && bevent->button==3){
+ gtk_menu_popup(GTK_MENU(rst->menu), NULL, NULL, NULL, NULL,
+ bevent->button, bevent->time);
+ }
+
+ return FALSE;
+}
+
+
+/* Action callbacks */
+static void
+apply_as_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ srt_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_SELECTED, 0));
+}
+static void
+apply_as_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ srt_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_NOT_SELECTED, 0));
+}
+static void
+apply_as_and_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ srt_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_SELECTED, 0));
+}
+static void
+apply_as_or_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ srt_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_SELECTED, 0));
+}
+static void
+apply_as_and_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ srt_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, 0));
+}
+static void
+apply_as_or_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ srt_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, 0));
+}
+
+static void
+prep_as_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ srt_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_SELECTED, 0));
+}
+static void
+prep_as_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ srt_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, 0));
+}
+static void
+prep_as_and_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ srt_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_SELECTED, 0));
+}
+static void
+prep_as_or_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ srt_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_SELECTED, 0));
+}
+static void
+prep_as_and_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ srt_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, 0));
+}
+static void
+prep_as_or_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ srt_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, 0));
+}
+
+static void
+find_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ srt_select_filter_cb( widget , user_data, CALLBACK_FIND_FRAME(ACTYPE_SELECTED, 0));
+}
+static void
+find_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ srt_select_filter_cb( widget , user_data, CALLBACK_FIND_FRAME(ACTYPE_NOT_SELECTED, 0));
+}
+static void
+find_prev_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ srt_select_filter_cb( widget , user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, 0));
+}
+static void
+find_prev_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ srt_select_filter_cb( widget , user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_NOT_SELECTED, 0));
+}
+static void
+find_next_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ srt_select_filter_cb( widget , user_data, CALLBACK_FIND_NEXT(ACTYPE_SELECTED, 0));
+}
+static void
+find_next_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ srt_select_filter_cb( widget , user_data, CALLBACK_FIND_NEXT(ACTYPE_NOT_SELECTED, 0));
+}
+static void
+color_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ srt_select_filter_cb( widget , user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, 0));
+}
+static void
+color_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ srt_select_filter_cb( widget , user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, 0));
+}
+
+static const char *ui_desc_service_resp_t_filter_popup =
+"<ui>\n"
+" <popup name='ServiceRespTFilterPopup'>\n"
+" <menu action='/Apply as Filter'>\n"
+" <menuitem action='/Apply as Filter/Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " not Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected'/>\n"
+" </menu>\n"
+" <menu action='/Prepare a Filter'>\n"
+" <menuitem action='/Prepare a Filter/Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " not Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected'/>\n"
+" </menu>\n"
+" <menu action='/Find Frame'>\n"
+" <menu action='/Find Frame/Find Frame'>\n"
+" <menuitem action='/Find Frame/Selected'/>\n"
+" <menuitem action='/Find Frame/Not Selected'/>\n"
+" </menu>\n"
+" <menu action='/Find Frame/Find Next'>\n"
+" <menuitem action='/Find Next/Selected'/>\n"
+" <menuitem action='/Find Next/Not Selected'/>\n"
+" </menu>\n"
+" <menu action='/Find Frame/Find Previous'>\n"
+" <menuitem action='/Find Previous/Selected'/>\n"
+" <menuitem action='/Find Previous/Not Selected'/>\n"
+" </menu>\n"
+" </menu>\n"
+" <menu action='/Colorize Procedure'>\n"
+" <menuitem action='/Colorize Procedure/Selected'/>\n"
+" <menuitem action='/Colorize Procedure/Not Selected'/>\n"
+" </menu>\n"
+" </popup>\n"
+"</ui>\n";
+
+/*
+ * GtkActionEntry
+ * typedef struct {
+ * const gchar *name;
+ * const gchar *stock_id;
+ * const gchar *label;
+ * const gchar *accelerator;
+ * const gchar *tooltip;
+ * GCallback callback;
+ * } GtkActionEntry;
+ * const gchar *name; The name of the action.
+ * const gchar *stock_id; The stock id for the action, or the name of an icon from the icon theme.
+ * const gchar *label; The label for the action. This field should typically be marked for translation,
+ * see gtk_action_group_set_translation_domain().
+ * If label is NULL, the label of the stock item with id stock_id is used.
+ * const gchar *accelerator; The accelerator for the action, in the format understood by gtk_accelerator_parse().
+ * const gchar *tooltip; The tooltip for the action. This field should typically be marked for translation,
+ * see gtk_action_group_set_translation_domain().
+ * GCallback callback; The function to call when the action is activated.
+ *
+ */
+static const GtkActionEntry service_resp_t__popup_entries[] = {
+ { "/Apply as Filter", NULL, "Apply as Filter", NULL, NULL, NULL },
+ { "/Prepare a Filter", NULL, "Prepare a Filter", NULL, NULL, NULL },
+ { "/Find Frame", NULL, "Find Frame", NULL, NULL, NULL },
+ { "/Find Frame/Find Frame", NULL, "Find Frame", NULL, NULL, NULL },
+ { "/Find Frame/Find Next", NULL, "Find Next" , NULL, NULL, NULL },
+ { "/Find Frame/Find Previous", NULL, "Find Previous", NULL, NULL, NULL },
+ { "/Colorize Procedure", NULL, "Colorize Procedure", NULL, NULL, NULL },
+ { "/Apply as Filter/Selected", NULL, "Selected", NULL, "Selected", G_CALLBACK(apply_as_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " not Selected", G_CALLBACK(apply_as_not_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", G_CALLBACK(apply_as_and_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", G_CALLBACK(apply_as_or_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", G_CALLBACK(apply_as_and_not_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", G_CALLBACK(apply_as_or_not_selected_cb) },
+ { "/Prepare a Filter/Selected", NULL, "Selected", NULL, "selcted", G_CALLBACK(prep_as_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " not Selected", G_CALLBACK(prep_as_not_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", G_CALLBACK(prep_as_and_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", G_CALLBACK(prep_as_or_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", G_CALLBACK(prep_as_and_not_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", G_CALLBACK(prep_as_or_not_selected_cb) },
+ { "/Find Frame/Selected", NULL, "Selected", NULL, "Selected", G_CALLBACK(find_selected_cb) },
+ { "/Find Frame/Not Selected", NULL, "Not Selected", NULL, "Not Selected", G_CALLBACK(find_not_selected_cb) },
+ { "/Find Previous/Selected", NULL, "Selected", NULL, "Selected", G_CALLBACK(find_prev_selected_cb) },
+ { "/Find Previous/Not Selected", NULL, "Not Selected", NULL, "Not Selected", G_CALLBACK(find_prev_not_selected_cb) },
+ { "/Find Next/Selected", NULL, "Selected", NULL, "Selected", G_CALLBACK(find_next_selected_cb) },
+ { "/Find Next/Not Selected", NULL, "Not Selected", NULL, "Not Selected", G_CALLBACK(find_next_not_selected_cb) },
+ { "/Colorize Procedure/Selected", NULL, "Selected", NULL, "Selected", G_CALLBACK(color_selected_cb) },
+ { "/Colorize Procedure/Not Selected", NULL, "Not Selected", NULL, "Not Selected", G_CALLBACK(color_not_selected_cb) },
+};
+
+static void
+srt_create_popup_menu(srt_stat_table *rst)
+{
+ GtkUIManager *ui_manager;
+ GtkActionGroup *action_group;
+ GError *error = NULL;
+
+ action_group = gtk_action_group_new ("ServiceRespTFilterPopupActionGroup");
+ gtk_action_group_add_actions (action_group, /* the action group */
+ (gpointer)service_resp_t__popup_entries, /* an array of action descriptions */
+ G_N_ELEMENTS(service_resp_t__popup_entries), /* the number of entries */
+ rst); /* data to pass to the action callbacks */
+
+ ui_manager = gtk_ui_manager_new ();
+ gtk_ui_manager_insert_action_group (ui_manager,
+ action_group,
+ 0); /* the position at which the group will be inserted */
+ gtk_ui_manager_add_ui_from_string (ui_manager,ui_desc_service_resp_t_filter_popup, -1, &error);
+ if (error != NULL)
+ {
+ fprintf (stderr, "Warning: building service response time filter popup failed: %s\n",
+ error->message);
+ g_error_free (error);
+ error = NULL;
+ }
+ rst->menu = gtk_ui_manager_get_widget(ui_manager, "/ServiceRespTFilterPopup");
+ g_signal_connect(rst->table, "button_press_event", G_CALLBACK(srt_show_popup_menu_cb), rst);
+
+}
+
+/* ---------------- */
+static void
+srt_time_func (GtkTreeViewColumn *column _U_,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gchar *str;
+ nstime_t *data;
+
+ /* The col to get data from is in userdata */
+ gint data_column = GPOINTER_TO_INT(user_data);
+
+ gtk_tree_model_get(model, iter, data_column, &data, -1);
+ if (!data) {
+ g_object_set(renderer, "text", "", NULL);
+ return;
+ }
+ str = g_strdup_printf("%3d.%06d", (int)data->secs, (data->nsecs+500)/1000);
+ g_object_set(renderer, "text", str, NULL);
+ g_free(str);
+}
+
+static void
+srt_avg_func (GtkTreeViewColumn *column _U_,
+ GtkCellRenderer *renderer,
+ GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer user_data)
+{
+ gchar *str;
+ guint64 td;
+ gint data_column = GPOINTER_TO_INT(user_data);
+
+ gtk_tree_model_get(model, iter, data_column, &td, -1);
+ str=g_strdup_printf("%3d.%06d",
+ (int)(td/1000000), (int)(td%1000000));
+ g_object_set(renderer, "text", str, NULL);
+ g_free(str);
+}
+
+static gint
+srt_time_sort_func(GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ nstime_t *ns_a;
+ nstime_t *ns_b;
+ gint ret = 0;
+ gint data_column = GPOINTER_TO_INT(user_data);
+
+ gtk_tree_model_get(model, a, data_column, &ns_a, -1);
+ gtk_tree_model_get(model, b, data_column, &ns_b, -1);
+
+ if (ns_a == ns_b) {
+ ret = 0;
+ }
+ else if (ns_a == NULL || ns_b == NULL) {
+ ret = (ns_a == NULL) ? -1 : 1;
+ }
+ else {
+ ret = nstime_cmp(ns_a,ns_b);
+ }
+ return ret;
+}
+
+/*
+ XXX Resizable columns are ugly when there's more than on table cf. SMB
+*/
+void
+init_srt_table(srt_stat_table *rst, int num_procs, GtkWidget *vbox, const char *filter_string)
+{
+ int i;
+ GtkListStore *store;
+ GtkWidget *tree;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSortable *sortable;
+ GtkTreeSelection *sel;
+
+ const char *default_titles[] = { "Index", "Procedure", "Calls", "Min SRT", "Max SRT", "Avg SRT" };
+
+ /* Create the store */
+ store = gtk_list_store_new (N_COLUMNS, /* Total number of columns */
+ G_TYPE_INT, /* Index */
+ G_TYPE_STRING, /* Procedure */
+ G_TYPE_UINT, /* Calls */
+ G_TYPE_POINTER, /* Min SRT */
+ G_TYPE_POINTER, /* Max SRT */
+ G_TYPE_UINT64); /* Avg SRT */
+
+ /* Create a view */
+ tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
+ rst->table = GTK_TREE_VIEW(tree);
+ sortable = GTK_TREE_SORTABLE(store);
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref (G_OBJECT (store));
+
+ if(filter_string){
+ rst->filter_string=g_strdup(filter_string);
+ } else {
+ rst->filter_string=NULL;
+ }
+ for (i = 0; i < N_COLUMNS; i++) {
+ renderer = gtk_cell_renderer_text_new ();
+ if (i != PROCEDURE_COLUMN) {
+ /* right align numbers */
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+ }
+ g_object_set(renderer, "ypad", 0, NULL);
+ switch (i) {
+ case MIN_SRT_COLUMN:
+ case MAX_SRT_COLUMN:
+ column = gtk_tree_view_column_new_with_attributes (default_titles[i], renderer, NULL);
+ gtk_tree_view_column_set_cell_data_func(column, renderer, srt_time_func, GINT_TO_POINTER(i), NULL);
+ gtk_tree_sortable_set_sort_func(sortable, i, srt_time_sort_func, GINT_TO_POINTER(i), NULL);
+ break;
+ case AVG_SRT_COLUMN:
+ column = gtk_tree_view_column_new_with_attributes (default_titles[i], renderer, NULL);
+ gtk_tree_view_column_set_cell_data_func(column, renderer, srt_avg_func, GINT_TO_POINTER(i), NULL);
+ break;
+ default:
+ column = gtk_tree_view_column_new_with_attributes (default_titles[i], renderer, "text",
+ i, NULL);
+ break;
+ }
+
+ gtk_tree_view_column_set_sort_column_id(column, i);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_append_column (rst->table, column);
+ if (i == CALLS_COLUMN) {
+ /* XXX revert order sort */
+ gtk_tree_view_column_clicked(column);
+ gtk_tree_view_column_clicked(column);
+ }
+ }
+
+ rst->scrolled_window=scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(rst->scrolled_window),
+ GTK_SHADOW_IN);
+ gtk_container_add(GTK_CONTAINER(rst->scrolled_window), GTK_WIDGET (rst->table));
+ gtk_box_pack_start(GTK_BOX(vbox), rst->scrolled_window, TRUE, TRUE, 0);
+
+ gtk_tree_view_set_reorderable (rst->table, FALSE);
+ /* Now enable the sorting of each column */
+ gtk_tree_view_set_rules_hint(rst->table, TRUE);
+ gtk_tree_view_set_headers_clickable(rst->table, TRUE);
+
+ gtk_widget_show(rst->scrolled_window);
+
+ rst->num_procs=num_procs;
+ rst->procedures=g_malloc(sizeof(srt_procedure_t)*num_procs);
+ for(i=0;i<num_procs;i++){
+ time_stat_init(&rst->procedures[i].stats);
+ rst->procedures[i].index = 0;
+ rst->procedures[i].procedure = NULL;
+ }
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(rst->table));
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+ /* create popup menu for this table */
+ if(rst->filter_string){
+ srt_create_popup_menu(rst);
+ }
+}
+
+void
+init_srt_table_row(srt_stat_table *rst, int index, const char *procedure)
+{
+ /* we have discovered a new procedure. Extend the table accordingly */
+ if(index>=rst->num_procs){
+ int old_num_procs=rst->num_procs;
+ int i;
+
+ rst->num_procs=index+1;
+ rst->procedures=g_realloc(rst->procedures, sizeof(srt_procedure_t)*(rst->num_procs));
+ for(i=old_num_procs;i<rst->num_procs;i++){
+ time_stat_init(&rst->procedures[i].stats);
+ rst->procedures[i].index = i;
+ rst->procedures[i].procedure=NULL;
+ }
+ }
+ rst->procedures[index].index = index;
+ rst->procedures[index].procedure=g_strdup(procedure);
+}
+
+void
+add_srt_table_data(srt_stat_table *rst, int index, const nstime_t *req_time, packet_info *pinfo)
+{
+ srt_procedure_t *rp;
+ nstime_t t, delta;
+
+ g_assert(index >= 0 && index < rst->num_procs);
+ rp=&rst->procedures[index];
+
+ /*
+ * If the count of calls for this procedure is currently zero, it's
+ * going to become non-zero, so add a row for it (we don't want
+ * rows for procedures that have no calls - especially if the
+ * procedure has no calls because the index doesn't correspond
+ * to a procedure, but is an unused/reserved value).
+ *
+ * (Yes, this means that the rows aren't in order by anything
+ * interesting. That's why we have the table sorted by a column.)
+ */
+
+ if (rp->stats.num==0){
+ GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(rst->table));
+ gtk_list_store_append(store, &rp->iter);
+ gtk_list_store_set(store, &rp->iter,
+ INDEX_COLUMN, rp->index,
+ PROCEDURE_COLUMN, rp->procedure,
+ CALLS_COLUMN, rp->stats.num,
+ MIN_SRT_COLUMN, NULL,
+ MAX_SRT_COLUMN, NULL,
+ AVG_SRT_COLUMN, (guint64)0,
+ -1);
+ }
+
+ /* calculate time delta between request and reply */
+ t=pinfo->fd->abs_ts;
+ nstime_delta(&delta, &t, req_time);
+
+ time_stat_update(&rp->stats, &delta, pinfo);
+}
+
+void
+draw_srt_table_data(srt_stat_table *rst)
+{
+ int i;
+ guint64 td;
+ GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(rst->table));
+
+ for(i=0;i<rst->num_procs;i++){
+ /* ignore procedures with no calls (they don't have rows) */
+ if(rst->procedures[i].stats.num==0){
+ continue;
+ }
+ /* Scale the average SRT in units of 1us and round to the nearest us.
+ tot.secs is a time_t which may be 32 or 64 bits (or even floating)
+ depending uon the platform. After casting tot.secs to 64 bits, it
+ would take a capture with a duration of over 136 *years* to
+ overflow the secs portion of td. */
+ td = ((guint64)(rst->procedures[i].stats.tot.secs))*NANOSECS_PER_SEC + rst->procedures[i].stats.tot.nsecs;
+ td = ((td / rst->procedures[i].stats.num) + 500) / 1000;
+
+ gtk_list_store_set(store, &rst->procedures[i].iter,
+ CALLS_COLUMN, rst->procedures[i].stats.num,
+ MIN_SRT_COLUMN, &rst->procedures[i].stats.min,
+ MAX_SRT_COLUMN, &rst->procedures[i].stats.max,
+ AVG_SRT_COLUMN, td,
+ -1);
+ }
+}
+
+
+void
+reset_srt_table_data(srt_stat_table *rst)
+{
+ int i;
+ GtkListStore *store;
+
+ for(i=0;i<rst->num_procs;i++){
+ time_stat_init(&rst->procedures[i].stats);
+ }
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(rst->table));
+ gtk_list_store_clear(store);
+}
+
+void
+free_srt_table_data(srt_stat_table *rst)
+{
+ int i;
+
+ for(i=0;i<rst->num_procs;i++){
+ g_free(rst->procedures[i].procedure);
+ rst->procedures[i].procedure=NULL;
+ }
+ g_free(rst->filter_string);
+ rst->filter_string=NULL;
+ g_free(rst->procedures);
+ rst->procedures=NULL;
+ rst->num_procs=0;
+}
+
diff --git a/ui/gtk/service_response_time_table.h b/ui/gtk/service_response_time_table.h
new file mode 100644
index 0000000000..46da3700ee
--- /dev/null
+++ b/ui/gtk/service_response_time_table.h
@@ -0,0 +1,102 @@
+/* service_response_time_table.h
+ * service_response_time_table 2003 Ronnie Sahlberg
+ * Helper routines common to all service response time statistics
+ * tap.
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __SERVICE_RESPONSE_TIME_TABLE_H__
+#define __SERVICE_RESPONSE_TIME_TABLE_H__
+
+#include <gtk/gtk.h>
+#include "epan/nstime.h"
+#include "../timestats.h"
+
+/** @file
+ * Helper routines common to all service response time statistics tap.
+ */
+
+/** Procedure data */
+typedef struct _srt_procedure_t {
+ int index;
+ timestat_t stats; /**< stats */
+ char *procedure; /**< column entries */
+ GtkTreeIter iter;
+} srt_procedure_t;
+
+/** Statistics table */
+typedef struct _srt_stat_table {
+ GtkWidget *scrolled_window; /**< window widget */
+ GtkTreeView *table; /**< Tree view */
+ GtkWidget *menu; /**< context menu */
+ char *filter_string; /**< append procedure number (%d) to this string
+ to create a display filter */
+ int num_procs; /**< number of elements on procedures array */
+ srt_procedure_t *procedures;/**< the procedures array */
+} srt_stat_table;
+
+/** Init an srt table data structure.
+ *
+ * @param rst the srt table to init
+ * @param num_procs number of procedures
+ * @param vbox the corresponding GtkVBox to fill in
+ * @param filter_string filter string or NULL
+ */
+void init_srt_table(srt_stat_table *rst, int num_procs, GtkWidget *vbox,
+ const char *filter_string);
+
+/** Init an srt table row data structure.
+ *
+ * @param rst the srt table
+ * @param index number of procedure
+ * @param procedure the procedures name
+ */
+void init_srt_table_row(srt_stat_table *rst, int index, const char *procedure);
+
+/** Add srt response to table row data. This will not draw the data!
+ *
+ * @param rst the srt table
+ * @param index number of procedure
+ * @param req_time the time of the corresponding request
+ * @param pinfo current packet info
+ */
+void add_srt_table_data(srt_stat_table *rst, int index, const nstime_t *req_time, packet_info *pinfo);
+
+/** Draw the srt table data.
+ *
+ * @param rst the srt table
+ */
+void draw_srt_table_data(srt_stat_table *rst);
+
+/** Reset the srt table data.
+ *
+ * @param rst the srt table
+ */
+void reset_srt_table_data(srt_stat_table *rst);
+
+/** Free the srt table data.
+ *
+ * @param rst the srt table
+ */
+void free_srt_table_data(srt_stat_table *rst);
+
+#endif /* __SERVICE_RESPONSE_TIME_TABLE_H__ */
diff --git a/ui/gtk/simple_dialog.c b/ui/gtk/simple_dialog.c
new file mode 100644
index 0000000000..3504195dc1
--- /dev/null
+++ b/ui/gtk/simple_dialog.c
@@ -0,0 +1,414 @@
+/* simple_dialog.c
+ * Simple message dialog box routines.
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <stdio.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/strutil.h>
+
+#include "../simple_dialog.h"
+
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/stock_icons.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+static void simple_dialog_cancel_cb(GtkWidget *, gpointer);
+
+#define CALLBACK_FCT_KEY "ESD_Callback_Fct"
+#define CALLBACK_BTN_KEY "ESD_Callback_Btn"
+#define CALLBACK_DATA_KEY "ESD_Callback_Data"
+#define CHECK_BUTTON "ESD_Check_Button"
+
+/*
+ * Queue for messages requested before we have a main window.
+ */
+typedef struct {
+ gint type;
+ gint btn_mask;
+ char *message;
+} queued_message_t;
+
+static GSList *message_queue;
+
+static GtkWidget *
+display_simple_dialog(gint type, gint btn_mask, char *message)
+{
+ GtkWidget *win, *main_vb, *top_hb, *msg_vb, *type_pm, *msg_label, *ask_cb,
+ *bbox, *ok_bt, *yes_bt, *bt, *save_bt, *dont_save_bt;
+
+ /* Main window */
+ switch (type) {
+ case ESD_TYPE_WARN :
+ type_pm = gtk_image_new_from_stock( GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG);
+ break;
+ case ESD_TYPE_CONFIRMATION:
+ type_pm = gtk_image_new_from_stock( GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG);
+ break;
+ case ESD_TYPE_ERROR:
+ type_pm = gtk_image_new_from_stock( GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_DIALOG);
+ break;
+ case ESD_TYPE_STOP :
+ type_pm = gtk_image_new_from_stock( GTK_STOCK_STOP, GTK_ICON_SIZE_DIALOG);
+ break;
+ case ESD_TYPE_INFO :
+ default :
+ type_pm = gtk_image_new_from_stock( GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG);
+ break;
+ }
+
+ /*
+ * The GNOME HIG:
+ *
+ * http://developer.gnome.org/projects/gup/hig/1.0/windows.html#alert-windows
+ *
+ * says that the title should be empty for alert boxes, so there's "less
+ * visual noise and confounding text."
+ *
+ * The Windows HIG:
+ *
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwue/html/ch09f.asp
+ *
+ * says it should
+ *
+ * ...appropriately identify the source of the message -- usually
+ * the name of the object. For example, if the message results
+ * from editing a document, the title text is the name of the
+ * document, optionally followed by the application name. If the
+ * message results from a non-document object, then use the
+ * application name."
+ *
+ * and notes that the title is important "because message boxes might
+ * not always the the result of current user interaction" (e.g., some
+ * app might randomly pop something up, e.g. some browser letting you
+ * know that it couldn't fetch something because of a timeout).
+ *
+ * It also says not to use "warning" or "caution", as there's already
+ * an icon that tells you what type of alert it is, and that you
+ * shouldn't say "error", as that provides no useful information.
+ *
+ * So we give it a title on Win32, and don't give it one on UN*X.
+ * For now, we give it a Win32 title of just "Wireshark"; we should
+ * arguably take an argument for the title.
+ */
+ if(btn_mask == ESD_BTN_NONE) {
+ win = splash_window_new();
+ } else {
+#ifdef _WIN32
+ win = dlg_window_new("Wireshark");
+#else
+ win = dlg_window_new("");
+#endif
+ }
+
+ gtk_window_set_modal(GTK_WINDOW(win), TRUE);
+ gtk_container_set_border_width(GTK_CONTAINER(win), 6);
+
+ /* Container for our rows */
+ main_vb = gtk_vbox_new(FALSE, 12);
+ gtk_container_add(GTK_CONTAINER(win), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* Top row: Icon and message text */
+ top_hb = gtk_hbox_new(FALSE, 12);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 6);
+ gtk_container_add(GTK_CONTAINER(main_vb), top_hb);
+ gtk_widget_show(top_hb);
+
+ gtk_misc_set_alignment (GTK_MISC (type_pm), 0.5f, 0.0f);
+ gtk_container_add(GTK_CONTAINER(top_hb), type_pm);
+ gtk_widget_show(type_pm);
+
+ /* column for message and optional check button */
+ msg_vb = gtk_vbox_new(FALSE, 6);
+ gtk_box_set_spacing(GTK_BOX(msg_vb), 24);
+ gtk_container_add(GTK_CONTAINER(top_hb), msg_vb);
+ gtk_widget_show(msg_vb);
+
+ /* message */
+ msg_label = gtk_label_new(message);
+
+ gtk_label_set_markup(GTK_LABEL(msg_label), message);
+ gtk_label_set_selectable(GTK_LABEL(msg_label), TRUE);
+ g_object_set(gtk_widget_get_settings(msg_label),
+ "gtk-label-select-on-focus", FALSE, NULL);
+
+ gtk_label_set_justify(GTK_LABEL(msg_label), GTK_JUSTIFY_FILL);
+ gtk_misc_set_alignment (GTK_MISC (type_pm), 0.5f, 0.0f);
+ gtk_container_add(GTK_CONTAINER(msg_vb), msg_label);
+ gtk_label_set_line_wrap(GTK_LABEL(msg_label), TRUE);
+ gtk_widget_show(msg_label);
+
+ if(btn_mask == ESD_BTN_NONE) {
+ gtk_widget_show(win);
+ return win;
+ }
+
+ /* optional check button */
+ ask_cb = gtk_check_button_new_with_label("replace with text...");
+ gtk_container_add(GTK_CONTAINER(msg_vb), ask_cb);
+ g_object_set_data(G_OBJECT(win), CHECK_BUTTON, ask_cb);
+
+ /* Button row */
+ switch(btn_mask) {
+ case(ESD_BTN_OK):
+ bbox = dlg_button_row_new(GTK_STOCK_OK, NULL);
+ break;
+ case(ESD_BTN_OK | ESD_BTN_CANCEL):
+ bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL);
+ break;
+ case(ESD_BTN_CLEAR | ESD_BTN_CANCEL):
+ bbox = dlg_button_row_new(GTK_STOCK_CLEAR, GTK_STOCK_CANCEL, NULL);
+ break;
+ case(ESD_BTNS_YES_NO_CANCEL):
+ bbox = dlg_button_row_new(GTK_STOCK_YES, GTK_STOCK_NO, GTK_STOCK_CANCEL, NULL);
+ break;
+ case(ESD_BTNS_SAVE_DONTSAVE_CANCEL):
+ bbox = dlg_button_row_new(GTK_STOCK_SAVE, WIRESHARK_STOCK_DONT_SAVE, GTK_STOCK_CANCEL, NULL);
+ break;
+ case(ESD_BTNS_SAVE_QUIT_DONTSAVE_CANCEL):
+ bbox = dlg_button_row_new(GTK_STOCK_SAVE, WIRESHARK_STOCK_QUIT_DONT_SAVE, GTK_STOCK_CANCEL, NULL);
+ break;
+ case (ESD_BTNS_QUIT_DONTSAVE_CANCEL):
+ bbox = dlg_button_row_new(WIRESHARK_STOCK_QUIT_DONT_SAVE, GTK_STOCK_CANCEL, NULL);
+ break;
+ case(ESD_BTNS_YES_NO):
+ bbox = dlg_button_row_new(GTK_STOCK_YES, GTK_STOCK_NO, NULL);
+ break;
+ default:
+ g_assert_not_reached();
+ bbox = NULL;
+ break;
+ }
+ gtk_container_add(GTK_CONTAINER(main_vb), bbox);
+ gtk_widget_show(bbox);
+
+ ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ if(ok_bt) {
+ g_object_set_data(G_OBJECT(ok_bt), CALLBACK_BTN_KEY, GINT_TO_POINTER(ESD_BTN_OK));
+ g_signal_connect(ok_bt, "clicked", G_CALLBACK(simple_dialog_cancel_cb), win);
+ }
+
+ save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
+ if (save_bt) {
+ g_object_set_data(G_OBJECT(save_bt), CALLBACK_BTN_KEY, GINT_TO_POINTER(ESD_BTN_SAVE));
+ g_signal_connect(save_bt, "clicked", G_CALLBACK(simple_dialog_cancel_cb), win);
+ }
+
+ dont_save_bt = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_DONT_SAVE);
+ if (dont_save_bt) {
+ g_object_set_data(G_OBJECT(dont_save_bt), CALLBACK_BTN_KEY, GINT_TO_POINTER(ESD_BTN_DONT_SAVE));
+ g_signal_connect(dont_save_bt, "clicked", G_CALLBACK(simple_dialog_cancel_cb), win);
+ }
+
+ dont_save_bt = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_QUIT_DONT_SAVE);
+ if (dont_save_bt) {
+ g_object_set_data(G_OBJECT(dont_save_bt), CALLBACK_BTN_KEY, GINT_TO_POINTER(ESD_BTN_QUIT_DONT_SAVE));
+ g_signal_connect(dont_save_bt, "clicked", G_CALLBACK(simple_dialog_cancel_cb), win);
+ }
+ bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLEAR);
+ if(bt) {
+ g_object_set_data(G_OBJECT(bt), CALLBACK_BTN_KEY, GINT_TO_POINTER(ESD_BTN_CLEAR));
+ g_signal_connect(bt, "clicked", G_CALLBACK(simple_dialog_cancel_cb), win);
+ }
+
+ yes_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_YES);
+ if(yes_bt) {
+ g_object_set_data(G_OBJECT(yes_bt), CALLBACK_BTN_KEY, GINT_TO_POINTER(ESD_BTN_YES));
+ g_signal_connect(yes_bt, "clicked", G_CALLBACK(simple_dialog_cancel_cb), win);
+ }
+
+ bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_NO);
+ if(bt) {
+ g_object_set_data(G_OBJECT(bt), CALLBACK_BTN_KEY, GINT_TO_POINTER(ESD_BTN_NO));
+ g_signal_connect(bt, "clicked", G_CALLBACK(simple_dialog_cancel_cb), win);
+ }
+
+ bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ if(bt) {
+ g_object_set_data(G_OBJECT(bt), CALLBACK_BTN_KEY, GINT_TO_POINTER(ESD_BTN_CANCEL));
+ window_set_cancel_button(win, bt, simple_dialog_cancel_cb);
+ }
+
+ if(!bt) {
+ if(yes_bt) {
+ window_set_cancel_button(win, yes_bt, simple_dialog_cancel_cb);
+ } else {
+ window_set_cancel_button(win, ok_bt, simple_dialog_cancel_cb);
+ }
+ }
+
+ dlg_button_focus_nth(bbox, 0);
+
+ gtk_widget_show(win);
+
+ return win;
+}
+
+void
+display_queued_messages(void)
+{
+ queued_message_t *queued_message;
+
+ while (message_queue != NULL) {
+ queued_message = message_queue->data;
+ message_queue = g_slist_remove(message_queue, queued_message);
+
+ display_simple_dialog(queued_message->type, queued_message->btn_mask,
+ queued_message->message);
+
+ g_free(queued_message->message);
+ g_free(queued_message);
+ }
+}
+
+/* Simple dialog function - Displays a dialog box with the supplied message
+ * text.
+ *
+ * Args:
+ * type : One of ESD_TYPE_*.
+ * btn_mask : The value passed in determines which buttons are displayed.
+ * msg_format : Sprintf-style format of the text displayed in the dialog.
+ * ... : Argument list for msg_format
+ */
+
+gpointer
+vsimple_dialog(ESD_TYPE_E type, gint btn_mask, const gchar *msg_format, va_list ap)
+{
+ gchar *message;
+ queued_message_t *queued_message;
+ GtkWidget *win;
+ GdkWindowState state = 0;
+
+ /* Format the message. */
+ message = g_strdup_vprintf(msg_format, ap);
+
+ if (top_level != NULL) {
+ state = gdk_window_get_state(gtk_widget_get_window(top_level));
+ }
+
+ /* If we don't yet have a main window or it's iconified or hidden (i.e. not
+ yet ready, don't show the dialog. If showing up a dialog, while main
+ window is iconified, program will become unresponsive! */
+ if (top_level == NULL || state & GDK_WINDOW_STATE_ICONIFIED
+ || state & GDK_WINDOW_STATE_WITHDRAWN) {
+
+ queued_message = g_malloc(sizeof (queued_message_t));
+ queued_message->type = type;
+ queued_message->btn_mask = btn_mask;
+ queued_message->message = message;
+ message_queue = g_slist_append(message_queue, queued_message);
+ return NULL;
+ }
+
+ /*
+ * Do we have any queued up messages? If so, pop them up.
+ */
+ display_queued_messages();
+
+ win = display_simple_dialog(type, btn_mask, message);
+
+ g_free(message);
+
+ return win;
+}
+
+gpointer
+simple_dialog(ESD_TYPE_E type, gint btn_mask, const gchar *msg_format, ...)
+{
+ va_list ap;
+ gpointer ret;
+
+ va_start(ap, msg_format);
+ ret = vsimple_dialog(type, btn_mask, msg_format, ap);
+ va_end(ap);
+ return ret;
+}
+
+static void
+simple_dialog_cancel_cb(GtkWidget *w, gpointer win) {
+ gint button = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(w), CALLBACK_BTN_KEY));
+ simple_dialog_cb_t callback_fct = g_object_get_data(G_OBJECT(win), CALLBACK_FCT_KEY);
+ gpointer data = g_object_get_data(G_OBJECT(win), CALLBACK_DATA_KEY);
+
+ if (callback_fct)
+ (callback_fct) (win, button, data);
+
+ window_destroy(GTK_WIDGET(win));
+}
+
+void
+simple_dialog_close(gpointer dialog)
+{
+ window_destroy(GTK_WIDGET(dialog));
+}
+
+void simple_dialog_set_cb(gpointer dialog, simple_dialog_cb_t callback_fct, gpointer data)
+{
+
+ g_object_set_data(G_OBJECT(GTK_WIDGET(dialog)), CALLBACK_FCT_KEY, callback_fct);
+ g_object_set_data(G_OBJECT(GTK_WIDGET(dialog)), CALLBACK_DATA_KEY, data);
+}
+
+void simple_dialog_check_set(gpointer dialog, gchar *text _U_) {
+ GtkWidget *ask_cb = g_object_get_data(G_OBJECT(dialog), CHECK_BUTTON);
+
+ gtk_button_set_label(GTK_BUTTON(ask_cb), text);
+ gtk_widget_show(ask_cb);
+}
+
+gboolean simple_dialog_check_get(gpointer dialog) {
+ GtkWidget *ask_cb = g_object_get_data(G_OBJECT(GTK_WIDGET(dialog)), CHECK_BUTTON);
+
+ return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ask_cb));
+}
+
+char *
+simple_dialog_primary_start(void) {
+ return "<span weight=\"bold\" size=\"larger\">";
+}
+
+char *
+simple_dialog_primary_end(void) {
+ return "</span>";
+}
+
+char *
+simple_dialog_format_message(const char *msg)
+{
+ char *str;
+
+ if (msg) {
+ str = xml_escape(msg);
+ } else {
+ str = NULL;
+ }
+ return str;
+}
diff --git a/ui/gtk/sip_stat.c b/ui/gtk/sip_stat.c
new file mode 100644
index 0000000000..f8a68dd641
--- /dev/null
+++ b/ui/gtk/sip_stat.c
@@ -0,0 +1,748 @@
+/* sip_stat.c
+ * sip_stat 2004 Martin Mathieson
+ *
+ * $Id$
+ * Copied from http_stat.c
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-sip.h>
+
+#include "../simple_dialog.h"
+#include "../globals.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/main.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+#define SUM_STR_MAX 1024
+
+/* Used to keep track of the statistics for an entire program interface */
+typedef struct _sip_stats_t {
+ char *filter;
+ GtkWidget *win;
+ GHashTable *hash_responses;
+ GHashTable *hash_requests;
+ guint32 packets; /* number of sip packets, including continuations */
+ guint32 resent_packets;
+ guint32 average_setup_time;
+ guint32 max_setup_time;
+ guint32 min_setup_time;
+ guint32 no_of_completed_calls;
+ guint64 total_setup_time;
+ GtkWidget *packets_label;
+ GtkWidget *resent_label;
+ GtkWidget *average_setup_time_label;
+
+ GtkWidget *request_box; /* container for INVITE, ... */
+
+ GtkWidget *informational_table; /* Status code between 100 and 199 */
+ GtkWidget *success_table; /* 200 and 299 */
+ GtkWidget *redirection_table; /* 300 and 399 */
+ GtkWidget *client_error_table; /* 400 and 499 */
+ GtkWidget *server_errors_table; /* 500 and 599 */
+ GtkWidget *global_failures_table; /* 600 and 699 */
+} sipstat_t;
+
+/* Used to keep track of the stats for a specific response code
+ * for example it can be { 3, 404, "Not Found" ,...}
+ * which means we captured 3 reply sip/1.1 404 Not Found */
+typedef struct _sip_response_code_t {
+ guint32 packets; /* 3 */
+ guint response_code; /* 404 */
+ const gchar *name; /* "Not Found" */
+ GtkWidget *widget; /* Label where we display it */
+ GtkWidget *table; /* Table in which we put it,
+ e.g. client_error_table */
+ sipstat_t *sp; /* Pointer back to main struct */
+} sip_response_code_t;
+
+/* Used to keep track of the stats for a specific request string */
+typedef struct _sip_request_method_t {
+ gchar *response; /* eg. : INVITE */
+ guint32 packets;
+ GtkWidget *widget;
+ sipstat_t *sp; /* Pointer back to main struct */
+} sip_request_method_t;
+
+/* TODO: extra codes to be added from SIP extensions? */
+static const value_string vals_status_code[] = {
+ { 100, "Trying"},
+ { 180, "Ringing"},
+ { 181, "Call Is Being Forwarded"},
+ { 182, "Queued"},
+ { 183, "Session Progress"},
+ { 199, "Informational - Others" },
+
+ { 200, "OK"},
+ { 202, "Accepted"},
+ { 204, "No Notification"},
+ { 299, "Success - Others"}, /* used to keep track of other Success packets */
+
+ { 300, "Multiple Choices"},
+ { 301, "Moved Permanently"},
+ { 302, "Moved Temporarily"},
+ { 305, "Use Proxy"},
+ { 380, "Alternative Service"},
+ { 399, "Redirection - Others"},
+
+ { 400, "Bad Request"},
+ { 401, "Unauthorized"},
+ { 402, "Payment Required"},
+ { 403, "Forbidden"},
+ { 404, "Not Found"},
+ { 405, "Method Not Allowed"},
+ { 406, "Not Acceptable"},
+ { 407, "Proxy Authentication Required"},
+ { 408, "Request Timeout"},
+ { 410, "Gone"},
+ { 412, "Conditional Request Failed"},
+ { 413, "Request Entity Too Large"},
+ { 414, "Request-URI Too Long"},
+ { 415, "Unsupported Media Type"},
+ { 416, "Unsupported URI Scheme"},
+ { 420, "Bad Extension"},
+ { 421, "Extension Required"},
+ { 422, "Session Timer Too Small"},
+ { 423, "Interval Too Brief"},
+ { 428, "Use Identity Header"},
+ { 429, "Provide Referrer Identity"},
+ { 430, "Flow Failed"},
+ { 433, "Anonymity Disallowed"},
+ { 436, "Bad Identity-Info"},
+ { 437, "Unsupported Certificate"},
+ { 438, "Invalid Identity Header"},
+ { 439, "First Hop Lacks Outbound Support"},
+ { 440, "Max-Breadth Exceeded"},
+ { 470, "Consent Needed"},
+ { 480, "Temporarily Unavailable"},
+ { 481, "Call/Transaction Does Not Exist"},
+ { 482, "Loop Detected"},
+ { 483, "Too Many Hops"},
+ { 484, "Address Incomplete"},
+ { 485, "Ambiguous"},
+ { 486, "Busy Here"},
+ { 487, "Request Terminated"},
+ { 488, "Not Acceptable Here"},
+ { 489, "Bad Event"},
+ { 491, "Request Pending"},
+ { 493, "Undecipherable"},
+ { 494, "Security Agreement Required"},
+ { 499, "Client Error - Others"},
+
+ { 500, "Server Internal Error"},
+ { 501, "Not Implemented"},
+ { 502, "Bad Gateway"},
+ { 503, "Service Unavailable"},
+ { 504, "Server Time-out"},
+ { 505, "Version Not Supported"},
+ { 513, "Message Too Large"},
+ { 599, "Server Error - Others"},
+
+ { 600, "Busy Everywhere"},
+ { 603, "Decline"},
+ { 604, "Does Not Exist Anywhere"},
+ { 606, "Not Acceptable"},
+ { 699, "Global Failure - Others"},
+
+ { 0, NULL}
+};
+
+void register_tap_listener_gtksipstat(void);
+
+
+/* Create tables for responses and requests */
+static void
+sip_init_hash(sipstat_t *sp)
+{
+ int i;
+
+ /* Create responses table */
+ sp->hash_responses = g_hash_table_new(g_int_hash, g_int_equal);
+
+ /* Add all response codes */
+ for (i=0 ; vals_status_code[i].strptr ; i++)
+ {
+ gint *key = g_malloc (sizeof(gint));
+ sip_response_code_t *sc = g_malloc (sizeof(sip_response_code_t));
+ *key = vals_status_code[i].value;
+ sc->packets=0;
+ sc->response_code = *key;
+ sc->name=vals_status_code[i].strptr;
+ sc->widget=NULL;
+ sc->table=NULL;
+ sc->sp = sp;
+ g_hash_table_insert(sc->sp->hash_responses, key, sc);
+ }
+
+ /* Create empty requests table */
+ sp->hash_requests = g_hash_table_new(g_str_hash, g_str_equal);
+}
+
+/* Draw the entry for an individual request message */
+static void
+sip_draw_hash_requests(gchar *key _U_ , sip_request_method_t *data, gchar * unused _U_)
+{
+ gchar string_buff[SUM_STR_MAX];
+
+ g_assert(data!=NULL);
+
+ if (data->packets==0)
+ {
+ return;
+ }
+
+ /* Build string showing method and count */
+ g_snprintf(string_buff, sizeof(string_buff),
+ " %-11s : %3d packets", data->response, data->packets);
+ if (data->widget==NULL)
+ {
+ /* Create new label */
+ data->widget=gtk_label_new(string_buff);
+ gtk_misc_set_alignment(GTK_MISC(data->widget), 0.0f, 0.5f);
+ gtk_box_pack_start(GTK_BOX(data->sp->request_box), data->widget,FALSE,FALSE, 0);
+ gtk_widget_show(data->widget);
+ }
+ else
+ {
+ /* Update existing label */
+ gtk_label_set_text(GTK_LABEL(data->widget), string_buff);
+ }
+}
+
+/* Draw an individual response entry */
+static void
+sip_draw_hash_responses(gint * key _U_ , sip_response_code_t *data, gchar * unused _U_)
+{
+ gchar string_buff[SUM_STR_MAX];
+
+ g_assert(data!=NULL);
+
+ if (data->packets==0)
+ {
+ return;
+ }
+
+ /* Create an entry in the relevant box of the window */
+ if (data->widget==NULL)
+ {
+ guint x;
+ GtkWidget *tmp;
+ guint i = data->response_code;
+
+ /* Out of valid range - ignore */
+ if ((i<100)||(i>=700))
+ {
+ return;
+ }
+
+ /* Find the table matching the code */
+ if (i<200)
+ {
+ data->table = data->sp->informational_table;
+ }
+ else if (i<300)
+ {
+ data->table = data->sp->success_table;
+ }
+ else if (i<400)
+ {
+ data->table = data->sp->redirection_table;
+ }
+ else if (i<500)
+ {
+ data->table = data->sp->client_error_table;
+ }
+ else if (i < 600)
+ {
+ data->table = data->sp->server_errors_table;
+ }
+ else
+ {
+ data->table = data->sp->global_failures_table;
+ }
+
+ /* Get number of rows in table */
+#if GTK_CHECK_VERSION(2,22,0)
+ gtk_table_get_size(GTK_TABLE(data->table), &x, NULL);
+#else
+ /* Work around GTK bug: Sealed in 2.14, accessor provided in 2.22 */
+# if GTK_CHECK_VERSION (2, 14, 0) && defined(GSEAL_ENABLE)
+ x = GTK_TABLE(data->table)->_g_sealed__nrows;
+# else
+ x = GTK_TABLE(data->table)->nrows;
+# endif
+#endif
+
+ /* Create a new label with this response, e.g. "SIP 180 Ringing" */
+ g_snprintf(string_buff, sizeof(string_buff),
+ "SIP %3d %s ", data->response_code, data->name);
+ tmp = gtk_label_new(string_buff);
+
+ /* Insert the label in the correct place in the table */
+ gtk_table_attach_defaults(GTK_TABLE(data->table), tmp, 0, 1, x, x+1);
+ gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
+ gtk_widget_show(tmp);
+
+ /* Show number of packets */
+ g_snprintf(string_buff, sizeof(string_buff), "%9d", data->packets);
+ data->widget=gtk_label_new(string_buff);
+
+ /* Show this widget in the right place */
+ gtk_table_attach_defaults(GTK_TABLE(data->table), data->widget, 1, 2,x,x+1);
+ gtk_label_set_justify(GTK_LABEL(data->widget), GTK_JUSTIFY_RIGHT);
+ gtk_widget_show(data->widget);
+
+ gtk_table_resize(GTK_TABLE(data->table), x+1, 4);
+
+ } else
+ {
+ /* Just update the existing label string */
+ g_snprintf(string_buff, sizeof(string_buff), "%9d", data->packets);
+ gtk_label_set_text(GTK_LABEL(data->widget), string_buff);
+ }
+}
+
+
+
+static void
+sip_free_hash(gpointer key, gpointer value, gpointer user_data _U_)
+{
+ g_free(key);
+ g_free(value);
+}
+
+static void
+sip_reset_hash_responses(gchar *key _U_ , sip_response_code_t *data, gpointer ptr _U_)
+{
+ data->packets = 0;
+}
+
+static void
+sip_reset_hash_requests(gchar *key _U_ , sip_request_method_t *data, gpointer ptr _U_)
+{
+ data->packets = 0;
+}
+
+static void
+sipstat_reset(void *psp)
+{
+ sipstat_t *sp = psp;
+ if (sp)
+ {
+ sp->packets = 0;
+ sp->resent_packets = 0;
+ sp->average_setup_time = 0;
+ sp->max_setup_time = 0;
+ sp->max_setup_time = 0;
+ sp->no_of_completed_calls = 0;
+ sp->total_setup_time = 0;
+ g_hash_table_foreach(sp->hash_responses, (GHFunc)sip_reset_hash_responses, NULL);
+ g_hash_table_foreach(sp->hash_requests, (GHFunc)sip_reset_hash_requests, NULL);
+ }
+}
+
+/* Main entry point to SIP tap */
+static int
+sipstat_packet(void *psp, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *pri)
+{
+ const sip_info_value_t *value=pri;
+ sipstat_t *sp = (sipstat_t *)psp;
+
+ /* Total number of packets, including continuation packets */
+ sp->packets++;
+
+ /* Update resent count if flag set */
+ if (value->resend)
+ {
+ sp->resent_packets++;
+ }
+
+ /* Calculate average setup time */
+ if (value->setup_time){
+ sp->no_of_completed_calls++;
+ /* Check if it's the first value */
+ if ( sp->total_setup_time == 0 ){
+ sp->average_setup_time = value->setup_time;
+ sp->total_setup_time = value->setup_time;
+ sp->max_setup_time = value->setup_time;
+ sp->min_setup_time = value->setup_time;
+ }else{
+ sp->total_setup_time = sp->total_setup_time + value->setup_time;
+ if (sp->max_setup_time < value->setup_time){
+ sp->max_setup_time = value->setup_time;
+ }
+ if (sp->min_setup_time > value->setup_time){
+ sp->min_setup_time = value->setup_time;
+ }
+ /* Calculate average */
+ sp->average_setup_time = (guint32)(sp->total_setup_time / sp->no_of_completed_calls);
+ }
+ }
+
+ /* Looking at both requests and responses */
+ if (value->response_code != 0)
+ {
+ /* Responses */
+ guint *key = g_malloc(sizeof(guint));
+ sip_response_code_t *sc;
+
+ /* Look up response code in hash table */
+ *key = value->response_code;
+ sc = g_hash_table_lookup(sp->hash_responses, key);
+ if (sc==NULL)
+ {
+ /* Non-standard status code ; we classify it as others
+ * in the relevant category
+ * (Informational,Success,Redirection,Client Error,Server Error,Global Failure)
+ */
+ int i = value->response_code;
+ if ((i<100) || (i>=700))
+ {
+ /* Forget about crazy values */
+ return 0;
+ }
+ else if (i<200)
+ {
+ *key=199; /* Hopefully, this status code will never be used */
+ }
+ else if (i<300)
+ {
+ *key=299;
+ }
+ else if (i<400)
+ {
+ *key=399;
+ }
+ else if (i<500)
+ {
+ *key=499;
+ }
+ else if (i<600)
+ {
+ *key=599;
+ }
+ else
+ {
+ *key = 699;
+ }
+
+ /* Now look up this fallback code to get its text description */
+ sc = g_hash_table_lookup(sp->hash_responses, key);
+ if (sc==NULL)
+ {
+ return 0;
+ }
+ }
+ sc->packets++;
+ }
+ else if (value->request_method)
+ {
+ /* Requests */
+ sip_request_method_t *sc;
+
+ /* Look up the request method in the table */
+ sc = g_hash_table_lookup(sp->hash_requests, value->request_method);
+ if (sc == NULL)
+ {
+ /* First of this type. Create structure and initialise */
+ sc=g_malloc(sizeof(sip_request_method_t));
+ sc->response = g_strdup(value->request_method);
+ sc->packets = 1;
+ sc->widget = NULL;
+ sc->sp = sp;
+ /* Insert it into request table */
+ g_hash_table_insert(sp->hash_requests, sc->response, sc);
+ }
+ else
+ {
+ /* Already existed, just update count for that method */
+ sc->packets++;
+ }
+ /* g_free(value->request_method); */
+ }
+ else
+ {
+ /* No request method set. Just ignore */
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Redraw the whole stats window */
+static void
+sipstat_draw(void *psp)
+{
+ gchar string_buff[SUM_STR_MAX];
+ sipstat_t *sp=psp;
+
+ /* Set summary label */
+ g_snprintf(string_buff, sizeof(string_buff),
+ "SIP stats (%d packets)", sp->packets);
+ gtk_label_set_text(GTK_LABEL(sp->packets_label), string_buff);
+
+ /* Set resend count label */
+ g_snprintf(string_buff, sizeof(string_buff),
+ "(%d resent packets)", sp->resent_packets);
+ gtk_label_set_text(GTK_LABEL(sp->resent_label), string_buff);
+
+ /* Draw responses and requests from their tables */
+ g_hash_table_foreach(sp->hash_responses, (GHFunc)sip_draw_hash_responses, NULL);
+ g_hash_table_foreach(sp->hash_requests, (GHFunc)sip_draw_hash_requests, NULL);
+
+ /* Set resend count label */
+ g_snprintf(string_buff, sizeof(string_buff),
+ "Average setup time %d ms\n Min %d ms\n Max %d ms", sp->average_setup_time, sp->min_setup_time, sp->max_setup_time);
+ gtk_label_set_text(GTK_LABEL(sp->average_setup_time_label), string_buff);
+
+ gtk_widget_show_all(sp->win);
+}
+
+
+/* since the gtk2 implementation of tap is multithreaded we must protect
+ * remove_tap_listener() from modifying the list while draw_tap_listener()
+ * is running. the other protected block is in main.c
+ *
+ * there should not be any other critical regions in gtk2
+ */
+/* When window is destroyed, clean up */
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ sipstat_t *sp=(sipstat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(sp);
+ unprotect_thread_critical_region();
+
+ g_hash_table_foreach(sp->hash_responses, (GHFunc)sip_free_hash, NULL);
+ g_hash_table_destroy(sp->hash_responses);
+ g_hash_table_foreach(sp->hash_requests, (GHFunc)sip_free_hash, NULL);
+ g_hash_table_destroy(sp->hash_requests);
+ g_free(sp->filter);
+ g_free(sp);
+}
+
+
+/* Create a new instance of gtk_sipstat. */
+static void
+gtk_sipstat_init(const char *optarg, void *userdata _U_)
+{
+ sipstat_t *sp;
+ const char *filter = NULL;
+ GString *error_string;
+ char *title = NULL;
+ GtkWidget *main_vb, *separator,
+ *informational_fr, *success_fr, *redirection_fr,
+ *client_errors_fr, *server_errors_fr, *global_failures_fr,
+ *request_fr;
+ GtkWidget *bt_close;
+ GtkWidget *bbox;
+
+
+ if (strncmp (optarg, "sip,stat,", 9) == 0)
+ {
+ /* Skip those characters from filter to display */
+ filter=optarg + 9;
+ }
+ else
+ {
+ /* No filter */
+ filter = NULL;
+ }
+
+ /* Create sip stats window structure */
+ sp = g_malloc(sizeof(sipstat_t));
+ sp->win = dlg_window_new("sip-stat"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(sp->win), TRUE);
+
+ /* Set title to include any filter given */
+ if (filter)
+ {
+ sp->filter = g_strdup(filter);
+ title = g_strdup_printf("SIP statistics with filter: %s", filter);
+ }
+ else
+ {
+ sp->filter = NULL;
+ title = g_strdup("SIP statistics");
+ }
+
+ gtk_window_set_title(GTK_WINDOW(sp->win), title);
+ g_free(title);
+
+
+ /* Create container for all widgets */
+ main_vb = gtk_vbox_new(FALSE, 12);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 12);
+ gtk_container_add(GTK_CONTAINER(sp->win), main_vb);
+
+ /* Initialise & show number of packets */
+ sp->packets = 0;
+ sp->packets_label = gtk_label_new("SIP stats (0 SIP packets)");
+ gtk_container_add(GTK_CONTAINER(main_vb), sp->packets_label);
+
+ sp->resent_packets = 0;
+ sp->resent_label = gtk_label_new("(0 resent packets)");
+ gtk_container_add(GTK_CONTAINER(main_vb), sp->resent_label);
+ gtk_widget_show(sp->resent_label);
+
+
+ /* Informational response frame */
+ informational_fr = gtk_frame_new("Informational SIP 1xx");
+ gtk_container_add(GTK_CONTAINER(main_vb), informational_fr);
+
+ /* Information table (within that frame) */
+ sp->informational_table = gtk_table_new(0, 2, FALSE);
+ gtk_container_add(GTK_CONTAINER(informational_fr), sp->informational_table);
+
+ /* Success table and frame */
+ success_fr = gtk_frame_new ("Success SIP 2xx");
+ gtk_container_add(GTK_CONTAINER(main_vb), success_fr);
+
+ sp->success_table = gtk_table_new(0, 2, FALSE);
+ gtk_container_add(GTK_CONTAINER(success_fr), sp->success_table);
+
+ /* Redirection table and frame */
+ redirection_fr = gtk_frame_new ("Redirection SIP 3xx");
+ gtk_container_add(GTK_CONTAINER(main_vb), redirection_fr);
+
+ sp->redirection_table = gtk_table_new(0, 2, FALSE);
+ gtk_container_add(GTK_CONTAINER(redirection_fr), sp->redirection_table);
+
+ /* Client Errors table and frame */
+ client_errors_fr = gtk_frame_new("Client errors SIP 4xx");
+ gtk_container_add(GTK_CONTAINER(main_vb), client_errors_fr);
+
+ sp->client_error_table = gtk_table_new(0, 2, FALSE);
+ gtk_container_add(GTK_CONTAINER(client_errors_fr), sp->client_error_table);
+
+ /* Server Errors table and frame */
+ server_errors_fr = gtk_frame_new("Server errors SIP 5xx");
+ gtk_container_add(GTK_CONTAINER(main_vb), server_errors_fr);
+
+ sp->server_errors_table = gtk_table_new(0, 2, FALSE);
+ gtk_container_add(GTK_CONTAINER(server_errors_fr), sp->server_errors_table);
+
+ /* Global Failures table and frame */
+ global_failures_fr = gtk_frame_new("Global failures SIP 6xx");
+ gtk_container_add(GTK_CONTAINER(main_vb), global_failures_fr);
+
+ sp->global_failures_table = gtk_table_new(0, 2, FALSE);
+ gtk_container_add(GTK_CONTAINER(global_failures_fr), sp->global_failures_table);
+
+
+ /* Separator between requests and responses */
+ separator = gtk_hseparator_new();
+ gtk_container_add(GTK_CONTAINER(main_vb), separator);
+
+ /* Request table and frame */
+ request_fr = gtk_frame_new("List of request methods");
+ gtk_container_add(GTK_CONTAINER(main_vb), request_fr);
+ gtk_container_set_border_width(GTK_CONTAINER(request_fr), 0);
+
+ sp->request_box = gtk_vbox_new(FALSE, 10);
+ gtk_container_add(GTK_CONTAINER(request_fr), sp->request_box);
+
+ sp->average_setup_time = 0;
+ sp->max_setup_time =0;
+ sp->min_setup_time =0;
+ sp->average_setup_time_label = gtk_label_new("(Not calculated)");
+ gtk_container_add(GTK_CONTAINER(main_vb), sp->average_setup_time_label);
+ gtk_widget_show(sp->average_setup_time_label);
+
+
+ /* Register this tap listener now */
+ error_string = register_tap_listener("sip",
+ sp,
+ filter,
+ 0,
+ sipstat_reset,
+ sipstat_packet,
+ sipstat_draw);
+ if (error_string)
+ {
+ /* Error. We failed to attach to the tap. Clean up */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_free(sp->filter);
+ g_free(sp);
+ g_string_free(error_string, TRUE);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+
+ bt_close = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(sp->win, bt_close, window_cancel_button_cb);
+
+ g_signal_connect(sp->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(sp->win, "destroy", G_CALLBACK(win_destroy_cb), sp);
+
+ /* Display up-to-date contents */
+ gtk_widget_show_all(sp->win);
+ window_present(sp->win);
+
+ sip_init_hash(sp);
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(sp->win));
+}
+
+static tap_param sip_stat_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg sip_stat_dlg = {
+ "SIP Packet Counter",
+ "sip,stat",
+ gtk_sipstat_init,
+ -1,
+ G_N_ELEMENTS(sip_stat_params),
+ sip_stat_params
+};
+
+/* Register this tap listener and add menu item. */
+void
+register_tap_listener_gtksipstat(void)
+{
+ register_dfilter_stat(&sip_stat_dlg, "_SIP", REGISTER_STAT_GROUP_TELEPHONY);
+}
+
+void sipstat_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &sip_stat_dlg);
+}
+
diff --git a/ui/gtk/smb2_stat.c b/ui/gtk/smb2_stat.c
new file mode 100644
index 0000000000..fa1eb626f7
--- /dev/null
+++ b/ui/gtk/smb2_stat.c
@@ -0,0 +1,227 @@
+/* smb2_stat.c
+ * smb2_stat 2005 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/value_string.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-smb2.h>
+
+#include "../timestats.h"
+#include "../simple_dialog.h"
+#include "../file.h"
+#include "../globals.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/service_response_time_table.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/main.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _smb2stat_t {
+ GtkWidget *win;
+ srt_stat_table smb2_srt_table;
+} smb2stat_t;
+
+static void
+smb2stat_set_title(smb2stat_t *ss)
+{
+ char *title;
+
+ title = g_strdup_printf("SMB2 Service Response Time statistics: %s",
+ cf_get_display_name(&cfile));
+ gtk_window_set_title(GTK_WINDOW(ss->win), title);
+ g_free(title);
+}
+
+static void
+smb2stat_reset(void *pss)
+{
+ smb2stat_t *ss=(smb2stat_t *)pss;
+
+ reset_srt_table_data(&ss->smb2_srt_table);
+ smb2stat_set_title(ss);
+}
+
+static int
+smb2stat_packet(void *pss, packet_info *pinfo, epan_dissect_t *edt _U_, const void *psi)
+{
+ smb2stat_t *ss=(smb2stat_t *)pss;
+ const smb2_info_t *si=psi;
+
+ /* we are only interested in response packets */
+ if(!(si->flags&SMB2_FLAGS_RESPONSE)){
+ return 0;
+ }
+ /* if we havnt seen the request, just ignore it */
+ if(!si->saved){
+ return 0;
+ }
+
+ add_srt_table_data(&ss->smb2_srt_table, si->opcode, &si->saved->req_time, pinfo);
+
+ return 1;
+}
+
+
+
+static void
+smb2stat_draw(void *pss)
+{
+ smb2stat_t *ss=(smb2stat_t *)pss;
+
+ draw_srt_table_data(&ss->smb2_srt_table);
+}
+
+
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ smb2stat_t *ss=(smb2stat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(ss);
+ unprotect_thread_critical_region();
+
+ free_srt_table_data(&ss->smb2_srt_table);
+ g_free(ss);
+}
+
+
+static void
+gtk_smb2stat_init(const char *optarg, void *userdata _U_)
+{
+ smb2stat_t *ss;
+ const char *filter=NULL;
+ GtkWidget *label;
+ char *filter_string;
+ GString *error_string;
+ int i;
+ GtkWidget *vbox;
+ GtkWidget *bbox;
+ GtkWidget *close_bt;
+
+ if(!strncmp(optarg,"smb2,srt,",9)){
+ filter=optarg+9;
+ } else {
+ filter=NULL;
+ }
+
+ ss=g_malloc(sizeof(smb2stat_t));
+
+ ss->win = dlg_window_new("smb2-stat"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(ss->win), TRUE);
+ gtk_window_set_default_size(GTK_WINDOW(ss->win), 550, 400);
+ smb2stat_set_title(ss);
+
+ vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(ss->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ label=gtk_label_new("SMB2 Service Response Time statistics");
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ filter_string = g_strdup_printf("Filter: %s", filter ? filter : "");
+ label=gtk_label_new(filter_string);
+ g_free(filter_string);
+ gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ label=gtk_label_new("SMB2 Commands");
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ /* We must display TOP LEVEL Widget before calling init_srt_table() */
+ gtk_widget_show_all(ss->win);
+
+ init_srt_table(&ss->smb2_srt_table, 256, vbox, "smb2.cmd");
+ for(i=0;i<256;i++){
+ init_srt_table_row(&ss->smb2_srt_table, i, val_to_str_ext(i, &smb2_cmd_vals_ext, "Unknown(0x%02x)"));
+ }
+
+
+ error_string=register_tap_listener("smb2", ss, filter, 0, smb2stat_reset, smb2stat_packet, smb2stat_draw);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(ss);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(ss->win, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(ss->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(ss->win, "destroy", G_CALLBACK(win_destroy_cb), ss);
+
+ gtk_widget_show_all(ss->win);
+ window_present(ss->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(ss->win));
+}
+
+static tap_param smb2_stat_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg smb2_stat_dlg = {
+ "SMB2 SRT Statistics",
+ "smb2,srt",
+ gtk_smb2stat_init,
+ -1,
+ G_N_ELEMENTS(smb2_stat_params),
+ smb2_stat_params
+};
+
+void
+register_tap_listener_gtksmb2stat(void)
+{
+ register_dfilter_stat(&smb2_stat_dlg, "SMB2",
+ REGISTER_STAT_GROUP_RESPONSE_TIME);
+}
+void smb2_srt_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &smb2_stat_dlg);
+}
+
diff --git a/ui/gtk/smb_stat.c b/ui/gtk/smb_stat.c
new file mode 100644
index 0000000000..8502448308
--- /dev/null
+++ b/ui/gtk/smb_stat.c
@@ -0,0 +1,265 @@
+/* smb_stat.c
+ * smb_stat 2003 Ronnie Sahlberg
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/value_string.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-smb.h>
+
+#include "../timestats.h"
+#include "../simple_dialog.h"
+#include "../file.h"
+#include "../globals.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/service_response_time_table.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/main.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _smbstat_t {
+ GtkWidget *win;
+ srt_stat_table smb_srt_table;
+ srt_stat_table trans2_srt_table;
+ srt_stat_table nt_trans_srt_table;
+} smbstat_t;
+
+static void
+smbstat_set_title(smbstat_t *ss)
+{
+ char *title;
+
+ title = g_strdup_printf("SMB Service Response Time statistics: %s",
+ cf_get_display_name(&cfile));
+ gtk_window_set_title(GTK_WINDOW(ss->win), title);
+ g_free(title);
+}
+
+static void
+smbstat_reset(void *pss)
+{
+ smbstat_t *ss=(smbstat_t *)pss;
+
+ reset_srt_table_data(&ss->smb_srt_table);
+ reset_srt_table_data(&ss->trans2_srt_table);
+ reset_srt_table_data(&ss->nt_trans_srt_table);
+ smbstat_set_title(ss);
+}
+
+static int
+smbstat_packet(void *pss, packet_info *pinfo, epan_dissect_t *edt _U_, const void *psi)
+{
+ smbstat_t *ss=(smbstat_t *)pss;
+ const smb_info_t *si=psi;
+
+ /* we are only interested in reply packets */
+ if(si->request){
+ return 0;
+ }
+ /* if we havnt seen the request, just ignore it */
+ if(!si->sip){
+ return 0;
+ }
+
+ add_srt_table_data(&ss->smb_srt_table, si->cmd, &si->sip->req_time, pinfo);
+
+ if(si->cmd==0xA0 && si->sip->extra_info_type == SMB_EI_NTI){
+ smb_nt_transact_info_t *sti=(smb_nt_transact_info_t *)si->sip->extra_info;
+
+ if(sti){
+ add_srt_table_data(&ss->nt_trans_srt_table, sti->subcmd, &si->sip->req_time, pinfo);
+ }
+ } else if(si->cmd==0x32 && si->sip->extra_info_type == SMB_EI_T2I){
+ smb_transact2_info_t *st2i=(smb_transact2_info_t *)si->sip->extra_info;
+
+ if(st2i){
+ add_srt_table_data(&ss->trans2_srt_table, st2i->subcmd, &si->sip->req_time, pinfo);
+ }
+ }
+
+ return 1;
+}
+
+
+
+static void
+smbstat_draw(void *pss)
+{
+ smbstat_t *ss=(smbstat_t *)pss;
+
+ draw_srt_table_data(&ss->smb_srt_table);
+ draw_srt_table_data(&ss->trans2_srt_table);
+ draw_srt_table_data(&ss->nt_trans_srt_table);
+}
+
+
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ smbstat_t *ss=(smbstat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(ss);
+ unprotect_thread_critical_region();
+
+ free_srt_table_data(&ss->smb_srt_table);
+ free_srt_table_data(&ss->trans2_srt_table);
+ free_srt_table_data(&ss->nt_trans_srt_table);
+ g_free(ss);
+}
+
+
+static void
+gtk_smbstat_init(const char *optarg, void *userdata _U_)
+{
+ smbstat_t *ss;
+ const char *filter=NULL;
+ GtkWidget *label;
+ char *filter_string;
+ GString *error_string;
+ int i;
+ GtkWidget *vbox;
+ GtkWidget *bbox;
+ GtkWidget *close_bt;
+
+ if(!strncmp(optarg,"smb,srt,",8)){
+ filter=optarg+8;
+ } else {
+ filter=NULL;
+ }
+
+ ss=g_malloc(sizeof(smbstat_t));
+
+ ss->win = dlg_window_new("smb-stat"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(ss->win), TRUE);
+ gtk_window_set_default_size(GTK_WINDOW(ss->win), 550, 600);
+ smbstat_set_title(ss);
+
+ vbox=gtk_vbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(ss->win), vbox);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+
+ label=gtk_label_new("SMB Service Response Time statistics");
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ filter_string = g_strdup_printf("Filter: %s", filter ? filter : "");
+ label=gtk_label_new(filter_string);
+ g_free(filter_string);
+ gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ label=gtk_label_new("SMB Commands");
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+
+ /* We must display TOP LEVEL Widget before calling init_srt_table() */
+ gtk_widget_show_all(ss->win);
+
+ init_srt_table(&ss->smb_srt_table, 256, vbox, "smb.cmd");
+ for(i=0;i<256;i++){
+ init_srt_table_row(&ss->smb_srt_table, i, val_to_str_ext(i, &smb_cmd_vals_ext, "Unknown(0x%02x)"));
+ }
+
+
+ label=gtk_label_new("Transaction2 Sub-Commands");
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->trans2_srt_table, 256, vbox, "smb.trans2.cmd");
+ for(i=0;i<256;i++){
+ init_srt_table_row(&ss->trans2_srt_table, i, val_to_str_ext(i, &trans2_cmd_vals_ext, "Unknown(0x%02x)"));
+ }
+
+
+ label=gtk_label_new("NT Transaction Sub-Commands");
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+ init_srt_table(&ss->nt_trans_srt_table, 256, vbox, "smb.nt.function");
+ for(i=0;i<256;i++){
+ init_srt_table_row(&ss->nt_trans_srt_table, i, val_to_str_ext(i, &nt_cmd_vals_ext, "Unknown(0x%02x)"));
+ }
+
+
+ error_string=register_tap_listener("smb", ss, filter, 0, smbstat_reset, smbstat_packet, smbstat_draw);
+ if(error_string){
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ g_free(ss);
+ return;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(ss->win, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(ss->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(ss->win, "destroy", G_CALLBACK(win_destroy_cb), ss);
+
+ gtk_widget_show_all(ss->win);
+ window_present(ss->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(ss->win));
+}
+
+static tap_param smb_stat_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg smb_stat_dlg = {
+ "SMB SRT Statistics",
+ "smb,srt",
+ gtk_smbstat_init,
+ -1,
+ G_N_ELEMENTS(smb_stat_params),
+ smb_stat_params
+};
+
+void
+register_tap_listener_gtksmbstat(void)
+{
+ register_dfilter_stat(&smb_stat_dlg, "SMB",
+ REGISTER_STAT_GROUP_RESPONSE_TIME);
+}
+void smb_srt_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &smb_stat_dlg);
+}
+
diff --git a/ui/gtk/stats_tree_stat.c b/ui/gtk/stats_tree_stat.c
new file mode 100644
index 0000000000..250db6b87f
--- /dev/null
+++ b/ui/gtk/stats_tree_stat.c
@@ -0,0 +1,402 @@
+/* stats_tree_stat.c
+ * GTK Tap implementation of stats_tree
+ * 2005, Luis E. G. Ontanon
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/stats_tree_priv.h>
+#include <epan/report_err.h>
+
+#include "../simple_dialog.h"
+#include "../globals.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/main.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+struct _st_node_pres {
+ GtkTreeIter* iter;
+};
+
+struct _tree_cfg_pres {
+ tap_param_dlg* stat_dlg;
+};
+
+struct _tree_pres {
+ GString* text;
+ GtkWidget* win;
+ GtkTreeStore* store;
+ GtkWidget* tree;
+};
+
+/* the columns of the tree pane */
+enum _stat_tree_columns {
+ TITLE_COLUMN,
+ COUNT_COLUMN,
+ RATE_COLUMN,
+ PERCENT_COLUMN,
+ N_COLUMNS
+};
+
+/* used for converting numbers */
+#define NUM_BUF_SIZE 32
+
+/* creates the gtk representation for a stat_node
+ * node: the node
+ */
+static void
+setup_gtk_node_pr(stat_node* node)
+{
+ GtkTreeIter* parent = NULL;
+
+ node->pr = g_malloc(sizeof(st_node_pres));
+
+ if (node->st->pr->store) {
+ node->pr->iter = g_malloc0(sizeof(GtkTreeIter));
+
+ if ( node->parent && node->parent->pr ) {
+ parent = node->parent->pr->iter;
+ }
+ gtk_tree_store_append (node->st->pr->store, node->pr->iter, parent);
+ gtk_tree_store_set(node->st->pr->store, node->pr->iter,
+ TITLE_COLUMN, node->name, RATE_COLUMN, "", COUNT_COLUMN, "", -1);
+ }
+}
+
+
+static void
+draw_gtk_node(stat_node* node)
+{
+ static gchar value[NUM_BUF_SIZE];
+ static gchar rate[NUM_BUF_SIZE];
+ static gchar percent[NUM_BUF_SIZE];
+ stat_node* child;
+
+ stats_tree_get_strs_from_node(node, value, rate,
+ percent);
+
+ if (node->st->pr->store && node->pr->iter) {
+ gtk_tree_store_set(node->st->pr->store, node->pr->iter,
+ RATE_COLUMN, rate,
+ COUNT_COLUMN, value,
+ PERCENT_COLUMN, percent,
+ -1);
+ }
+
+ if (node->children) {
+ for (child = node->children; child; child = child->next )
+ draw_gtk_node(child);
+ }
+}
+
+static void
+draw_gtk_tree(void *psp)
+{
+ stats_tree *st = psp;
+ stat_node* child;
+
+ for (child = st->root.children; child; child = child->next ) {
+ draw_gtk_node(child);
+
+ if (child->pr->iter && st->pr->store) {
+ gtk_tree_view_expand_row(GTK_TREE_VIEW(st->pr->tree),
+ gtk_tree_model_get_path(GTK_TREE_MODEL(st->pr->store),
+ child->pr->iter),
+ FALSE);
+ }
+ }
+
+}
+
+static void
+free_gtk_tree(GtkWindow *win _U_, stats_tree *st)
+{
+
+ protect_thread_critical_region();
+ remove_tap_listener(st);
+ unprotect_thread_critical_region();
+
+ if (st->root.pr)
+ st->root.pr->iter = NULL;
+
+ st->cfg->in_use = FALSE;
+ stats_tree_free(st);
+
+}
+
+static void
+clear_node_pr(stat_node* n)
+{
+ stat_node* c;
+ for (c = n->children; c; c = c->next) {
+ clear_node_pr(c);
+ }
+
+ if (n->pr->iter) {
+ gtk_tree_store_remove(n->st->pr->store, n->pr->iter);
+ n->pr->iter = NULL;
+ }
+}
+
+static void
+reset_tap(void* p)
+{
+ stats_tree* st = p;
+ stat_node* c;
+ for (c = st->root.children; c; c = c->next) {
+ clear_node_pr(c);
+ }
+
+ st->cfg->init(st);
+}
+
+/* initializes the stats_tree window */
+static void
+init_gtk_tree(const char* optarg, void *userdata _U_)
+{
+ gchar *abbr = stats_tree_get_abbr(optarg);
+ stats_tree* st = NULL;
+ stats_tree_cfg* cfg = NULL;
+ tree_pres* pr = g_malloc(sizeof(tree_pres));
+ gchar* title = NULL;
+ gchar* window_name = NULL;
+ GString* error_string;
+ GtkWidget *scr_win;
+ size_t init_strlen;
+ GtkWidget *main_vb, *bbox, *bt_close;
+ GtkTreeViewColumn* column;
+ GtkCellRenderer* renderer;
+
+ if (abbr) {
+ cfg = stats_tree_get_cfg_by_abbr(abbr);
+
+ if (cfg && cfg->in_use) {
+ /* XXX: ! */
+ report_failure("cannot open more than one tree of the same type at once");
+ return;
+ }
+
+ if (cfg != NULL) {
+ init_strlen = strlen(cfg->pr->stat_dlg->init_string);
+
+ if (strncmp (optarg, cfg->pr->stat_dlg->init_string, init_strlen) == 0){
+ if (init_strlen == strlen(optarg)) {
+ st = stats_tree_new(cfg,pr,NULL);
+ } else {
+ st = stats_tree_new(cfg,pr,(char*)optarg+init_strlen+1);
+ }
+
+ } else {
+ st = stats_tree_new(cfg,pr,NULL);
+ }
+ } else {
+ report_failure("no such stats_tree (%s) in stats_tree registry",abbr);
+ g_free(abbr);
+ return;
+ }
+ g_free(abbr);
+
+ } else {
+ report_failure("could not obtain stats_tree abbr from optarg");
+ g_free(pr);
+ return;
+ }
+
+ cfg->in_use = TRUE;
+
+ window_name = g_strdup_printf("%s Stats Tree", cfg->name);
+
+ st->pr->win = window_new_with_geom(GTK_WINDOW_TOPLEVEL,window_name,window_name);
+ gtk_window_set_default_size(GTK_WINDOW(st->pr->win), 400, 400);
+ g_free(window_name);
+
+ if(st->filter){
+ title=g_strdup_printf("%s with filter: %s",cfg->name,st->filter);
+ } else {
+ st->filter=NULL;
+ title=g_strdup_printf("%s", cfg->name);
+ }
+
+ gtk_window_set_title(GTK_WINDOW(st->pr->win), title);
+ g_free(title);
+
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 12);
+ gtk_container_add(GTK_CONTAINER(st->pr->win), main_vb);
+
+ scr_win = scrolled_window_new(NULL, NULL);
+
+ st->pr->store = gtk_tree_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING);
+
+ st->pr->tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (st->pr->store));
+ g_object_unref(G_OBJECT(st->pr->store));
+
+ gtk_container_add( GTK_CONTAINER(scr_win), st->pr->tree);
+
+ /* the columns */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Topic / Item", renderer,
+ "text", TITLE_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_resizable (column,TRUE);
+ gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (st->pr->tree), column);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Count", renderer,
+ "text", COUNT_COLUMN,
+ NULL);
+
+ gtk_tree_view_column_set_resizable (column,TRUE);
+ gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (st->pr->tree), column);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Rate (ms)", renderer,
+ "text", RATE_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_resizable (column,TRUE);
+ gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (st->pr->tree), column);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Percent", renderer,
+ "text", PERCENT_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_resizable(column,TRUE);
+ gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (st->pr->tree), column);
+
+ gtk_container_add( GTK_CONTAINER(main_vb), scr_win);
+
+ error_string = register_tap_listener( cfg->tapname,
+ st,
+ st->filter,
+ cfg->flags,
+ reset_tap,
+ stats_tree_packet,
+ draw_gtk_tree);
+
+ if (error_string) {
+ /* error, we failed to attach to the tap. clean up */
+ /* destroy_stat_tree_window(st); */
+ report_failure("stats_tree for: %s failed to attach to the tap: %s",cfg->name,error_string->str);
+ g_string_free(error_string, TRUE);
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+
+ bt_close = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(st->pr->win, bt_close, window_cancel_button_cb);
+
+ g_signal_connect(GTK_WINDOW(st->pr->win), "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(GTK_WINDOW(st->pr->win), "destroy", G_CALLBACK(free_gtk_tree), st);
+
+ gtk_widget_show_all(st->pr->win);
+ window_present(st->pr->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(st->pr->win));
+}
+
+static tap_param tree_stat_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static void
+register_gtk_stats_tree_tap (gpointer k _U_, gpointer v, gpointer p _U_)
+{
+ stats_tree_cfg* cfg = v;
+
+ cfg->pr = g_malloc(sizeof(tree_pres));
+
+ cfg->pr->stat_dlg = g_malloc(sizeof(tap_param_dlg));
+
+ cfg->pr->stat_dlg->win_title = g_strdup_printf("%s Stats Tree",cfg->name);
+ cfg->pr->stat_dlg->init_string = g_strdup_printf("%s,tree",cfg->abbr);
+ cfg->pr->stat_dlg->tap_init_cb = init_gtk_tree;
+ cfg->pr->stat_dlg->index = -1;
+ cfg->pr->stat_dlg->nparams = G_N_ELEMENTS(tree_stat_params);
+ cfg->pr->stat_dlg->params = tree_stat_params;
+}
+
+static void
+free_tree_presentation(stats_tree* st)
+{
+ g_free(st->pr);
+}
+
+void
+register_tap_listener_stats_tree_stat(void)
+{
+
+ stats_tree_presentation(register_gtk_stats_tree_tap,
+ setup_gtk_node_pr,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ free_tree_presentation,
+ NULL,
+ NULL,
+ NULL);
+}
+
+void gtk_stats_tree_cb(GtkAction *action, gpointer user_data _U_)
+{
+ const gchar *action_name;
+ gchar *abbr;
+ stats_tree_cfg* cfg = NULL;
+
+ action_name = gtk_action_get_name (action);
+ abbr = strrchr(action_name,'/');
+ if(abbr){
+ abbr = abbr+1;
+ }else{
+ abbr = g_strdup_printf("%s",action_name);
+ }
+ cfg = stats_tree_get_cfg_by_abbr(abbr);
+ if(cfg){
+ tap_param_dlg_cb(action, cfg->pr->stat_dlg);
+ }else{
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Failed to find the stat tree named %s",
+ abbr);
+ return;
+ }
+
+}
+
diff --git a/ui/gtk/stock_icons.c b/ui/gtk/stock_icons.c
new file mode 100644
index 0000000000..52456dca18
--- /dev/null
+++ b/ui/gtk/stock_icons.c
@@ -0,0 +1,267 @@
+/* stock_icons.c
+ * Wireshark specific stock icons
+ * Copyright 2003-2008, Ulf Lamping <ulf.lamping@web.de>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gtk/gtk.h>
+
+#include "ui/gtk/stock_icons.h"
+
+/* these icons are derived from the original stock icons */
+#include "../image/toolbar/capture_interfaces_24.xpm"
+#include "../image/toolbar/capture_options_24.xpm"
+#include "../image/toolbar/capture_start_24.xpm"
+#include "../image/toolbar/capture_stop_24.xpm"
+#include "../image/toolbar/capture_restart_24.xpm"
+#include "../image/toolbar/capture_filter_24.xpm"
+#include "../image/toolbar/capture_details_24.xpm"
+#include "../image/toolbar/display_filter_24.xpm"
+#include "../image/wsicon16.xpm"
+#include "../image/toolbar/colorize_24.xpm"
+#include "../image/toolbar/autoscroll_24.xpm"
+#include "../image/toolbar/resize_columns_24.xpm"
+#include "../image/toolbar/time_24.xpm"
+#include "../image/toolbar/internet_24.xpm"
+#include "../image/toolbar/web_support_24.xpm"
+#include "../image/toolbar/wiki_24.xpm"
+#include "../image/toolbar/conversations_16.xpm"
+#include "../image/toolbar/endpoints_16.xpm"
+#include "../image/toolbar/expert_info_16.xpm"
+#include "../image/toolbar/flow_graph_16.xpm"
+#include "../image/toolbar/graphs_16.xpm"
+#include "../image/toolbar/telephony_16.xpm"
+#include "../image/toolbar/decode_as_16.xpm"
+#include "../image/toolbar/checkbox_16.xpm"
+#include "../image/toolbar/file_set_list_16.xpm"
+#include "../image/toolbar/file_set_next_16.xpm"
+#include "../image/toolbar/file_set_previous_16.xpm"
+#include "../image/toolbar/icon_color_1.xpm"
+#include "../image/toolbar/icon_color_2.xpm"
+#include "../image/toolbar/icon_color_3.xpm"
+#include "../image/toolbar/icon_color_4.xpm"
+#include "../image/toolbar/icon_color_5.xpm"
+#include "../image/toolbar/icon_color_6.xpm"
+#include "../image/toolbar/icon_color_7.xpm"
+#include "../image/toolbar/icon_color_8.xpm"
+#include "../image/toolbar/icon_color_9.xpm"
+#include "../image/toolbar/icon_color_0.xpm"
+#include "../image/toolbar/decode_24.xpm"
+#include "../image/toolbar/audio_player_24.xpm"
+#include "../image/toolbar/voip_flow_24.xpm"
+#include "../image/toolbar/telephone_16.xpm"
+#include "../image/toolbar/analyze_24.xpm"
+#include "../image/WiresharkDoc_24.xpm"
+
+typedef struct stock_pixmap_tag{
+ const char * name;
+ const char ** xpm_data;
+} stock_pixmap_t;
+
+/* generate application specific stock items */
+void stock_icons_init(void) {
+ GtkIconFactory * factory;
+ gint32 i;
+ GdkPixbuf * pixbuf;
+ GtkIconSet *icon_set;
+
+
+ /* register non-standard pixmaps with the gtk-stock engine */
+ static const GtkStockItem stock_items[] = {
+ { WIRESHARK_STOCK_CAPTURE_INTERFACES, "_Interfaces", 0, 0, NULL },
+ { WIRESHARK_STOCK_CAPTURE_OPTIONS, "_Options", 0, 0, NULL },
+ { WIRESHARK_STOCK_CAPTURE_START, "_Start", 0, 0, NULL },
+ { WIRESHARK_STOCK_CAPTURE_STOP, "S_top", 0, 0, NULL },
+ { WIRESHARK_STOCK_CAPTURE_RESTART, "_Restart", 0, 0, NULL },
+ { WIRESHARK_STOCK_CAPTURE_FILTER, "_Capture Filter", 0, 0, NULL },
+ { WIRESHARK_STOCK_CAPTURE_FILTER_ENTRY, "_Capture Filter:", 0, 0, NULL },
+ { WIRESHARK_STOCK_CAPTURE_DETAILS, "_Details", 0, 0, NULL },
+#ifdef HAVE_GEOIP
+ { WIRESHARK_STOCK_MAP, "Map", 0, 0, NULL },
+#endif
+ { WIRESHARK_STOCK_FOLLOW_STREAM, "Follow Stream", 0, 0, NULL },
+ { WIRESHARK_STOCK_DISPLAY_FILTER, "Display _Filter", 0, 0, NULL },
+ { WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY, "F_ilter:", 0, 0, NULL },
+ { WIRESHARK_STOCK_BROWSE, "_Browse...", 0, 0, NULL },
+ { WIRESHARK_STOCK_CREATE_STAT, "Create _Stat", 0, 0, NULL },
+ { WIRESHARK_STOCK_EXPORT, "_Export...", 0, 0, NULL },
+ { WIRESHARK_STOCK_IMPORT, "_Import...", 0, 0, NULL },
+ { WIRESHARK_STOCK_EDIT, "_Edit...", 0, 0, NULL },
+ { WIRESHARK_STOCK_ADD_EXPRESSION, "E_xpression..." , 0, 0, NULL }, /* plus sign coming from icon */
+ { WIRESHARK_STOCK_CLEAR_EXPRESSION, "Clea_r" , 0, 0, NULL },
+ { WIRESHARK_STOCK_APPLY_EXPRESSION, "App_ly" , 0, 0, NULL },
+ { WIRESHARK_STOCK_SAVE_ALL, "Save A_ll", 0, 0, NULL },
+ { WIRESHARK_STOCK_DONT_SAVE, "Continue _without Saving", 0, 0, NULL },
+ { WIRESHARK_STOCK_QUIT_DONT_SAVE, "Quit _without Saving", 0, 0, NULL },
+ { WIRESHARK_STOCK_ABOUT, "_About", 0, 0, NULL },
+ { WIRESHARK_STOCK_COLORIZE, "_Colorize", 0, 0, NULL },
+ { WIRESHARK_STOCK_AUTOSCROLL, "_Auto Scroll", 0, 0, NULL },
+ { WIRESHARK_STOCK_RESIZE_COLUMNS, "Resize Columns", 0, 0, NULL },
+ { WIRESHARK_STOCK_TIME, "Time", 0, 0, NULL },
+ { WIRESHARK_STOCK_INTERNET, "Internet", 0, 0, NULL },
+ { WIRESHARK_STOCK_WEB_SUPPORT, "Web Support", 0, 0, NULL },
+ { WIRESHARK_STOCK_WIKI, "Wiki", 0, 0, NULL },
+ { WIRESHARK_STOCK_CONVERSATIONS, "Conversations", 0, 0, NULL },
+ { WIRESHARK_STOCK_ENDPOINTS, "Endpoints", 0, 0, NULL },
+ { WIRESHARK_STOCK_EXPERT_INFO, "Expert Info", 0, 0, NULL },
+ { WIRESHARK_STOCK_GRAPHS, "Graphs", 0, 0, NULL },
+ { WIRESHARK_STOCK_FLOW_GRAPH, "Flow Graph", 0, 0, NULL },
+ { WIRESHARK_STOCK_TELEPHONY, "Telephony", 0, 0, NULL },
+ { WIRESHARK_STOCK_DECODE_AS, "Decode As", 0, 0, NULL },
+ { WIRESHARK_STOCK_CHECKBOX, "Checkbox", 0, 0, NULL },
+ { WIRESHARK_STOCK_FILE_SET_LIST, "List Files", 0, 0, NULL },
+ { WIRESHARK_STOCK_FILE_SET_NEXT, "Next File", 0, 0, NULL },
+ { WIRESHARK_STOCK_FILE_SET_PREVIOUS, "Previous File", 0, 0, NULL },
+ { WIRESHARK_STOCK_FILTER_OUT_STREAM, "Filter Out This Stream", 0, 0, NULL },
+ { WIRESHARK_STOCK_ENABLE, "Enable", 0, 0, NULL },
+ { WIRESHARK_STOCK_DISABLE, "Disable", 0, 0, NULL },
+ { WIRESHARK_STOCK_COLOR1, "Color 1", 0, 0, NULL },
+ { WIRESHARK_STOCK_COLOR2, "Color 2", 0, 0, NULL },
+ { WIRESHARK_STOCK_COLOR3, "Color 3", 0, 0, NULL },
+ { WIRESHARK_STOCK_COLOR4, "Color 4", 0, 0, NULL },
+ { WIRESHARK_STOCK_COLOR5, "Color 5", 0, 0, NULL },
+ { WIRESHARK_STOCK_COLOR6, "Color 6", 0, 0, NULL },
+ { WIRESHARK_STOCK_COLOR7, "Color 7", 0, 0, NULL },
+ { WIRESHARK_STOCK_COLOR8, "Color 8", 0, 0, NULL },
+ { WIRESHARK_STOCK_COLOR9, "Color 9", 0, 0, NULL },
+ { WIRESHARK_STOCK_COLOR0, "Color 10", 0, 0, NULL },
+ { WIRESHARK_STOCK_DECODE, "Decode", 0, 0, NULL },
+ { WIRESHARK_STOCK_AUDIO_PLAYER, "Player", 0, 0, NULL },
+ { WIRESHARK_STOCK_VOIP_FLOW, "Flow", 0, 0, NULL },
+ { WIRESHARK_STOCK_TELEPHONE, "Telephone", 0, 0, NULL },
+ { WIRESHARK_STOCK_PREPARE_FILTER, "Prepare Filter", 0, 0, NULL },
+ { WIRESHARK_STOCK_ANALYZE, "Analyze", 0, 0, NULL },
+ { WIRESHARK_STOCK_FILE, "File", 0, 0, NULL }
+ };
+
+ static const stock_pixmap_t pixmaps[] = {
+ { WIRESHARK_STOCK_CAPTURE_INTERFACES, capture_interfaces_24_xpm },
+ { WIRESHARK_STOCK_CAPTURE_OPTIONS, capture_options_24_xpm },
+ { WIRESHARK_STOCK_CAPTURE_START, capture_start_24_xpm },
+ { WIRESHARK_STOCK_CAPTURE_STOP, capture_stop_24_xpm },
+ { WIRESHARK_STOCK_CAPTURE_RESTART, capture_restart_24_xpm },
+ { WIRESHARK_STOCK_CAPTURE_FILTER, capture_filter_24_xpm },
+ { WIRESHARK_STOCK_CAPTURE_FILTER_ENTRY, capture_filter_24_xpm },
+ { WIRESHARK_STOCK_CAPTURE_DETAILS, capture_details_24_xpm },
+#ifdef HAVE_GEOIP
+ { WIRESHARK_STOCK_MAP, internet_24_xpm},
+#endif
+ { WIRESHARK_STOCK_DISPLAY_FILTER, display_filter_24_xpm },
+ { WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY, display_filter_24_xpm },
+ { WIRESHARK_STOCK_ABOUT, wsicon16_xpm },
+ { WIRESHARK_STOCK_COLORIZE, colorize_24_xpm },
+ { WIRESHARK_STOCK_AUTOSCROLL, autoscroll_24_xpm },
+ { WIRESHARK_STOCK_RESIZE_COLUMNS, resize_columns_24_xpm},
+ { WIRESHARK_STOCK_TIME, time_24_xpm},
+ { WIRESHARK_STOCK_INTERNET, internet_24_xpm},
+ { WIRESHARK_STOCK_WEB_SUPPORT, web_support_24_xpm},
+ { WIRESHARK_STOCK_WIKI, wiki_24_xpm},
+ { WIRESHARK_STOCK_CONVERSATIONS, conversations_16_xpm},
+ { WIRESHARK_STOCK_ENDPOINTS, endpoints_16_xpm},
+ { WIRESHARK_STOCK_EXPERT_INFO, expert_info_16_xpm},
+ { WIRESHARK_STOCK_GRAPHS, graphs_16_xpm},
+ { WIRESHARK_STOCK_FLOW_GRAPH, flow_graph_16_xpm},
+ { WIRESHARK_STOCK_TELEPHONY, telephony_16_xpm},
+ { WIRESHARK_STOCK_DECODE_AS, decode_as_16_xpm},
+ { WIRESHARK_STOCK_CHECKBOX, checkbox_16_xpm},
+ { WIRESHARK_STOCK_FILE_SET_LIST, file_set_list_16_xpm},
+ { WIRESHARK_STOCK_FILE_SET_NEXT, file_set_next_16_xpm},
+ { WIRESHARK_STOCK_FILE_SET_PREVIOUS, file_set_previous_16_xpm},
+ { WIRESHARK_STOCK_FILTER_OUT_STREAM, display_filter_24_xpm},
+ { WIRESHARK_STOCK_ENABLE, checkbox_16_xpm},
+ { WIRESHARK_STOCK_COLOR1, icon_color_1_xpm},
+ { WIRESHARK_STOCK_COLOR2, icon_color_2_xpm},
+ { WIRESHARK_STOCK_COLOR3, icon_color_3_xpm},
+ { WIRESHARK_STOCK_COLOR4, icon_color_4_xpm},
+ { WIRESHARK_STOCK_COLOR5, icon_color_5_xpm},
+ { WIRESHARK_STOCK_COLOR6, icon_color_6_xpm},
+ { WIRESHARK_STOCK_COLOR7, icon_color_7_xpm},
+ { WIRESHARK_STOCK_COLOR8, icon_color_8_xpm},
+ { WIRESHARK_STOCK_COLOR9, icon_color_9_xpm},
+ { WIRESHARK_STOCK_COLOR0, icon_color_0_xpm},
+ { WIRESHARK_STOCK_DECODE, decode_24_xpm},
+ { WIRESHARK_STOCK_AUDIO_PLAYER, audio_player_24_xpm},
+ { WIRESHARK_STOCK_VOIP_FLOW, voip_flow_24_xpm},
+ { WIRESHARK_STOCK_TELEPHONE, telephone_16_xpm},
+ { WIRESHARK_STOCK_PREPARE_FILTER, display_filter_24_xpm},
+ { WIRESHARK_STOCK_ANALYZE, analyze_24_xpm},
+ { WIRESHARK_STOCK_FILE, WiresharkDoc_24_xpm},
+ { NULL, NULL }
+ };
+
+ /* Register our stock items */
+ gtk_stock_add (stock_items, G_N_ELEMENTS (stock_items));
+
+ /* Add our custom icon factory to the list of defaults */
+ factory = gtk_icon_factory_new();
+ gtk_icon_factory_add_default(factory);
+
+ /* Create the stock items to add into our icon factory */
+ for (i = 0; pixmaps[i].name != NULL; i++) {
+ /* The default icon */
+ pixbuf = gdk_pixbuf_new_from_xpm_data((const char **) (pixmaps[i].xpm_data));
+ g_assert(pixbuf);
+ icon_set = gtk_icon_set_new_from_pixbuf (pixbuf);
+
+ /* XXX - add different sized icons here (some 16*16 icons look a bit blurred) */
+ /*gtk_icon_set_add_source(icon_set, const GtkIconSource *source);*/
+
+ gtk_icon_factory_add (factory, pixmaps[i].name, icon_set);
+ gtk_icon_set_unref (icon_set);
+ g_object_unref (G_OBJECT (pixbuf));
+ }
+
+ /* use default stock icons for Wireshark specifics where the icon metapher makes sense */
+ /* PLEASE DON'T REUSE STOCK ICONS IF THEY ARE USUALLY USED FOR SOME DIFFERENT MEANING!!!) */
+ icon_set = gtk_icon_factory_lookup_default(GTK_STOCK_OPEN);
+ gtk_icon_factory_add(factory, WIRESHARK_STOCK_BROWSE, icon_set);
+ icon_set = gtk_icon_factory_lookup_default(GTK_STOCK_OK);
+ gtk_icon_factory_add(factory, WIRESHARK_STOCK_CREATE_STAT, icon_set);
+ icon_set = gtk_icon_factory_lookup_default(GTK_STOCK_SAVE);
+ gtk_icon_factory_add(factory, WIRESHARK_STOCK_EXPORT, icon_set); /* XXX: needs a better icon */
+ icon_set = gtk_icon_factory_lookup_default(GTK_STOCK_OPEN);
+ gtk_icon_factory_add(factory, WIRESHARK_STOCK_IMPORT, icon_set); /* XXX: needs a better icon */
+ icon_set = gtk_icon_factory_lookup_default(GTK_STOCK_PROPERTIES);
+ gtk_icon_factory_add(factory, WIRESHARK_STOCK_EDIT, icon_set);
+ icon_set = gtk_icon_factory_lookup_default(GTK_STOCK_ADD);
+ gtk_icon_factory_add(factory, WIRESHARK_STOCK_ADD_EXPRESSION, icon_set);
+ icon_set = gtk_icon_factory_lookup_default(GTK_STOCK_CLEAR);
+ gtk_icon_factory_add(factory, WIRESHARK_STOCK_CLEAR_EXPRESSION, icon_set);
+ icon_set = gtk_icon_factory_lookup_default(GTK_STOCK_APPLY);
+ gtk_icon_factory_add(factory, WIRESHARK_STOCK_APPLY_EXPRESSION, icon_set);
+ icon_set = gtk_icon_factory_lookup_default(GTK_STOCK_CLEAR);
+ gtk_icon_factory_add(factory, WIRESHARK_STOCK_DONT_SAVE, icon_set);
+ gtk_icon_factory_add(factory, WIRESHARK_STOCK_QUIT_DONT_SAVE, icon_set);
+ icon_set = gtk_icon_factory_lookup_default(GTK_STOCK_SAVE);
+ gtk_icon_factory_add(factory, WIRESHARK_STOCK_SAVE_ALL, icon_set); /* XXX: needs a better icon */
+ icon_set = gtk_icon_factory_lookup_default(GTK_STOCK_CLOSE);
+ gtk_icon_factory_add(factory, WIRESHARK_STOCK_DISABLE, icon_set);
+
+ /* Drop our reference to the factory, GTK will hold a reference.*/
+ g_object_unref (G_OBJECT (factory));
+}
+
+
diff --git a/ui/gtk/stock_icons.h b/ui/gtk/stock_icons.h
new file mode 100644
index 0000000000..3158a1732a
--- /dev/null
+++ b/ui/gtk/stock_icons.h
@@ -0,0 +1,97 @@
+/* stock_icons.h
+ * Wireshark specific stock icons
+ * Copyright 2003-2008, Ulf Lamping <ulf.lamping@web.de>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __STOCK_ICONS_H__
+#define __STOCK_ICONS_H__
+
+
+#define WIRESHARK_STOCK_CAPTURE_INTERFACES "Wireshark_Stock_CaptureInterfaces"
+#define WIRESHARK_STOCK_CAPTURE_OPTIONS "Wireshark_Stock_CaptureOptionss"
+#define WIRESHARK_STOCK_CAPTURE_START "Wireshark_Stock_CaptureStart"
+#define WIRESHARK_STOCK_CAPTURE_STOP "Wireshark_Stock_CaptureStop"
+#define WIRESHARK_STOCK_CAPTURE_RESTART "Wireshark_Stock_CaptureRestart"
+#define WIRESHARK_STOCK_CAPTURE_FILTER "Wireshark_Stock_CaptureFilter"
+#define WIRESHARK_STOCK_CAPTURE_FILTER_ENTRY "Wireshark_Stock_CaptureFilter_Entry"
+#define WIRESHARK_STOCK_CAPTURE_DETAILS "Wireshark_Stock_CaptureDetails"
+#ifdef HAVE_GEOIP
+#define WIRESHARK_STOCK_MAP "Wireshark_Stock_Map"
+#endif
+#define WIRESHARK_STOCK_FOLLOW_STREAM "Wireshark_Stock_FollowStream"
+#define WIRESHARK_STOCK_DISPLAY_FILTER "Wireshark_Stock_DisplayFilter"
+#define WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY "Wireshark_Stock_DisplayFilter_Entry"
+#define WIRESHARK_STOCK_BROWSE "Wireshark_Stock_Browse"
+#define WIRESHARK_STOCK_CREATE_STAT "Wireshark_Stock_CreateStat"
+#define WIRESHARK_STOCK_EXPORT "Wireshark_Stock_Export"
+#define WIRESHARK_STOCK_IMPORT "Wireshark_Stock_Import"
+#define WIRESHARK_STOCK_EDIT "Wireshark_Stock_Edit"
+#define WIRESHARK_STOCK_ADD_EXPRESSION "Wireshark_Stock_Edit_Add_Expression"
+#define WIRESHARK_STOCK_CLEAR_EXPRESSION "Wireshark_Stock_Clear_Expression"
+#define WIRESHARK_STOCK_APPLY_EXPRESSION "Wireshark_Stock_Apply_Expression"
+#define WIRESHARK_STOCK_DONT_SAVE "Wireshark_Stock_Continue_without_Saving"
+#define WIRESHARK_STOCK_QUIT_DONT_SAVE "Wireshark_Stock_Quit_without_Saving"
+#define WIRESHARK_STOCK_SAVE_ALL "Wireshark_Stock_Save_All"
+#define WIRESHARK_STOCK_ABOUT "Wireshark_Stock_About"
+#define WIRESHARK_STOCK_COLORIZE "Wireshark_Stock_Colorize"
+#define WIRESHARK_STOCK_AUTOSCROLL "Wireshark_Stock_Autoscroll"
+#define WIRESHARK_STOCK_RESIZE_COLUMNS "Wireshark_Stock_Resize_Columns"
+#define WIRESHARK_STOCK_TIME "Wireshark_Stock_Time"
+#define WIRESHARK_STOCK_INTERNET "Wireshark_Stock_Internet"
+#define WIRESHARK_STOCK_WEB_SUPPORT "Wireshark_Stock_Web_Support"
+#define WIRESHARK_STOCK_WIKI "Wireshark_Stock_Wiki"
+#define WIRESHARK_STOCK_CONVERSATIONS "Wireshark_Stock_Conversations"
+#define WIRESHARK_STOCK_ENDPOINTS "Wireshark_Stock_Endpoints"
+#define WIRESHARK_STOCK_EXPERT_INFO "Wireshark_Stock_Expert_Info"
+#define WIRESHARK_STOCK_GRAPHS "Wireshark_Stock_Graphs"
+#define WIRESHARK_STOCK_FLOW_GRAPH "Wireshark_Stock_Flow_Graph"
+#define WIRESHARK_STOCK_TELEPHONY "Wireshark_Stock_Telephony"
+#define WIRESHARK_STOCK_DECODE_AS "Wireshark_Stock_DecodeAs"
+#define WIRESHARK_STOCK_CHECKBOX "Wireshark_Stock_Checkbox"
+#define WIRESHARK_STOCK_FILE_SET_LIST "Wireshark_Stock_File_Set_List"
+#define WIRESHARK_STOCK_FILE_SET_NEXT "Wireshark_Stock_File_Set_Next"
+#define WIRESHARK_STOCK_FILE_SET_PREVIOUS "Wireshark_Stock_File_Set_Previous"
+#define WIRESHARK_STOCK_FILTER_OUT_STREAM "Wireshark_Stock_Filter_Out_This_Stream"
+#define WIRESHARK_STOCK_ENABLE "Wireshark_Stock_Enable"
+#define WIRESHARK_STOCK_DISABLE "Wireshark_Stock_Disable"
+#define WIRESHARK_STOCK_COLOR1 "Wireshark_Stock_Color_1"
+#define WIRESHARK_STOCK_COLOR2 "Wireshark_Stock_Color_2"
+#define WIRESHARK_STOCK_COLOR3 "Wireshark_Stock_Color_3"
+#define WIRESHARK_STOCK_COLOR4 "Wireshark_Stock_Color_4"
+#define WIRESHARK_STOCK_COLOR5 "Wireshark_Stock_Color_5"
+#define WIRESHARK_STOCK_COLOR6 "Wireshark_Stock_Color_6"
+#define WIRESHARK_STOCK_COLOR7 "Wireshark_Stock_Color_7"
+#define WIRESHARK_STOCK_COLOR8 "Wireshark_Stock_Color_8"
+#define WIRESHARK_STOCK_COLOR9 "Wireshark_Stock_Color_9"
+#define WIRESHARK_STOCK_COLOR0 "Wireshark_Stock_Color_10"
+#define WIRESHARK_STOCK_DECODE "Wireshark_Stock_Decode"
+#define WIRESHARK_STOCK_AUDIO_PLAYER "Wireshark_Audio_Player"
+#define WIRESHARK_STOCK_VOIP_FLOW "Wireshark_Voip_Flow"
+#define WIRESHARK_STOCK_TELEPHONE "Wireshark_Telephone"
+#define WIRESHARK_STOCK_PREPARE_FILTER "Wireshark_Prepare_Filter"
+#define WIRESHARK_STOCK_ANALYZE "Wireshark_Analyze"
+#define WIRESHARK_STOCK_FILE "Wireshark_File"
+
+void stock_icons_init(void);
+
+#endif /* __STOCK_ICONS_H__ */
diff --git a/ui/gtk/summary_dlg.c b/ui/gtk/summary_dlg.c
new file mode 100644
index 0000000000..12a312053e
--- /dev/null
+++ b/ui/gtk/summary_dlg.c
@@ -0,0 +1,506 @@
+/* summary_dlg.c
+ * Routines for capture file summary window
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <string.h>
+#include <time.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/strutil.h>
+
+#include <wiretap/wtap.h>
+
+#include "../globals.h"
+#include "../file.h"
+#include "../summary.h"
+#include "../capture-pcap-util.h"
+#ifdef HAVE_LIBPCAP
+#include "../capture.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/capture_globals.h"
+#endif
+
+#include "ui/gtk/summary_dlg.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/help_dlg.h"
+
+#define SUM_STR_MAX 1024
+#define FILTER_SNIP_LEN 50
+
+
+static void
+add_string_to_table_sensitive(GtkWidget *list, guint *row, const gchar *title, const gchar *value, gboolean sensitive)
+{
+ GtkWidget *label;
+ gchar *indent;
+
+ if(strlen(value) != 0) {
+ indent = g_strdup_printf(" %s", title);
+ } else {
+ indent = g_strdup(title);
+ }
+ label = gtk_label_new(indent);
+ if (strlen(value) == 0) {
+ gchar *message = g_strdup_printf("<span weight=\"bold\">%s</span>", title);
+ gtk_label_set_markup(GTK_LABEL(label), message);
+ g_free (message);
+ }
+ g_free(indent);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
+ gtk_widget_set_sensitive(label, sensitive);
+ gtk_table_attach_defaults(GTK_TABLE(list), label, 0, 1, *row, *row+1);
+
+ label = gtk_label_new(value);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.5f);
+ gtk_widget_set_sensitive(label, sensitive);
+ gtk_table_attach_defaults(GTK_TABLE(list), label, 1, 2, *row, *row+1);
+
+ *row = *row + 1;
+}
+
+static void
+add_string_to_table(GtkWidget *list, guint *row, gchar *title, gchar *value)
+{
+ add_string_to_table_sensitive(list, row, title, value, TRUE);
+}
+
+
+static void
+add_string_to_list(GtkWidget *list, gchar *title, gchar *captured, gchar *displayed, gchar *marked)
+{
+ simple_list_append(list, 0, title, 1, captured, 2, displayed, 3, marked, -1);
+}
+
+static void
+time_to_string(char *string_buff, gulong string_buff_size, time_t ti_time)
+{
+ struct tm *ti_tm;
+
+#ifdef _MSC_VER
+ /* calling localtime() on MSVC 2005 with huge values causes it to crash */
+ /* XXX - find the exact value that still does work */
+ /* XXX - using _USE_32BIT_TIME_T might be another way to circumvent this problem */
+ if (ti_time > 2000000000) {
+ ti_tm = NULL;
+ } else
+#endif
+ ti_tm = localtime(&ti_time);
+ if (ti_tm == NULL) {
+ g_snprintf(string_buff, string_buff_size, "Not representable");
+ return;
+ }
+ g_snprintf(string_buff, string_buff_size,
+ "%04d-%02d-%02d %02d:%02d:%02d",
+ ti_tm->tm_year + 1900,
+ ti_tm->tm_mon + 1,
+ ti_tm->tm_mday,
+ ti_tm->tm_hour,
+ ti_tm->tm_min,
+ ti_tm->tm_sec);
+}
+
+void
+summary_open_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ summary_tally summary;
+ GtkWidget *sum_open_w,
+ *main_vb, *bbox, *close_bt, *help_bt;
+ GtkWidget *table, *scrolled_window;
+ GtkWidget *list, *treeview;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+#ifdef HAVE_LIBPCAP
+ const char *dl_description;
+#endif
+ static const char *titles[] = { "Traffic", "Captured", "Displayed", "Marked" };
+
+ gchar string_buff[SUM_STR_MAX];
+ gchar string_buff2[SUM_STR_MAX];
+ gchar string_buff3[SUM_STR_MAX];
+ gchar string_buff4[SUM_STR_MAX];
+ gchar string_buff5[SUM_STR_MAX];
+
+ double seconds;
+ double disp_seconds;
+ double marked_seconds;
+ guint offset;
+ guint snip;
+ guint row;
+ gchar *str_dup;
+ gchar *str_work;
+
+ unsigned int elapsed_time;
+ iface_options iface;
+ unsigned int i;
+
+ /* initial computations */
+ summary_fill_in(&cfile, &summary);
+#ifdef HAVE_LIBPCAP
+ summary_fill_in_capture(&global_capture_opts, &summary);
+#endif
+ seconds = summary.stop_time - summary.start_time;
+ disp_seconds = summary.filtered_stop - summary.filtered_start;
+ marked_seconds = summary.marked_stop - summary.marked_start;
+
+ sum_open_w = window_new(GTK_WINDOW_TOPLEVEL, "Wireshark: Summary");
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 12);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 12);
+ gtk_container_add(GTK_CONTAINER(sum_open_w), main_vb);
+
+ /* table */
+ table = gtk_table_new(1, 2, FALSE);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 6);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 3);
+ gtk_container_add(GTK_CONTAINER(main_vb), table);
+ row = 0;
+
+
+ /* File */
+ add_string_to_table(table, &row, "File", "");
+
+ /* filename */
+ g_snprintf(string_buff, SUM_STR_MAX, "%s", summary.filename);
+ add_string_to_table(table, &row, "Name:", string_buff);
+
+ /* length */
+ g_snprintf(string_buff, SUM_STR_MAX, "%" G_GINT64_MODIFIER "d bytes", summary.file_length);
+ add_string_to_table(table, &row, "Length:", string_buff);
+
+ /* format */
+ g_snprintf(string_buff, SUM_STR_MAX, "%s", wtap_file_type_string(summary.file_type));
+ add_string_to_table(table, &row, "Format:", string_buff);
+
+ /* encapsulation */
+ g_snprintf(string_buff, SUM_STR_MAX, "%s", wtap_encap_string(summary.encap_type));
+ add_string_to_table(table, &row, "Encapsulation:", string_buff);
+
+ if (summary.has_snap) {
+ /* snapshot length */
+ g_snprintf(string_buff, SUM_STR_MAX, "%u bytes", summary.snap);
+ add_string_to_table(table, &row, "Packet size limit:", string_buff);
+ }
+
+
+ /* Time */
+ add_string_to_table(table, &row, "", "");
+ add_string_to_table(table, &row, "Time", "");
+
+ /* start time */
+ time_to_string(string_buff, SUM_STR_MAX, (time_t)summary.start_time);
+ add_string_to_table(table, &row, "First packet:", string_buff);
+
+ /* stop time */
+ time_to_string(string_buff, SUM_STR_MAX, (time_t)summary.stop_time);
+ add_string_to_table(table, &row, "Last packet:", string_buff);
+
+ /* elapsed seconds */
+ elapsed_time = (unsigned int)summary.elapsed_time;
+ if(elapsed_time/86400) {
+ g_snprintf(string_buff, SUM_STR_MAX, "%02u days %02u:%02u:%02u",
+ elapsed_time/86400, elapsed_time%86400/3600, elapsed_time%3600/60, elapsed_time%60);
+ } else {
+ g_snprintf(string_buff, SUM_STR_MAX, "%02u:%02u:%02u",
+ elapsed_time%86400/3600, elapsed_time%3600/60, elapsed_time%60);
+ }
+ add_string_to_table(table, &row, "Elapsed:", string_buff);
+
+
+ /* Capture */
+ add_string_to_table(table, &row, "", "");
+ add_string_to_table_sensitive(table, &row, "Capture", "", (summary.ifaces->len > 0));
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_container_set_border_width (GTK_CONTAINER (scrolled_window), 5);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_widget_set_size_request(scrolled_window, -1, 120);
+
+ treeview = gtk_tree_view_new();
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Interface", renderer, "text", 0, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Dropped Packets", renderer, "text", 1, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Capture Filter", renderer, "text", 2, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Link type", renderer, "text", 3, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Packet size limit", renderer, "text", 4, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
+
+ store = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+ for (i = 0; i < summary.ifaces->len; i++) {
+ iface = g_array_index(summary.ifaces, iface_options, i);
+ /* interface */
+ if (iface.descr) {
+ g_snprintf(string_buff, SUM_STR_MAX, "%s", iface.descr);
+ } else if (iface.name) {
+ g_snprintf(string_buff, SUM_STR_MAX, "%s", iface.name);
+ } else {
+ g_snprintf(string_buff, SUM_STR_MAX, "unknown");
+ }
+ /* Dropped count */
+ if (iface.drops_known) {
+ g_snprintf(string_buff2, SUM_STR_MAX, "%" G_GINT64_MODIFIER "u", iface.drops);
+ } else {
+ g_snprintf(string_buff2, SUM_STR_MAX, "unknown");
+ }
+#ifdef HAVE_LIBPCAP
+ /* Capture filter */
+ if (iface.cfilter && iface.cfilter[0] != '\0') {
+ g_snprintf(string_buff3, SUM_STR_MAX, "%s", iface.cfilter);
+ } else {
+ if (iface.name) {
+ g_snprintf(string_buff3, SUM_STR_MAX, "none");
+ } else {
+ g_snprintf(string_buff3, SUM_STR_MAX, "unknown");
+ }
+ }
+ dl_description = pcap_datalink_val_to_description(iface.linktype);
+ if (dl_description != NULL)
+ g_snprintf(string_buff4, SUM_STR_MAX, "%s", dl_description);
+ else
+ g_snprintf(string_buff4, SUM_STR_MAX, "DLT %d", iface.linktype);
+#else
+ g_snprintf(string_buff3, SUM_STR_MAX, "unknown");
+ g_snprintf(string_buff4, SUM_STR_MAX, "unknown");
+#endif
+ if (iface.has_snap) {
+ g_snprintf(string_buff5, SUM_STR_MAX, "%u bytes", iface.snap);
+ } else {
+ g_snprintf(string_buff5, SUM_STR_MAX, "unknown");
+ }
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, 0, string_buff, 1, string_buff2, 2, string_buff3, 3, string_buff4, 4, string_buff5,-1);
+ }
+ gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), GTK_TREE_MODEL(store));
+ g_object_unref (store);
+ gtk_container_add(GTK_CONTAINER(scrolled_window), treeview);
+ gtk_container_add(GTK_CONTAINER(main_vb),scrolled_window);
+ gtk_widget_show_all (scrolled_window);
+ table = gtk_table_new(1, 2, FALSE);
+ gtk_table_set_col_spacings(GTK_TABLE(table), 6);
+ gtk_table_set_row_spacings(GTK_TABLE(table), 3);
+ gtk_container_add(GTK_CONTAINER(main_vb), table);
+ row = 0;
+
+
+ /* Data */
+ add_string_to_table(table, &row, "", "");
+ add_string_to_table(table, &row, "Display", "");
+
+ if (summary.dfilter) {
+ /* Display filter */
+ /* limit each row to some reasonable length */
+ str_dup = g_strdup_printf("%s", summary.dfilter);
+ str_work = g_strdup(str_dup);
+ offset = 0;
+ snip = 0;
+ while(strlen(str_work) > FILTER_SNIP_LEN) {
+ str_work[FILTER_SNIP_LEN] = '\0';
+ add_string_to_table(table, &row, (snip == 0) ? "Display filter:" : "", str_work);
+ g_free(str_work);
+ offset+=FILTER_SNIP_LEN;
+ str_work = g_strdup(&str_dup[offset]);
+ snip++;
+ }
+
+ add_string_to_table(table, &row, (snip == 0) ? "Display filter:" : "", str_work);
+ g_free(str_work);
+ g_free(str_dup);
+ } else {
+ /* Display filter */
+ add_string_to_table(table, &row, "Display filter:", "none");
+ }
+
+ /* Ignored packet count */
+ g_snprintf(string_buff, SUM_STR_MAX, "%i", summary.ignored_count);
+ add_string_to_table(table, &row, "Ignored packets:", string_buff);
+
+ /* Traffic */
+ list = simple_list_new(4, titles);
+ gtk_container_add(GTK_CONTAINER(main_vb), list);
+
+ /* Packet count */
+ g_snprintf(string_buff, SUM_STR_MAX, "%i", summary.packet_count);
+ if (summary.dfilter) {
+ g_snprintf(string_buff2, SUM_STR_MAX, "%i", summary.filtered_count);
+ } else {
+ g_strlcpy(string_buff2, string_buff, SUM_STR_MAX);
+ }
+ g_snprintf(string_buff3, SUM_STR_MAX, "%i", summary.marked_count);
+ add_string_to_list(list, "Packets", string_buff, string_buff2, string_buff3);
+
+ /* Time between first and last */
+ if (seconds > 0) {
+ g_snprintf(string_buff, SUM_STR_MAX, "%.3f sec", seconds);
+ } else {
+ string_buff[0] = '\0';
+ }
+ if (summary.dfilter && disp_seconds > 0) {
+ g_snprintf(string_buff2, SUM_STR_MAX, "%.3f sec", disp_seconds);
+ } else {
+ string_buff2[0] = '\0';
+ }
+ if (summary.marked_count && marked_seconds > 0) {
+ g_snprintf(string_buff3, SUM_STR_MAX, "%.3f sec", marked_seconds);
+ } else {
+ string_buff3[0] = '\0';
+ }
+ add_string_to_list(list, "Between first and last packet", string_buff, string_buff2, string_buff3);
+
+ /* Packets per second */
+ if (seconds > 0) {
+ g_snprintf(string_buff, SUM_STR_MAX, "%.3f", summary.packet_count/seconds);
+ } else {
+ string_buff[0] = '\0';
+ }
+ if(summary.dfilter && disp_seconds > 0) {
+ g_snprintf(string_buff2, SUM_STR_MAX, "%.3f", summary.filtered_count/disp_seconds);
+ } else {
+ string_buff2[0] = '\0';
+ }
+ if(summary.marked_count && marked_seconds > 0) {
+ g_snprintf(string_buff3, SUM_STR_MAX, "%.3f", summary.marked_count/marked_seconds);
+ } else {
+ string_buff3[0] = '\0';
+ }
+ add_string_to_list(list, "Avg. packets/sec", string_buff, string_buff2, string_buff3);
+
+ /* Packet size */
+ if (summary.packet_count > 1) {
+ g_snprintf(string_buff, SUM_STR_MAX, "%.3f bytes",
+ /* MSVC cannot convert from unsigned __int64 to float, so first convert to signed __int64 */
+ (float) ((gint64) summary.bytes)/summary.packet_count);
+ } else {
+ string_buff[0] = '\0';
+ }
+ if (summary.dfilter && summary.filtered_count > 1) {
+ g_snprintf(string_buff2, SUM_STR_MAX, "%.3f bytes",
+ /* MSVC cannot convert from unsigned __int64 to float, so first convert to signed __int64 */
+ (float) ((gint64) summary.filtered_bytes)/summary.filtered_count);
+ } else {
+ string_buff2[0] = '\0';
+ }
+ if (summary.marked_count > 1) {
+ g_snprintf(string_buff3, SUM_STR_MAX, "%.3f bytes",
+ /* MSVC cannot convert from unsigned __int64 to float, so first convert to signed __int64 */
+ (float) ((gint64) summary.marked_bytes)/summary.marked_count);
+ } else {
+ string_buff3[0] = '\0';
+ }
+ add_string_to_list(list, "Avg. packet size", string_buff, string_buff2, string_buff3);
+
+ /* Byte count */
+ g_snprintf(string_buff, SUM_STR_MAX, "%" G_GINT64_MODIFIER "u", summary.bytes);
+ if (summary.dfilter && summary.filtered_count > 0) {
+ g_snprintf(string_buff2, SUM_STR_MAX, "%" G_GINT64_MODIFIER "u", summary.filtered_bytes);
+ } else {
+ string_buff2[0] = '\0';
+ }
+ if (summary.marked_count) {
+ g_snprintf(string_buff3, SUM_STR_MAX, "%" G_GINT64_MODIFIER "u", summary.marked_bytes);
+ } else {
+ string_buff3[0] = '\0';
+ }
+ add_string_to_list(list, "Bytes", string_buff, string_buff2, string_buff3);
+
+ /* Bytes per second */
+ if (seconds > 0){
+ /* MSVC cannot convert from unsigned __int64 to float, so first convert to signed __int64 */
+ g_snprintf(string_buff, SUM_STR_MAX, "%.3f", ((gint64) summary.bytes)/seconds);
+ } else {
+ string_buff[0] = '\0';
+ }
+ if (summary.dfilter && disp_seconds > 0) {
+ /* MSVC cannot convert from unsigned __int64 to float, so first convert to signed __int64 */
+ g_snprintf(string_buff2, SUM_STR_MAX, "%.3f", ((gint64) summary.filtered_bytes)/disp_seconds);
+ } else {
+ string_buff2[0] = '\0';
+ }
+ if (summary.marked_count && marked_seconds > 0) {
+ /* MSVC cannot convert from unsigned __int64 to float, so first convert to signed __int64 */
+ g_snprintf(string_buff3, SUM_STR_MAX, "%.3f", ((gint64) summary.marked_bytes)/marked_seconds);
+ } else {
+ string_buff3[0] = '\0';
+ }
+ add_string_to_list(list, "Avg. bytes/sec", string_buff, string_buff2, string_buff3);
+
+ /* MBit per second */
+ if (seconds > 0) {
+ g_snprintf(string_buff, SUM_STR_MAX, "%.3f",
+ /* MSVC cannot convert from unsigned __int64 to float, so first convert to signed __int64 */
+ ((gint64) summary.bytes) * 8.0 / (seconds * 1000.0 * 1000.0));
+ } else {
+ string_buff[0] = '\0';
+ }
+ if (summary.dfilter && disp_seconds > 0) {
+ g_snprintf(string_buff2, SUM_STR_MAX, "%.3f",
+ /* MSVC cannot convert from unsigned __int64 to float, so first convert to signed __int64 */
+ ((gint64) summary.filtered_bytes) * 8.0 / (disp_seconds * 1000.0 * 1000.0));
+ } else {
+ string_buff2[0] = '\0';
+ }
+ if (summary.marked_count && marked_seconds > 0) {
+ g_snprintf(string_buff3, SUM_STR_MAX, "%.3f",
+ /* MSVC cannot convert from unsigned __int64 to float, so first convert to signed __int64 */
+ ((gint64) summary.marked_bytes) * 8.0 / (marked_seconds * 1000.0 * 1000.0));
+ } else {
+ string_buff3[0] = '\0';
+ }
+ add_string_to_list(list, "Avg. MBit/sec", string_buff, string_buff2, string_buff3);
+
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
+ gtk_container_add(GTK_CONTAINER(main_vb), bbox);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(sum_open_w, close_bt, window_cancel_button_cb);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_SUMMARY_DIALOG);
+
+ gtk_widget_grab_focus(close_bt);
+
+ g_signal_connect(sum_open_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+
+ gtk_widget_show_all(sum_open_w);
+ window_present(sum_open_w);
+}
diff --git a/ui/gtk/summary_dlg.h b/ui/gtk/summary_dlg.h
new file mode 100644
index 0000000000..d3f003b49d
--- /dev/null
+++ b/ui/gtk/summary_dlg.h
@@ -0,0 +1,41 @@
+/* summary_dlg.h
+ * Routines for capture file summary window
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __SUMMARY_DLG_H__
+#define __SUMMARY_DLG_H__
+
+/** @file
+ * "Summary" dialog box.
+ */
+
+/**
+ * Create the summary dialog box.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+void summary_open_cb(GtkWidget *widget, gpointer data);
+
+#endif /* __SUMMARY_DLG_H__ */
diff --git a/ui/gtk/supported_protos_dlg.c b/ui/gtk/supported_protos_dlg.c
new file mode 100644
index 0000000000..711e9bded6
--- /dev/null
+++ b/ui/gtk/supported_protos_dlg.c
@@ -0,0 +1,360 @@
+/* supported_protos_dlg.c
+ *
+ * Laurent Deniel <laurent.deniel@free.fr>
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2000 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/prefs.h>
+
+#include "../globals.h"
+
+#include "ui/gtk/supported_protos_dlg.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/font_utils.h"
+
+
+
+static const char *proto_supported =
+"The following %d protocols (and packet types) are currently\n"
+"supported by Wireshark:\n\n";
+
+static const char *dfilter_supported =
+"The following per-protocol fields are currently supported by\n"
+"Wireshark and can be used in display filters:\n";
+
+
+
+typedef enum {
+ PROTOCOL_SUPPORTED,
+ DFILTER_SUPPORTED
+} supported_type_t;
+
+static void supported_destroy_cb(GtkWidget *w, gpointer data);
+static void insert_text(GtkWidget *w, const char *buffer, int nchars);
+static void set_supported_text(GtkWidget *w, supported_type_t type);
+
+/*
+ * Keep a static pointer to the current "Supported" window, if any, so that
+ * if somebody tries to do "Help->Supported" while there's already a
+ * "Supported" window up, we just pop up the existing one, rather than
+ * creating a new one.
+*/
+static GtkWidget *supported_w = NULL;
+
+/*
+ * Keep static pointers to the text widgets as well (for text format changes).
+ */
+static GtkWidget *proto_text, *dfilter_text;
+
+
+
+void supported_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+
+ GtkWidget *main_vb, *bbox, *supported_nb, *ok_bt, *label, *txt_scrollw,
+ *proto_vb,
+ *dfilter_vb;
+
+ if (supported_w != NULL) {
+ /* There's already a "Supported" dialog box; reactivate it. */
+ reactivate_window(supported_w);
+ return;
+ }
+
+ supported_w = window_new(GTK_WINDOW_TOPLEVEL, "Wireshark: Supported Protocols");
+ gtk_window_set_default_size(GTK_WINDOW(supported_w), DEF_WIDTH * 2/3, DEF_HEIGHT * 2/3);
+ gtk_container_set_border_width(GTK_CONTAINER(supported_w), 2);
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 1);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 1);
+ gtk_container_add(GTK_CONTAINER(supported_w), main_vb);
+ gtk_widget_show(main_vb);
+
+ /* supported topics container */
+ supported_nb = gtk_notebook_new();
+ gtk_container_add(GTK_CONTAINER(main_vb), supported_nb);
+
+
+ /* protocol list */
+ proto_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(proto_vb), 1);
+
+ txt_scrollw = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
+ GTK_SHADOW_IN);
+ gtk_box_pack_start(GTK_BOX(proto_vb), txt_scrollw, TRUE, TRUE, 0);
+ proto_text = gtk_text_view_new();
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(proto_text), FALSE);
+ set_supported_text(proto_text, PROTOCOL_SUPPORTED);
+ gtk_container_add(GTK_CONTAINER(txt_scrollw), proto_text);
+ gtk_widget_show(txt_scrollw);
+ gtk_widget_show(proto_text);
+ gtk_widget_show(proto_vb);
+ label = gtk_label_new("Protocols");
+ gtk_notebook_append_page(GTK_NOTEBOOK(supported_nb), proto_vb, label);
+
+ /* display filter fields */
+ dfilter_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(dfilter_vb), 1);
+
+ txt_scrollw = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
+ GTK_SHADOW_IN);
+ gtk_box_pack_start(GTK_BOX(dfilter_vb), txt_scrollw, TRUE, TRUE, 0);
+ dfilter_text = gtk_text_view_new();
+ if (prefs.gui_scrollbar_on_right) {
+ gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(txt_scrollw),
+ GTK_CORNER_TOP_LEFT);
+ }
+ else {
+ gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(txt_scrollw),
+ GTK_CORNER_TOP_RIGHT);
+ }
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(dfilter_text), FALSE);
+ set_supported_text(dfilter_text, DFILTER_SUPPORTED);
+ gtk_container_add(GTK_CONTAINER(txt_scrollw), dfilter_text);
+ gtk_widget_show(txt_scrollw);
+ gtk_widget_show(dfilter_text);
+ gtk_widget_show(dfilter_vb);
+ label = gtk_label_new("Display Filter Fields");
+ gtk_notebook_append_page(GTK_NOTEBOOK(supported_nb), dfilter_vb, label);
+
+ /* XXX add other panels here ... */
+
+ gtk_widget_show(supported_nb);
+
+ /* Button row */
+ bbox = dlg_button_row_new(GTK_STOCK_OK, NULL);
+ gtk_box_pack_end(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+
+ ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ window_set_cancel_button(supported_w, ok_bt, window_cancel_button_cb);
+
+ g_signal_connect(supported_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(supported_w, "destroy", G_CALLBACK(supported_destroy_cb), NULL);
+
+ gtk_widget_show(supported_w);
+ window_present(supported_w);
+} /* supported_cb */
+
+static void supported_destroy_cb(GtkWidget *w _U_, gpointer data _U_)
+{
+ /* Note that we no longer have a Help window. */
+ supported_w = NULL;
+}
+
+static void insert_text(GtkWidget *w, const char *buffer, int nchars)
+{
+ GtkTextBuffer *buf= gtk_text_view_get_buffer(GTK_TEXT_VIEW(w));
+ GtkTextIter iter;
+
+ gtk_text_buffer_get_end_iter(buf, &iter);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_font(w, user_font_get_regular());
+#else
+ gtk_widget_modify_font(w, user_font_get_regular());
+#endif
+ if (!g_utf8_validate(buffer, -1, NULL))
+ printf("Invalid utf8 encoding: %s\n", buffer); /* ToDo: Don't use printf ?? */
+ gtk_text_buffer_insert(buf, &iter, buffer, nchars);
+}
+
+
+static void set_supported_text(GtkWidget *w, supported_type_t type)
+{
+
+#define BUFF_LEN 4096
+#define B_LEN 256
+ char buffer[BUFF_LEN];
+ header_field_info *hfinfo;
+ int i, len, maxlen = 0, maxlen2 = 0, maxlen4 = 0;
+ const char *type_name;
+ void *cookie, *cookie2;
+ protocol_t *protocol;
+ const char *name, *short_name, *filter_name;
+ int namel = 0, short_namel = 0, filter_namel = 0;
+ int count, fcount;
+
+
+ /*
+ * XXX quick hack:
+ * the width and height computations are performed to make the
+ * horizontal scrollbar work in gtk1.2. This is only necessary for the
+ * PROTOCOL_SUPPORTED and DFILTER_SUPPORTED windows since all others should
+ * not have any horizontal scrollbar (line wrapping enabled).
+ */
+
+
+ switch(type) {
+
+ case PROTOCOL_SUPPORTED :
+ /* first pass to know the maximum length of first field */
+ count = 0;
+ for (i = proto_get_first_protocol(&cookie); i != -1;
+ i = proto_get_next_protocol(&cookie)) {
+ count++;
+ protocol = find_protocol_by_id(i);
+ name = proto_get_protocol_name(i);
+ short_name = proto_get_protocol_short_name(protocol);
+ filter_name = proto_get_protocol_filter_name(i);
+ if ((len = (int) strlen(name)) > namel)
+ namel = len;
+ if ((len = (int) strlen(short_name)) > short_namel)
+ short_namel = len;
+ if ((len = (int) strlen(filter_name)) > filter_namel)
+ filter_namel = len;
+ }
+
+ len = g_snprintf(buffer, BUFF_LEN, proto_supported, count);
+ insert_text(w, buffer, len);
+
+ /* ok, display the correctly aligned strings */
+ for (i = proto_get_first_protocol(&cookie); i != -1;
+ i = proto_get_next_protocol(&cookie)) {
+ protocol = find_protocol_by_id(i);
+ name = proto_get_protocol_name(i);
+ short_name = proto_get_protocol_short_name(protocol);
+ filter_name = proto_get_protocol_filter_name(i);
+
+ /* the name used for sorting in the left column */
+ len = g_snprintf(buffer, BUFF_LEN, "%*s %*s %*s\n",
+ -short_namel, short_name,
+ -namel, name,
+ -filter_namel, filter_name);
+ insert_text(w, buffer, len);
+ }
+
+ break;
+
+ case DFILTER_SUPPORTED :
+
+ /* XXX we should display hinfo->blurb instead of name (if not empty) */
+
+ /* first pass to know the maximum length of first and second fields */
+ for (i = proto_get_first_protocol(&cookie); i != -1;
+ i = proto_get_next_protocol(&cookie)) {
+
+ for (hfinfo = proto_get_first_protocol_field(i, &cookie2); hfinfo != NULL;
+ hfinfo = proto_get_next_protocol_field(&cookie2)) {
+
+ if (hfinfo->same_name_prev != NULL) /* ignore duplicate names */
+ continue;
+
+ if ((len = (int) strlen(hfinfo->abbrev)) > maxlen)
+ maxlen = len;
+ if ((len = (int) strlen(hfinfo->name)) > maxlen2)
+ maxlen2 = len;
+ if (hfinfo->blurb != NULL) {
+ if ((len = (int) strlen(hfinfo->blurb)) > maxlen4)
+ maxlen4 = len;
+ }
+ }
+ }
+
+ insert_text(w, dfilter_supported, (int) strlen(dfilter_supported));
+
+ fcount = 0;
+ for (i = proto_get_first_protocol(&cookie); i != -1;
+ i = proto_get_next_protocol(&cookie)) {
+ protocol = find_protocol_by_id(i);
+ name = proto_get_protocol_name(i);
+ short_name = proto_get_protocol_short_name(protocol);
+ filter_name = proto_get_protocol_filter_name(i);
+
+ count = 0;
+ for (hfinfo = proto_get_first_protocol_field(i, &cookie2); hfinfo != NULL;
+ hfinfo = proto_get_next_protocol_field(&cookie2)) {
+
+ if (hfinfo->same_name_prev != NULL) /* ignore duplicate names */
+ continue;
+ count++;
+ }
+ fcount += count;
+
+ len = g_snprintf(buffer, BUFF_LEN, "\n%s - %s (%s) [%d fields]:\n",
+ short_name, name, filter_name, count);
+ insert_text(w, buffer, len);
+
+ for (hfinfo = proto_get_first_protocol_field(i, &cookie2); hfinfo != NULL;
+ hfinfo = proto_get_next_protocol_field(&cookie2)) {
+
+ if (hfinfo->same_name_prev != NULL) /* ignore duplicate names */
+ continue;
+
+ type_name = ftype_pretty_name(hfinfo->type);
+ if (hfinfo->blurb != NULL && hfinfo->blurb[0] != '\0') {
+ len = g_snprintf(buffer, BUFF_LEN, "%*s %*s %*s (%s)\n",
+ -maxlen, hfinfo->abbrev,
+ -maxlen2, hfinfo->name,
+ -maxlen4, hfinfo->blurb,
+ type_name);
+ } else {
+ len = g_snprintf(buffer, BUFF_LEN, "%*s %*s (%s)\n",
+ -maxlen, hfinfo->abbrev,
+ -maxlen2, hfinfo->name,
+ type_name);
+ }
+ insert_text(w, buffer, len);
+ }
+ }
+ len = g_snprintf(buffer, BUFF_LEN, "\n-- Total %d fields\n", fcount);
+ insert_text(w, buffer, len);
+
+ break;
+ default :
+ g_assert_not_reached();
+ break;
+ } /* switch(type) */
+} /* set_supported_text */
+
+
+static void clear_supported_text(GtkWidget *w)
+{
+ GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(w));
+
+ gtk_text_buffer_set_text(buf, "", 0);
+}
+
+
+/* Redraw all the text widgets, to use a new font. */
+void supported_redraw(void)
+{
+ if (supported_w != NULL) {
+ clear_supported_text(proto_text);
+ set_supported_text(proto_text, PROTOCOL_SUPPORTED);
+ clear_supported_text(dfilter_text);
+ set_supported_text(dfilter_text, DFILTER_SUPPORTED);
+ }
+}
diff --git a/ui/gtk/supported_protos_dlg.h b/ui/gtk/supported_protos_dlg.h
new file mode 100644
index 0000000000..1ce2b6b846
--- /dev/null
+++ b/ui/gtk/supported_protos_dlg.h
@@ -0,0 +1,45 @@
+/* supported_protos_dlg.h
+ *
+ * Laurent Deniel <laurent.deniel@free.fr>
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2000 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __SUPPORTED_PROTOS_DLG_H__
+#define __SUPPORTED_PROTOS_DLG_H__
+
+/** @file
+ * "Supported Protocols" dialog box.
+ */
+
+/**
+ * Create the supported protos dialog box.
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ */
+void supported_cb(GtkWidget *widget, gpointer data);
+
+/** Redraw all the supported protos text widgets, to use a new font. */
+void supported_redraw(void);
+
+#endif /* __SUPPORTED_PROTOS_DLG_H__ */
diff --git a/ui/gtk/tap_param_dlg.c b/ui/gtk/tap_param_dlg.c
new file mode 100644
index 0000000000..731d778fee
--- /dev/null
+++ b/ui/gtk/tap_param_dlg.c
@@ -0,0 +1,322 @@
+/* tap_param_dlg.c
+ * Routines for parameter dialog used by gui taps
+ * Copyright 2003 Lars Roland
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#include <gtk/gtk.h>
+
+#include <epan/stat_cmd_args.h>
+
+#include "../simple_dialog.h"
+#include "../file.h"
+#include "../globals.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/filter_dlg.h"
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/filter_autocomplete.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+typedef struct _tap_param_dlg_list_item {
+ GtkWidget *dlg;
+ tap_param_dlg cont;
+ construct_args_t args;
+ GtkWidget **param_items; /* items for params */
+ struct _tap_param_dlg_list_item *next;
+} tap_param_dlg_list_item;
+
+static tap_param_dlg_list_item *start_dlg_list=NULL;
+static tap_param_dlg_list_item *end_dlg_list=NULL;
+static tap_param_dlg_list_item *current_dlg = NULL;
+
+/*
+ * Register a stat that has a parameter dialog.
+ * We register it both as a command-line stat and a menu item stat.
+ */
+void
+register_dfilter_stat(tap_param_dlg *info, const char *name _U_,
+ register_stat_group_t group _U_ )
+{
+ register_stat_cmd_arg(info->init_string, info->tap_init_cb, NULL);
+}
+
+void tap_param_dlg_update (void)
+{
+ tap_param_dlg_list_item *dialog = start_dlg_list;
+ char *title;
+
+ while(dialog != NULL) {
+ if(dialog->dlg) {
+ title = g_strdup_printf("Wireshark: %s: %s", dialog->cont.win_title , cf_get_display_name(&cfile));
+ gtk_window_set_title(GTK_WINDOW(dialog->dlg), title);
+ g_free(title);
+ }
+ dialog = dialog->next;
+ }
+}
+
+static void
+dlg_destroy_cb(GtkWidget *item _U_, gpointer dialog_data)
+{
+ tap_param_dlg_list_item *dlg_data = (tap_param_dlg_list_item *) dialog_data;
+ dlg_data->dlg = NULL;
+}
+
+static void
+tap_param_dlg_start_button_clicked(GtkWidget *item _U_, gpointer dialog_data)
+{
+ GString *params;
+ size_t i;
+ gint j;
+
+ tap_param_dlg_list_item *dlg_data = (tap_param_dlg_list_item *) dialog_data;
+
+ params = g_string_new(dlg_data->cont.init_string);
+ for(i=0;i<dlg_data->cont.nparams;i++) {
+ g_string_append_c(params, ',');
+ switch (dlg_data->cont.params[i].type) {
+
+ case PARAM_ENUM:
+ j = gtk_combo_box_get_active(GTK_COMBO_BOX(dlg_data->param_items[i]));
+ g_string_append_printf(params,"%d",
+ dlg_data->cont.params[i].enum_vals[j].value);
+ break;
+
+ case PARAM_UINT:
+ case PARAM_STRING:
+ case PARAM_FILTER:
+ g_string_append(params,
+ gtk_entry_get_text(GTK_ENTRY(dlg_data->param_items[i])));
+ break;
+ }
+ }
+ (dlg_data->cont.tap_init_cb)(params->str,NULL);
+ g_string_free(params, TRUE);
+}
+
+void
+tap_param_dlg_cb(GtkAction *action _U_, gpointer data)
+{
+ const char *filter;
+ char *title;
+ GtkWidget *dlg_box;
+ GtkWidget *item_box, *item, *label, *filter_bt;
+ GtkWidget *bbox, *start_button, *cancel_button;
+ size_t i, j;
+ char *label_with_colon;
+
+ tap_param_dlg *dlg_data = (tap_param_dlg *) data;
+
+ if(dlg_data==NULL)
+ return;
+
+ if(dlg_data->index==-1) {
+ /* Dialog is not registered */
+ if(start_dlg_list==NULL) {
+ start_dlg_list = (tap_param_dlg_list_item *) g_malloc(sizeof (tap_param_dlg_list_item));
+ end_dlg_list = start_dlg_list;
+ end_dlg_list->cont.index = 0; /* first entry in list -> index = 0 */
+ } else {
+ end_dlg_list->next = (tap_param_dlg_list_item *) g_malloc(sizeof (tap_param_dlg_list_item));
+ end_dlg_list->next->cont.index = end_dlg_list->cont.index + 1;
+ end_dlg_list = end_dlg_list->next;
+ }
+ end_dlg_list->dlg = NULL;
+ end_dlg_list->param_items = g_malloc(dlg_data->nparams * sizeof (GtkWidget *));
+ end_dlg_list->cont.win_title = dlg_data->win_title;
+ end_dlg_list->cont.init_string = dlg_data->init_string;
+ end_dlg_list->cont.tap_init_cb = dlg_data->tap_init_cb;
+ end_dlg_list->cont.nparams = dlg_data->nparams;
+ end_dlg_list->cont.params = dlg_data->params;
+ end_dlg_list->args.title = g_strdup_printf("%s Filter", dlg_data->win_title);
+ end_dlg_list->args.wants_apply_button = TRUE;
+ end_dlg_list->args.activate_on_ok = FALSE;
+ end_dlg_list->args.modal_and_transient = FALSE;
+ end_dlg_list->next = NULL;
+ dlg_data->index = end_dlg_list->cont.index;
+ current_dlg = end_dlg_list;
+ } else {
+ /* Dialog is registered, find it */
+ current_dlg = start_dlg_list;
+ while(dlg_data->index != current_dlg->cont.index)
+ {
+ if(current_dlg->next == NULL) {
+ /* could not find any dialog */
+ return;
+ }
+ current_dlg = current_dlg->next;
+ }
+ }
+
+ /* if the window is already open, bring it to front */
+ if(current_dlg->dlg){
+ gdk_window_raise(gtk_widget_get_window(current_dlg->dlg));
+ return;
+ }
+
+ title = g_strdup_printf("Wireshark: %s: %s", current_dlg->cont.win_title , cf_get_display_name(&cfile));
+
+ current_dlg->dlg=dlg_window_new(title);
+ gtk_window_set_default_size(GTK_WINDOW(current_dlg->dlg), 300, -1);
+ g_free(title);
+
+ dlg_box=gtk_vbox_new(FALSE, 10);
+ gtk_container_set_border_width(GTK_CONTAINER(dlg_box), 10);
+ gtk_container_add(GTK_CONTAINER(current_dlg->dlg), dlg_box);
+ gtk_widget_show(dlg_box);
+
+ /* Parameter items */
+ for(i=0;i<current_dlg->cont.nparams;i++) {
+ /* Item box */
+ item_box=gtk_hbox_new(FALSE, 3);
+
+ switch (current_dlg->cont.params[i].type) {
+
+ case PARAM_UINT:
+ case PARAM_STRING:
+ /* Label */
+ label_with_colon=g_strdup_printf("%s:", current_dlg->cont.params[i].title);
+ label=gtk_label_new(label_with_colon);
+ g_free(label_with_colon);
+ gtk_box_pack_start(GTK_BOX(item_box), label, FALSE, TRUE, 0);
+ gtk_widget_show(label);
+
+ /* Entry */
+ item=gtk_entry_new();
+ break;
+
+ case PARAM_ENUM:
+ /* Label */
+ label_with_colon=g_strdup_printf("%s:", current_dlg->cont.params[i].title);
+ label=gtk_label_new(label_with_colon);
+ g_free(label_with_colon);
+ gtk_box_pack_start(GTK_BOX(item_box), label, FALSE, TRUE, 0);
+ gtk_widget_show(label);
+
+ /* Combo box */
+ item=gtk_combo_box_text_new();
+ for (j = 0; current_dlg->cont.params[i].enum_vals[j].name != NULL;
+ j++)
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(item),
+ current_dlg->cont.params[i].enum_vals[j].description);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(item), 0);
+ break;
+
+ case PARAM_FILTER:
+ /* Filter button */
+ filter_bt=gtk_button_new_from_stock(WIRESHARK_STOCK_DISPLAY_FILTER_ENTRY);
+ g_signal_connect(filter_bt, "clicked", G_CALLBACK(display_filter_construct_cb), &(current_dlg->args));
+ gtk_box_pack_start(GTK_BOX(item_box), filter_bt, FALSE, TRUE, 0);
+ gtk_widget_show(filter_bt);
+
+ /* Entry */
+ item=gtk_entry_new();
+ g_signal_connect(item, "changed", G_CALLBACK(filter_te_syntax_check_cb), NULL);
+ g_object_set_data(G_OBJECT(item_box), E_FILT_AUTOCOMP_PTR_KEY, NULL);
+ g_signal_connect(item, "key-press-event", G_CALLBACK (filter_string_te_key_pressed_cb), NULL);
+ g_signal_connect(current_dlg->dlg, "key-press-event", G_CALLBACK (filter_parent_dlg_key_pressed_cb), NULL);
+ g_object_set_data(G_OBJECT(filter_bt), E_FILT_TE_PTR_KEY, item);
+
+ filter=gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
+ if(filter){
+ gtk_entry_set_text(GTK_ENTRY(item), filter);
+ } else {
+ colorize_filter_te_as_empty(item);
+ }
+ break;
+
+ default:
+ g_assert_not_reached();
+ item=NULL;
+ break;
+ }
+
+ gtk_box_pack_start(GTK_BOX(item_box), item, TRUE, TRUE, 0);
+ current_dlg->param_items[i]=item;
+ gtk_widget_show(item);
+
+ gtk_box_pack_start(GTK_BOX(dlg_box), item_box, TRUE, TRUE, 0);
+ gtk_widget_show(item_box);
+ }
+
+ /* button box */
+ bbox = dlg_button_row_new(WIRESHARK_STOCK_CREATE_STAT, GTK_STOCK_CANCEL, NULL);
+ gtk_box_pack_start(GTK_BOX(dlg_box), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+
+ start_button = g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_CREATE_STAT);
+ g_signal_connect(start_button, "clicked",
+ G_CALLBACK(tap_param_dlg_start_button_clicked), current_dlg);
+
+ cancel_button = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ window_set_cancel_button(current_dlg->dlg, cancel_button, window_cancel_button_cb);
+
+ /* Catch the "activate" signal on all the text entries, so that
+ if the user types Return there, we act as if the "Create Stat"
+ button had been selected, as happens if Return is typed if
+ some widget that *doesn't* handle the Return key has the input
+ focus. */
+ for(i=0;i<current_dlg->cont.nparams;i++){
+ switch (current_dlg->cont.params[i].type) {
+
+ case PARAM_ENUM:
+ break;
+
+ case PARAM_UINT:
+ case PARAM_STRING:
+ case PARAM_FILTER:
+ dlg_set_activate(current_dlg->param_items[i], start_button);
+ break;
+ }
+ }
+
+ /* Give the initial focus to the first entry box. */
+ if(current_dlg->cont.nparams>0){
+ gtk_widget_grab_focus(current_dlg->param_items[0]);
+ }
+
+ gtk_widget_grab_default(start_button );
+
+ g_signal_connect(current_dlg->dlg, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(current_dlg->dlg, "destroy", G_CALLBACK(dlg_destroy_cb), current_dlg);
+
+ gtk_widget_show_all(current_dlg->dlg);
+ window_present(current_dlg->dlg);
+}
diff --git a/ui/gtk/tap_param_dlg.h b/ui/gtk/tap_param_dlg.h
new file mode 100644
index 0000000000..b4cc917705
--- /dev/null
+++ b/ui/gtk/tap_param_dlg.h
@@ -0,0 +1,102 @@
+/* tap_param_dlg.h
+ * Header file for parameter dialog used by gui taps
+ * Copyright 2003 Lars Roland
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __TAP_DFILTER_DLG_H__
+#define __TAP_DFILTER_DLG_H__
+
+/*
+ * You can easily add a parameter dialog for your gui tap by using
+ * the following infrastructure:
+ *
+ * Define a global structure of tap_param_dlg within your stat source file.
+ * Initiate it with:
+ * 1) a title string for the Dialog Window
+ * 2) the init string, which is the same as the string after "-z" option without
+ * the filter string and without the separating comma.
+ * 3) a pointer to the init function of the tap, which will be called when you click
+ * on the start button in the display filter dialog.
+ * 4) the index with "-1"
+ *
+ * Within register_tap_menu_yourtap(void), call register_dfilter_stat()
+ * with a pointer to the tap_param_dlg structure, a string for the
+ * menu item (don't put "..." at the end, register_dfilter_stat() will
+ * add it for you), and the REGISTER_STAT_GROUP_ value for the stat
+ * group to which your stat should belong.
+ *
+ * Usage:
+ *
+ * tap_param_dlg my_tap_param_dlg = {
+ * "My Title",
+ * "myproto,mytap",
+ * gtk_mytap_init,
+ * -1
+ * };
+ *
+ * register_tap_menu_mytap(void) {
+ * register_dfilter_stat(&my_tap_param_dlg, "My Menu Item",
+ * REGISTER_STAT_GROUP_my_group);
+ * }
+ *
+ * See also: h225_ras_srt.c or h225_counter.c
+ *
+ */
+
+#include <epan/params.h>
+
+typedef enum {
+ PARAM_UINT,
+ PARAM_STRING,
+ PARAM_ENUM,
+ PARAM_FILTER
+} param_type;
+
+typedef struct _tap_param {
+ param_type type;
+ const char *title;
+ enum_val_t *enum_vals;
+} tap_param;
+
+typedef struct _tap_param_dlg {
+ const char *win_title; /* title */
+ const char *init_string; /* the string to call the tap without a filter via "-z" option */
+ void (* tap_init_cb)(const char *,void*); /* callback to init function of the tap */
+ gint index; /* initiate this value always with "-1" */
+ size_t nparams; /* number of parameters */
+ tap_param *params; /* pointer to table of parameter info */
+} tap_param_dlg;
+
+/*
+ * Register a stat that has a display filter dialog.
+ * We register it both as a command-line stat and a menu item stat.
+ */
+void register_dfilter_stat(tap_param_dlg *info, const char *name,
+ register_stat_group_t group);
+
+void tap_param_dlg_cb(GtkAction *action, gpointer user_data);
+
+/* This will update the titles of the dialog windows when we load a new capture file. */
+void tap_param_dlg_update (void);
+
+#endif /* __TAP_DFILTER_DLG_H__ */
diff --git a/ui/gtk/tcp_graph.c b/ui/gtk/tcp_graph.c
new file mode 100644
index 0000000000..4085cb31dc
--- /dev/null
+++ b/ui/gtk/tcp_graph.c
@@ -0,0 +1,4608 @@
+/* tcp_graph.c
+ * TCP graph drawing code
+ * By Pavel Mores <pvl@uh.cz>
+ * Win32 port: rwh@unifiedtech.com
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#if defined(GDK_DISABLE_DEPRECATED)
+# undef GDK_DISABLE_DEPRECATED
+#endif
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#if GTK_CHECK_VERSION(3,0,0)
+# include <gdk/gdkkeysyms-compat.h>
+#endif
+
+#include <epan/packet.h>
+#include <epan/ipproto.h>
+#include <epan/etypes.h>
+#include <epan/ppptypes.h>
+#include <epan/epan_dissect.h>
+#include <epan/dissectors/packet-tcp.h>
+#include <epan/address.h>
+#include <epan/tap.h>
+
+#include "../globals.h"
+#include "../simple_dialog.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_stat_menu.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+#define TH_FIN 0x01
+#define TH_SYN 0x02
+#define TH_RST 0x04
+#define TH_PUSH 0x08
+#define TH_ACK 0x10
+#define TH_URG 0x20
+
+#define TCP_SYN(flags) ( flags & TH_SYN )
+#define TCP_ACK(flags) ( flags & TH_ACK )
+#define TCP_FIN(flags) ( flags & TH_FIN )
+
+#define TXT_WIDTH 850
+#define TXT_HEIGHT 550
+
+/* for compare_headers() */
+/* segment went the same direction as the currently selected one */
+#define COMPARE_CURR_DIR 0
+#define COMPARE_ANY_DIR 1
+
+/* initialize_axis() */
+#define AXIS_HORIZONTAL 0
+#define AXIS_VERTICAL 1
+
+#define WINDOW_TITLE_LENGTH 256
+
+#define MOUSE_BUTTON_LEFT 1
+#define MOUSE_BUTTON_MIDDLE 2
+#define MOUSE_BUTTON_RIGHT 3
+
+struct segment {
+ struct segment *next;
+ guint32 num;
+ guint32 rel_secs;
+ guint32 rel_usecs;
+ guint32 abs_secs;
+ guint32 abs_usecs;
+
+ guint32 th_seq;
+ guint32 th_ack;
+ guint16 th_flags;
+ guint32 th_win; /* make it 32 bits so we can handle some scaling */
+ guint32 th_seglen;
+ guint16 th_sport;
+ guint16 th_dport;
+ address ip_src;
+ address ip_dst;
+};
+
+struct rect {
+ double x, y, width, height;
+};
+
+struct line {
+ double x1, y1, x2, y2;
+};
+
+struct irect {
+ int x, y, width, height;
+};
+
+struct ipoint {
+ int x, y;
+};
+
+typedef enum {
+ ELMT_NONE=0,
+ ELMT_RECT=1,
+ ELMT_LINE=2,
+ ELMT_ELLIPSE=3
+} ElementType;
+
+struct rect_params {
+ struct rect dim;
+ gint filled;
+};
+
+struct line_params {
+ struct line dim;
+};
+
+struct ellipse_params {
+ struct rect dim;
+};
+
+struct element {
+ ElementType type;
+ GdkColor *elment_color_p;
+ struct segment *parent;
+ union {
+ struct ellipse_params ellipse;
+ struct rect_params rect;
+ struct line_params line;
+ } p;
+};
+
+struct element_list {
+ struct element_list *next;
+ struct element *elements;
+};
+
+struct axis {
+ struct graph *g; /* which graph we belong to */
+ GtkWidget *drawing_area;
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_surface_t *surface[2];
+#else
+ GdkPixmap *pixmap[2];
+#endif
+ int displayed;
+#define AXIS_ORIENTATION 1 << 0
+ int flags;
+ /* dim and orig (relative to origin of window) of axis' pixmap */
+ struct irect p;
+ /* dim and orig (relative to origin of axis' pixmap) of scale itself */
+ struct irect s;
+ gdouble min, max;
+ gdouble major, minor; /* major and minor ticks */
+ const char **label;
+};
+
+#define HAXIS_INIT_HEIGHT 70
+#define VAXIS_INIT_WIDTH 100
+#define TITLEBAR_HEIGHT 50
+#define RMARGIN_WIDTH 30
+
+struct style_tseq_tcptrace {
+ GdkColor seq_color;
+ GdkColor ack_color[2];
+ int flags;
+};
+
+struct style_tseq_stevens {
+ int seq_width;
+ int seq_height;
+ int flags;
+};
+
+struct style_tput {
+ int width, height;
+ int nsegs;
+ int flags;
+};
+
+struct style_rtt {
+ int width, height;
+ int flags;
+};
+
+struct style_wscale {
+ int win_width;
+ int win_height;
+ int flags;
+};
+
+/* style flags */
+#define SEQ_ORIGIN 0x1
+/* show absolute sequence numbers (not differences from isn) */
+#define SEQ_ORIGIN_ZERO 0x1
+#define SEQ_ORIGIN_ISN 0x0
+#define TIME_ORIGIN 0x10
+/* show time from beginning of capture as opposed to time from beginning
+ * of the connection */
+#define TIME_ORIGIN_CAP 0x10
+#define TIME_ORIGIN_CONN 0x0
+
+/* this is used by rtt module only */
+struct unack {
+ struct unack *next;
+ double time;
+ unsigned int seqno;
+};
+
+struct cross {
+ int x, y;
+ int draw; /* indicates whether we should draw cross at all */
+ int erase_needed;
+ GtkToggleButton *on_toggle;
+ GtkToggleButton *off_toggle;
+};
+
+struct bounds {
+ double x0, y0, width, height;
+};
+
+struct zoom {
+ double x, y;
+};
+
+struct zooms {
+ double x, y;
+ double step_x, step_y;
+ struct zoom initial;
+#define ZOOM_OUT (1 << 0)
+#define ZOOM_HLOCK (1 << 1)
+#define ZOOM_VLOCK (1 << 2)
+#define ZOOM_STEPS_SAME (1 << 3)
+#define ZOOM_STEPS_KEEP_RATIO (1 << 4)
+ int flags;
+ /* unfortunately, we need them both because gtk_toggle_button_set_active ()
+ * with second argument FALSE doesn't do anything, somehow */
+ struct {
+ GtkToggleButton *in_toggle;
+ GtkToggleButton *out_toggle;
+ GtkEntry *h_zoom;
+ GtkEntry *v_zoom;
+ GtkSpinButton *h_step;
+ GtkSpinButton *v_step;
+ } widget;
+};
+
+struct grab {
+ int grabbed;
+ int x, y;
+};
+
+struct magnify {
+ int active;
+ int x, y;
+ struct ipoint offset;
+ int width, height;
+ struct zoom zoom;
+ struct graph *g;
+#define MAGZOOMS_SAME (1U << 0)
+#define MAGZOOMS_SAME_RATIO (1U << 1)
+#define MAGZOOMS_IGNORE (1U << 31)
+ guint flags;
+ struct {
+ GtkSpinButton *h_zoom, *v_zoom;
+ } widget;
+};
+
+struct graph {
+ struct graph *next;
+#define GRAPH_TSEQ_STEVENS 0
+#define GRAPH_TSEQ_TCPTRACE 1
+#define GRAPH_THROUGHPUT 2
+#define GRAPH_RTT 3
+#define GRAPH_WSCALE 4
+ int type;
+#define GRAPH_DESTROYED (1 << 0)
+#define GRAPH_INIT_ON_TYPE_CHANGE (1 << 1)
+ int flags;
+ GtkWidget *toplevel; /* keypress handler needs this */
+ GtkWidget *drawing_area;
+ GtkWidget *text; /* text widget for seg list - probably
+ * temporary
+ */
+ PangoFontDescription *font; /* font used for annotations etc. */
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_surface_t *title_surface;
+ cairo_surface_t *surface[2];
+#else
+ GdkPixmap *title_pixmap;
+ GdkPixmap *pixmap[2];
+#endif
+ int displayed; /* which of both pixmaps is on screen right now */
+ struct {
+ GtkWidget *control_panel;
+ /* this belongs to style structs of graph types that make use of it */
+ GtkToggleButton *time_orig_conn, *seq_orig_isn;
+ } gui;
+ const char **title;
+ /* Next 4 attribs describe the graph in natural units, before any scaling.
+ * For example, if we want to display graph of TCP conversation that
+ * started 112.309845 s after beginning of the capture and ran until
+ * 479.093582 s, 237019 B went through the connection (in one direction)
+ * starting with isn 31934022, then (bounds.x0, bounds.y0)=(112.309845,
+ * 31934022) and (bounds.width, bounds.height)=(366.783737, 237019). */
+ struct bounds bounds;
+ /* dimensions and position of the graph, both expressed already in pixels.
+ * x and y give the position of upper left corner of the graph relative
+ * to origin of the graph window, size is basically bounds*zoom */
+ struct irect geom;
+ /* viewport (=graph window area which is reserved for graph itself), its
+ * size and position relative to origin of the graph window */
+ struct irect wp;
+ struct grab grab;
+ /* If we need to display 237019 sequence numbers (=bytes) onto say 500
+ * pixels, we have to scale the graph down by factor of 0.002109. This
+ * number would be zoom.y. Obviously, both directions have separate zooms.*/
+ struct zooms zoom;
+ struct cross cross;
+ struct magnify magnify;
+ struct axis *x_axis, *y_axis;
+ struct segment *segments;
+ struct segment *current;
+ struct element_list *elists; /* element lists */
+ union {
+ struct style_tseq_stevens tseq_stevens;
+ struct style_tseq_tcptrace tseq_tcptrace;
+ struct style_tput tput;
+ struct style_rtt rtt;
+ struct style_wscale wscale;
+ } s;
+ /* This allows keyboard to set the radio button */
+ struct {
+ GtkToggleButton *graph_rtt, *graph_tput, *graph_tseqstevens, *graph_tseqttrace;
+ GtkToggleButton *graph_wscale;
+ } gt;
+};
+
+#if !GTK_CHECK_VERSION(3,0,0)
+static GdkGC *xor_gc = NULL;
+#endif
+static int refnum=0;
+
+#define debug(section) if (debugging & section)
+/* print function entry points */
+#define DBS_FENTRY (1 << 0)
+#define DBS_AXES_TICKS (1 << 1)
+#define DBS_AXES_DRAWING (1 << 2)
+#define DBS_GRAPH_DRAWING (1 << 3)
+#define DBS_TPUT_ELMTS (1 << 4)
+/*int debugging = DBS_FENTRY;*/
+static int debugging = 0;
+/*int debugging = DBS_AXES_TICKS;*/
+/*int debugging = DBS_AXES_DRAWING;*/
+/*int debugging = DBS_GRAPH_DRAWING;*/
+/*int debugging = DBS_TPUT_ELMTS;*/
+
+static void create_gui (struct graph * );
+#if 0
+static void create_text_widget (struct graph * );
+static void display_text (struct graph * );
+#endif
+static void create_drawing_area (struct graph * );
+static void control_panel_create (struct graph * );
+static GtkWidget *control_panel_create_zoom_group (struct graph * );
+static GtkWidget *control_panel_create_magnify_group (struct graph * );
+static GtkWidget *control_panel_create_cross_group (struct graph * );
+static GtkWidget *control_panel_create_zoomlock_group (struct graph * );
+static GtkWidget *control_panel_create_graph_type_group (struct graph * );
+static void control_panel_add_zoom_page (struct graph * , GtkWidget * );
+static void control_panel_add_magnify_page (struct graph * , GtkWidget * );
+static void control_panel_add_origin_page (struct graph * , GtkWidget * );
+static void control_panel_add_cross_page (struct graph * , GtkWidget * );
+static void control_panel_add_graph_type_page (struct graph * , GtkWidget * );
+static void callback_toplevel_destroy (GtkWidget * , gpointer );
+static gboolean callback_delete_event(GtkWidget * , GdkEvent * , gpointer);
+static void callback_close (GtkWidget * , gpointer );
+static void callback_time_origin (GtkWidget * , gpointer );
+static void callback_seq_origin (GtkWidget * , gpointer );
+static void callback_zoomlock_h (GtkWidget * , gpointer );
+static void callback_zoomlock_v (GtkWidget * , gpointer );
+static void callback_zoom_inout (GtkWidget * , gpointer );
+static void callback_zoom_step (GtkWidget * , gpointer );
+static void callback_zoom_flags (GtkWidget * , gpointer );
+static void callback_cross_on_off (GtkWidget * , gpointer );
+static void callback_mag_width (GtkWidget * , gpointer );
+static void callback_mag_height (GtkWidget * , gpointer );
+static void callback_mag_x (GtkWidget * , gpointer );
+static void callback_mag_y (GtkWidget * , gpointer );
+static void callback_mag_zoom (GtkWidget * , gpointer );
+static void callback_mag_flags (GtkWidget * , gpointer );
+static void callback_graph_type (GtkWidget * , gpointer );
+static void callback_graph_init_on_typechg (GtkWidget * , gpointer );
+static void callback_create_help (GtkWidget * , gpointer );
+static void update_zoom_spins (struct graph * );
+static struct tcpheader *select_tcpip_session (capture_file *, struct segment * );
+static int compare_headers (address *saddr1, address *daddr1, guint16 sport1, guint16 dport1, address *saddr2, address *daddr2, guint16 sport2, guint16 dport2, int dir);
+static int get_num_dsegs (struct graph * );
+static int get_num_acks (struct graph * );
+static void graph_type_dependent_initialize (struct graph * );
+static struct graph *graph_new (void);
+static void graph_destroy (struct graph * );
+static void graph_initialize_values (struct graph * );
+static void graph_init_sequence (struct graph * );
+static void draw_element_line (struct graph * , struct element * );
+static void draw_element_ellipse (struct graph * , struct element * );
+static void graph_display (struct graph * );
+static void graph_pixmaps_create (struct graph * );
+static void graph_pixmaps_switch (struct graph * );
+static void graph_pixmap_draw (struct graph * );
+static void graph_pixmap_display (struct graph * );
+static void graph_element_lists_make (struct graph * );
+static void graph_element_lists_free (struct graph * );
+static void graph_element_lists_initialize (struct graph * );
+static void graph_title_pixmap_create (struct graph * );
+static void graph_title_pixmap_draw (struct graph * );
+static void graph_title_pixmap_display (struct graph * );
+static void graph_segment_list_get (struct graph * );
+static void graph_segment_list_free (struct graph * );
+static void graph_select_segment (struct graph * , int , int );
+static int line_detect_collision (struct element * , int , int );
+static int ellipse_detect_collision (struct element * , int , int );
+static void axis_pixmaps_create (struct axis * );
+static void axis_pixmaps_switch (struct axis * );
+static void axis_display (struct axis * );
+static void v_axis_pixmap_draw (struct axis * );
+static void h_axis_pixmap_draw (struct axis * );
+static void axis_pixmap_display (struct axis * );
+static void axis_compute_ticks (struct axis * , double , double , int );
+static double axis_zoom_get (struct axis * , int );
+static void axis_ticks_up (int * , int * );
+static void axis_ticks_down (int * , int * );
+static void axis_destroy (struct axis * );
+static int get_label_dim (struct axis * , int , double );
+static void toggle_crosshairs (struct graph *g);
+static void toggle_time_origin (struct graph * );
+static void toggle_seq_origin (struct graph * );
+static void restore_initial_graph_view (struct graph *g);
+static void cross_xor (struct graph * , int , int );
+static void cross_draw (struct graph * , int , int );
+static void cross_erase (struct graph * );
+static void magnify_create (struct graph * , int , int );
+static void magnify_move (struct graph * , int , int );
+static void magnify_destroy (struct graph * );
+static void magnify_draw (struct graph * );
+static void magnify_get_geom (struct graph * , int , int );
+static gboolean configure_event (GtkWidget * , GdkEventConfigure * , gpointer );
+#if GTK_CHECK_VERSION(3,0,0)
+static gboolean draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data);
+#else
+static gboolean expose_event (GtkWidget * , GdkEventExpose * , gpointer );
+#endif
+static gboolean button_press_event (GtkWidget * , GdkEventButton * , gpointer );
+static gboolean button_release_event (GtkWidget * , GdkEventButton * , gpointer );
+static gboolean motion_notify_event (GtkWidget * , GdkEventMotion * , gpointer );
+static gboolean key_press_event (GtkWidget * , GdkEventKey * , gpointer );
+static gboolean key_release_event (GtkWidget * , GdkEventKey * , gpointer );
+static gboolean leave_notify_event (GtkWidget * , GdkEventCrossing * , gpointer );
+static gboolean enter_notify_event (GtkWidget * , GdkEventCrossing * , gpointer );
+static void tseq_initialize (struct graph * );
+static void tseq_get_bounds (struct graph * );
+static void tseq_stevens_read_config (struct graph * );
+static void tseq_stevens_make_elmtlist (struct graph * );
+static void tseq_stevens_toggle_seq_origin (struct graph * );
+static void tseq_stevens_toggle_time_origin (struct graph * );
+static void tseq_tcptrace_read_config (struct graph * );
+static void tseq_tcptrace_make_elmtlist (struct graph * );
+static void tseq_tcptrace_toggle_seq_origin (struct graph * );
+static void tseq_tcptrace_toggle_time_origin (struct graph * );
+static void tput_initialize (struct graph * );
+static void tput_read_config (struct graph * );
+static void tput_make_elmtlist (struct graph * );
+static void tput_toggle_time_origin (struct graph * );
+static void rtt_read_config (struct graph * );
+static void rtt_initialize (struct graph * );
+static int rtt_is_retrans (struct unack * , unsigned int );
+static struct unack *rtt_get_new_unack (double , unsigned int );
+static void rtt_put_unack_on_list (struct unack ** , struct unack * );
+static void rtt_delete_unack_from_list (struct unack ** , struct unack * );
+static void rtt_make_elmtlist (struct graph * );
+static void rtt_toggle_seq_origin (struct graph * );
+static void wscale_initialize(struct graph *);
+static void wscale_read_config(struct graph *);
+static void wscale_make_elmtlist(struct graph *);
+#if defined(_WIN32) && !defined(__MINGW32__)
+static int rint (double ); /* compiler template for Windows */
+#endif
+
+/*
+ * Uncomment the following define to revert WIN32 to
+ * use original mouse button controls
+ */
+
+/* #define ORIGINAL_WIN32_BUTTONS 1 */
+
+/* XXX - what about OS X? */
+static char helptext[] =
+ "Here's what you can do:\n"
+ "\n"
+#ifdef ORIGINAL_WIN32_BUTTONS
+ " <Ctrl>-Left Mouse Button selects segment under cursor in Wireshark's packet list\n"
+ "\n"
+ " Left Mouse Button zooms in (towards area under mouse pointer)\n"
+ " <Shift>-Left Mouse Button zooms out\n"
+ "\n"
+ " Right Mouse Button moves the graph (if zoomed in)\n"
+ " <Ctrl>-Right Mouse Button displays a portion of graph under cursor magnified\n"
+#else /* !ORIGINAL_WIN32_BUTTONS */
+ " Left Mouse Button selects segment under cursor in Wireshark's packet list\n"
+ "\n"
+ " Middle Mouse Button zooms in (towards area under cursor)\n"
+ " <Shift>-Middle Mouse Button zooms out\n"
+ "\n"
+ " Right Mouse Button moves the graph (if zoomed in)\n"
+ " <Ctrl>-Right Mouse Button displays a portion of graph under cursor magnified\n"
+#endif
+ "\n"
+ "\n"
+ " '1' display Round Trip Time Graph\n"
+ " '2' display Throughput Graph\n"
+ " '3' display Time/Sequence Graph (Stevens)\n"
+ " '4' display Time/Sequence Graph (tcptrace)\n"
+ " '5' display Window Scaling Graph\n"
+ "\n"
+ " <Space bar> toggles crosshairs on/off\n"
+ "\n"
+ " 'i' or '+' zoom in (towards area under mouse pointer)\n"
+ " 'o' or '-' zoom out\n"
+ " 'r' or <Home> restore graph to initial state (zoom out max)\n"
+ " 's' toggles relative/absolute sequence numbers\n"
+ " 't' toggles time origin\n"
+ " 'g' go to frame under cursor in Wireshark's packet list (if possible)\n"
+ "\n"
+ " <Left> move view left by 100 pixels (if zoomed in)\n"
+ " <Right> move view right 100 pixels (if zoomed in)\n"
+ " <Up> move view up by 100 pixels (if zoomed in)\n"
+ " <Down> move view down by 100 pixels (if zoomed in)\n"
+ "\n"
+ " <Shift><Left> move view left by 10 pixels (if zoomed in)\n"
+ " <Shift><Right> move view right 10 pixels (if zoomed in)\n"
+ " <Shift><Up> move view up by 10 pixels (if zoomed in)\n"
+ " <Shift><Down> move view down by 10 pixels (if zoomed in)\n"
+ "\n"
+ " <Ctrl><Left> move view left by 1 pixel (if zoomed in)\n"
+ " <Ctrl><Right> move view right 1 pixel (if zoomed in)\n"
+ " <Ctrl><Up> move view up by 1 pixel (if zoomed in)\n"
+ " <Ctrl><Down> move view down by 1 pixel (if zoomed in)\n"
+;
+
+#if 0
+static void debug_coord (struct graph *g, const char *c)
+{
+ static unsigned count = 0;
+
+ count++;
+ printf("%u: %s\n", count, c);
+ printf("%u: g->geom.width %d\n", count, g->geom.width);
+ printf("%u: g->geom.height %d\n", count, g->geom.height);
+ printf("%u: g->geom.x %d\n", count, g->geom.x);
+ printf("%u: g->geom.y %d\n", count, g->geom.y);
+
+ printf("%u: g->wp.width %d\n", count, g->wp.width);
+ printf("%u: g->wp.height %d\n", count, g->wp.height);
+ printf("%u: g->wp.x %d\n", count, g->wp.x);
+ printf("%u: g->wp.y %d\n", count, g->wp.y);
+ printf("---------------\n");
+}
+#endif
+
+static void set_busy_cursor(GdkWindow *w)
+{
+ GdkCursor* cursor;
+
+ cursor = gdk_cursor_new(GDK_WATCH);
+ gdk_window_set_cursor(w, cursor);
+ gdk_flush();
+ gdk_cursor_unref(cursor);
+}
+
+static void unset_busy_cursor(GdkWindow *w)
+{
+ gdk_window_set_cursor(w, NULL);
+ gdk_flush();
+}
+void tcp_graph_cb (GtkAction *action, gpointer user_data _U_)
+{
+ struct segment current;
+ struct graph *g;
+ const gchar *name;
+ guint graph_type;
+
+ name = gtk_action_get_name (action);
+ if(strcmp(name, "/StatisticsMenu/TCPStreamGraphMenu/Time-Sequence-Graph-Stevens") == 0){
+ graph_type = GRAPH_TSEQ_STEVENS;
+ }else if(strcmp(name, "/StatisticsMenu/TCPStreamGraphMenu/Time-Sequence-Graph-tcptrace") == 0){
+ graph_type = GRAPH_TSEQ_TCPTRACE;
+ }else if(strcmp(name, "/StatisticsMenu/TCPStreamGraphMenu/Throughput-Graph") == 0){
+ graph_type = GRAPH_THROUGHPUT;
+ }else if(strcmp(name, "/StatisticsMenu/TCPStreamGraphMenu/RTT-Graph") == 0){
+ graph_type = GRAPH_RTT;
+ }else if(strcmp(name, "/StatisticsMenu/TCPStreamGraphMenu/Window-Scaling-Graph") == 0){
+ graph_type = GRAPH_WSCALE;
+ }else{
+ return;
+ }
+
+ debug(DBS_FENTRY) puts ("tcp_graph_cb()");
+
+ if (! (g = graph_new()))
+ return;
+
+ refnum++;
+ graph_initialize_values (g);
+
+ g->type = graph_type;
+ if (!select_tcpip_session (&cfile, &current)) {
+ return;
+ }
+
+ graph_segment_list_get(g);
+ create_gui(g);
+ /* display_text(g); */
+ graph_init_sequence(g);
+
+}
+static void create_gui (struct graph *g)
+{
+ debug(DBS_FENTRY) puts ("create_gui()");
+ /* create_text_widget(g); */
+ control_panel_create (g);
+ create_drawing_area(g);
+}
+
+
+
+static void create_drawing_area (struct graph *g)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ GtkStyleContext *context;
+#else
+ GdkColormap *colormap;
+ GdkColor color;
+#endif
+ char window_title[WINDOW_TITLE_LENGTH];
+ struct segment current;
+ struct tcpheader *thdr;
+ GtkAllocation widget_alloc;
+#if 0
+ /* Prep. to include the controls in the graph window */
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+#endif
+ debug(DBS_FENTRY) puts ("create_drawing_area()");
+ thdr=select_tcpip_session (&cfile, &current);
+ g_snprintf (window_title, WINDOW_TITLE_LENGTH, "TCP Graph %d: %s %s:%d -> %s:%d",
+ refnum,
+ cf_get_display_name(&cfile),
+ ep_address_to_str(&(thdr->ip_src)),
+ thdr->th_sport,
+ ep_address_to_str(&(thdr->ip_dst)),
+ thdr->th_dport
+ );
+ g->toplevel = dlg_window_new ("Tcp Graph");
+ gtk_window_set_title(GTK_WINDOW(g->toplevel), window_title);
+ gtk_widget_set_name (g->toplevel, "Test Graph");
+
+ /* Create the drawing area */
+ g->drawing_area = gtk_drawing_area_new ();
+ g->x_axis->drawing_area = g->y_axis->drawing_area = g->drawing_area;
+ gtk_widget_set_size_request (g->drawing_area,
+ g->wp.width + g->wp.x + RMARGIN_WIDTH,
+ g->wp.height + g->wp.y + g->x_axis->s.height);
+ gtk_widget_show (g->drawing_area);
+
+#if GTK_CHECK_VERSION(3,0,0)
+ g_signal_connect(g->drawing_area, "draw", G_CALLBACK(draw_event), g);
+#else
+ g_signal_connect(g->drawing_area, "expose_event", G_CALLBACK(expose_event), g);
+#endif
+ /* this has to be done later, after the widget has been shown */
+ /*
+ g_signal_connect(g->drawing_area,"configure_event", G_CALLBACK(configure_event),
+ g);
+ */
+ g_signal_connect(g->drawing_area, "motion_notify_event",
+ G_CALLBACK(motion_notify_event), g);
+ g_signal_connect(g->drawing_area, "button_press_event",
+ G_CALLBACK(button_press_event), g);
+ g_signal_connect(g->drawing_area, "button_release_event",
+ G_CALLBACK(button_release_event), g);
+ g_signal_connect(g->drawing_area, "leave_notify_event",
+ G_CALLBACK(leave_notify_event), g);
+ g_signal_connect(g->drawing_area, "enter_notify_event",
+ G_CALLBACK(enter_notify_event), g);
+ g_signal_connect(g->toplevel, "destroy", G_CALLBACK(callback_toplevel_destroy), g);
+ /* why doesn't drawing area send key_press_signals? */
+ g_signal_connect(g->toplevel, "key_press_event", G_CALLBACK(key_press_event), g);
+ g_signal_connect(g->toplevel, "key_release_event", G_CALLBACK(key_release_event),
+ g);
+ gtk_widget_set_events(g->toplevel,
+ GDK_KEY_PRESS_MASK|GDK_KEY_RELEASE_MASK);
+
+ gtk_widget_set_events (g->drawing_area,
+ GDK_EXPOSURE_MASK
+ | GDK_LEAVE_NOTIFY_MASK
+ | GDK_ENTER_NOTIFY_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK
+ | GDK_POINTER_MOTION_MASK
+ | GDK_POINTER_MOTION_HINT_MASK);
+
+#if 0
+ /* Prep. to include the controls in the graph window */
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (g->toplevel), vbox);
+ gtk_container_set_border_width (GTK_CONTAINER (g->toplevel), 5);
+ gtk_widget_show (vbox);
+
+ frame = gtk_frame_new (NULL);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
+ gtk_container_add (GTK_CONTAINER (frame), g->drawing_area);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+
+ /*gtk_box_pack_start (GTK_BOX (vbox), g->gui.control_panel, FALSE, FALSE, 0);*/
+
+ hbox=gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
+ gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
+ gtk_widget_show(hbox);
+
+ create_ctrl_area(g, hbox);
+
+#endif
+
+ gtk_container_add (GTK_CONTAINER (g->toplevel), g->drawing_area);
+ gtk_widget_show (g->toplevel);
+
+ /* in case we didn't get what we asked for */
+ gtk_widget_get_allocation(GTK_WIDGET (g->drawing_area), &widget_alloc);
+ g->wp.width = widget_alloc.width - g->wp.x - RMARGIN_WIDTH;
+ g->wp.height = widget_alloc.height - g->wp.y - g->x_axis->s.height;
+
+#if GTK_CHECK_VERSION(3,0,0)
+ context = gtk_widget_get_style_context (g->drawing_area);
+ gtk_style_context_get (context, GTK_STATE_NORMAL,
+ GTK_STYLE_PROPERTY_FONT, &g->font,
+ NULL);
+#else
+ g->font = gtk_widget_get_style(g->drawing_area)->font_desc;
+#endif
+#if !GTK_CHECK_VERSION(3,0,0)
+ colormap = gtk_widget_get_colormap(GTK_WIDGET(g->drawing_area));
+ if (!xor_gc) {
+ xor_gc = gdk_gc_new (gtk_widget_get_window(g->drawing_area));
+ gdk_gc_set_function (xor_gc, GDK_XOR);
+ if (!gdk_color_parse ("gray15", &color)) {
+ /*
+ * XXX - do more than just warn.
+ */
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Could not parse color gray15.");
+ }
+ if (!gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE)) {
+ /*
+ * XXX - do more than just warn.
+ */
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Could not allocate color gray15.");
+ }
+ gdk_gc_set_foreground (xor_gc, &color);
+ }
+
+ /* this is probably quite an ugly way to get rid of the first configure
+ * event
+ * immediately after gtk_widget_show (window) drawing_area gets a configure
+ * event which is handled during the next return to gtk_main which is
+ * probably the gdk_gc_new() call. configure handler calls
+ * graph_element_lists_make() which is not good because the graph struct is
+ * not fully set up yet - namely we're not sure about actual geometry
+ * and we don't have the GC's at all. so we just postpone installation
+ * of configure handler until we're ready to deal with it.
+ *
+ * !!! NEMLLO BY TO BYT NA KONCI graph_init_sequence()? !!!
+ *
+ */
+#endif
+ g_signal_connect(g->drawing_area, "configure_event", G_CALLBACK(configure_event),
+ g);
+
+ /* puts ("exiting create_drawing_area()"); */
+}
+
+static void callback_toplevel_destroy (GtkWidget *widget _U_, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+
+ if (!(g->flags & GRAPH_DESTROYED)) {
+ g->flags |= GRAPH_DESTROYED;
+ graph_destroy ((struct graph * )data);
+ }
+}
+
+static void control_panel_create (struct graph *g)
+{
+ GtkWidget *toplevel, *notebook;
+ GtkWidget *table;
+ GtkWidget *help_bt, *close_bt, *bbox;
+ char window_title[WINDOW_TITLE_LENGTH];
+
+ debug(DBS_FENTRY) puts ("control_panel_create()");
+
+ notebook = gtk_notebook_new ();
+ control_panel_add_zoom_page (g, notebook);
+ control_panel_add_magnify_page (g, notebook);
+ control_panel_add_origin_page (g, notebook);
+ control_panel_add_cross_page (g, notebook);
+ control_panel_add_graph_type_page (g, notebook);
+
+ g_snprintf (window_title, WINDOW_TITLE_LENGTH,
+ "Graph %d - Control - Wireshark", refnum);
+ toplevel = dlg_window_new ("tcp-graph-control");
+ gtk_window_set_title(GTK_WINDOW(toplevel), window_title);
+
+ table = gtk_table_new (2, 1, FALSE);
+ gtk_container_add (GTK_CONTAINER (toplevel), table);
+
+ gtk_table_attach (GTK_TABLE (table), notebook, 0, 1, 0, 1,
+ GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_HELP, GTK_STOCK_CLOSE, NULL);
+ gtk_table_attach (GTK_TABLE (table), bbox, 0, 1, 1, 2,
+ GTK_FILL|GTK_EXPAND, GTK_FILL, 5, 5);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(callback_create_help), g);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(toplevel, close_bt, NULL);
+ g_signal_connect(close_bt, "clicked", G_CALLBACK(callback_close), g);
+
+ g_signal_connect(toplevel, "delete_event", G_CALLBACK(callback_delete_event), g);
+ g_signal_connect(toplevel, "destroy", G_CALLBACK(callback_toplevel_destroy), g);
+
+ /* gtk_widget_show_all (table); */
+ /* g->gui.control_panel = table; */
+ gtk_widget_show_all (toplevel);
+ window_present(toplevel);
+
+ g->gui.control_panel = toplevel;
+}
+
+static void control_panel_add_zoom_page (struct graph *g, GtkWidget *n)
+{
+ GtkWidget *zoom_frame;
+ GtkWidget *zoom_lock_frame;
+ GtkWidget *label;
+ GtkWidget *box;
+
+ zoom_frame = control_panel_create_zoom_group (g);
+ gtk_container_set_border_width (GTK_CONTAINER (zoom_frame), 5);
+ zoom_lock_frame = control_panel_create_zoomlock_group (g);
+ gtk_container_set_border_width (GTK_CONTAINER (zoom_lock_frame), 5);
+ box = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (box), zoom_frame, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (box), zoom_lock_frame, TRUE, TRUE, 0);
+ gtk_widget_show (box);
+ label = gtk_label_new ("Zoom");
+ gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label);
+}
+
+static void control_panel_add_magnify_page (struct graph *g, GtkWidget *n)
+{
+ GtkWidget *mag_frame, *label;
+
+ mag_frame = control_panel_create_magnify_group (g);
+ gtk_container_set_border_width (GTK_CONTAINER (mag_frame), 5);
+ label = gtk_label_new ("Magnify");
+ gtk_notebook_append_page (GTK_NOTEBOOK (n), mag_frame, label);
+}
+
+static void control_panel_add_origin_page (struct graph *g, GtkWidget *n)
+{
+ GtkWidget *time_orig_cap, *time_orig_conn, *time_orig_box, *time_orig_frame;
+ GtkWidget *seq_orig_isn, *seq_orig_zero, *seq_orig_box, *seq_orig_frame;
+ GtkWidget *box, *label;
+
+ /* time origin box */
+ time_orig_cap =
+ gtk_radio_button_new_with_label (NULL, "beginning of capture");
+ time_orig_conn = gtk_radio_button_new_with_label (
+ gtk_radio_button_get_group (GTK_RADIO_BUTTON (time_orig_cap)),
+ "beginning of this TCP connection");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (time_orig_conn), TRUE);
+ time_orig_box = gtk_vbox_new (TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_conn, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (time_orig_box), time_orig_cap, TRUE, TRUE, 0);
+ time_orig_frame = gtk_frame_new ("Time origin");
+ gtk_container_set_border_width (GTK_CONTAINER (time_orig_frame), 5);
+ gtk_container_add (GTK_CONTAINER (time_orig_frame), time_orig_box);
+
+ /* sequence number origin group */
+ seq_orig_isn =
+ gtk_radio_button_new_with_label (NULL, "initial sequence number");
+ seq_orig_zero = gtk_radio_button_new_with_label (gtk_radio_button_get_group (
+ GTK_RADIO_BUTTON (seq_orig_isn)), "0 (=absolute)");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (seq_orig_isn), TRUE);
+ seq_orig_box = gtk_vbox_new (TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_isn, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (seq_orig_box), seq_orig_zero, TRUE, TRUE, 0);
+ seq_orig_frame = gtk_frame_new ("Sequence number origin");
+ gtk_container_set_border_width (GTK_CONTAINER (seq_orig_frame), 5);
+ gtk_container_add (GTK_CONTAINER (seq_orig_frame), seq_orig_box);
+
+ g->gui.time_orig_conn = (GtkToggleButton * )time_orig_conn;
+ g->gui.seq_orig_isn = (GtkToggleButton * )seq_orig_isn;
+
+ g_signal_connect(time_orig_conn, "toggled", G_CALLBACK(callback_time_origin), g);
+ g_signal_connect(seq_orig_isn, "toggled", G_CALLBACK(callback_seq_origin), g);
+
+ box = gtk_vbox_new (FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (box), 5);
+ gtk_box_pack_start (GTK_BOX (box), time_orig_frame, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (box), seq_orig_frame, TRUE, TRUE, 0);
+ gtk_widget_show (box);
+ label = gtk_label_new ("Origin");
+ gtk_notebook_append_page (GTK_NOTEBOOK (n), box, label);
+}
+
+static void control_panel_add_cross_page (struct graph *g, GtkWidget *n)
+{
+ GtkWidget *cross_frame, *label;
+
+ cross_frame = control_panel_create_cross_group (g);
+ gtk_container_set_border_width (GTK_CONTAINER (cross_frame), 5);
+ label = gtk_label_new ("Cross");
+ gtk_notebook_append_page (GTK_NOTEBOOK (n), cross_frame, label);
+}
+
+static void control_panel_add_graph_type_page (struct graph *g, GtkWidget *n)
+{
+ GtkWidget *frame, *label;
+
+ frame = control_panel_create_graph_type_group (g);
+ gtk_container_set_border_width (GTK_CONTAINER (frame), 5);
+ label = gtk_label_new ("Graph type");
+ gtk_notebook_append_page (GTK_NOTEBOOK (n), frame, label);
+}
+
+/* Treat this as a cancel, by calling "callback_close()" */
+static gboolean
+callback_delete_event(GtkWidget *widget _U_, GdkEvent *event _U_,
+ gpointer data)
+{
+ callback_close(NULL, data);
+ return FALSE;
+}
+
+static void callback_close (GtkWidget *widget _U_, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+
+ if (!(g->flags & GRAPH_DESTROYED)) {
+ g->flags |= GRAPH_DESTROYED;
+ graph_destroy ((struct graph * )data);
+ }
+}
+
+static void callback_create_help(GtkWidget *widget _U_, gpointer data _U_)
+{
+ GtkWidget *toplevel, *vbox, *text, *scroll, *bbox, *close_bt;
+ GtkTextBuffer *buf;
+
+ toplevel = dlg_window_new ("Help for TCP graphing");
+ gtk_window_set_default_size(GTK_WINDOW(toplevel), 500, 400);
+
+ vbox = gtk_vbox_new (FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+ gtk_container_add (GTK_CONTAINER (toplevel), vbox);
+
+ scroll = scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
+ GTK_SHADOW_IN);
+ gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0);
+ text = gtk_text_view_new();
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
+ buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
+ gtk_text_buffer_set_text(buf, helptext, -1);
+ gtk_container_add (GTK_CONTAINER (scroll), text);
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(toplevel, close_bt, window_cancel_button_cb);
+
+ g_signal_connect(toplevel, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+
+ gtk_widget_show_all (toplevel);
+ window_present(toplevel);
+}
+
+static void callback_time_origin (GtkWidget *toggle _U_, gpointer data)
+{
+ toggle_time_origin ((struct graph * )data);
+}
+
+static void callback_seq_origin (GtkWidget *toggle _U_, gpointer data)
+{
+ toggle_seq_origin ((struct graph * )data);
+}
+
+static GtkWidget *control_panel_create_zoom_group (struct graph *g)
+{
+ GtkWidget *zoom_in, *zoom_out, *zoom_box, *zoom_frame;
+ GtkAdjustment *zoom_h_adj, *zoom_v_adj;
+ GtkWidget *zoom_inout_box, *zoom_h_step_label, *zoom_h_step;
+ GtkWidget *zoom_v_step_label, *zoom_v_step;
+ GtkWidget *zoom_separator1, *zoom_separator2, *zoom_step_table, *zoom_table;
+ GtkWidget *zoom_ratio_toggle, *zoom_same_toggle;
+ GtkWidget *zoom_h_entry, *zoom_v_entry;
+ GtkWidget *zoom_h_label, *zoom_v_label;
+
+ zoom_in = gtk_radio_button_new_with_label (NULL, "in");
+ zoom_out = gtk_radio_button_new_with_label (
+ gtk_radio_button_get_group (GTK_RADIO_BUTTON (zoom_in)), "out");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_in), TRUE);
+ zoom_inout_box = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_in, FALSE, FALSE, 10);
+ gtk_box_pack_start (GTK_BOX (zoom_inout_box), zoom_out, FALSE, FALSE, 0);
+
+ zoom_separator1 = gtk_hseparator_new ();
+
+ zoom_h_entry = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (zoom_h_entry), "1.000");
+ gtk_editable_set_editable (GTK_EDITABLE (zoom_h_entry), FALSE);
+ zoom_h_label = gtk_label_new ("Horizontal:");
+
+ zoom_v_entry = gtk_entry_new ();
+ gtk_entry_set_text (GTK_ENTRY (zoom_v_entry), "1.000");
+ gtk_editable_set_editable (GTK_EDITABLE (zoom_v_entry), FALSE);
+ zoom_v_label = gtk_label_new ("Vertical:");
+
+ g->zoom.widget.h_zoom = (GtkEntry * )zoom_h_entry;
+ g->zoom.widget.v_zoom = (GtkEntry * )zoom_v_entry;
+
+ zoom_table = gtk_table_new (2, 2, FALSE);
+ gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_label, 0,1,0,1,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (zoom_table), zoom_h_entry, 1, 2, 0, 1,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_label, 0,1,1,2,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (zoom_table), zoom_v_entry, 1, 2, 1, 2,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+
+ zoom_separator2 = gtk_hseparator_new ();
+
+ zoom_h_adj = (GtkAdjustment * )gtk_adjustment_new ((gfloat)1.2, 1.0, 5, (gfloat)0.1, 1, 0);
+ zoom_h_step = gtk_spin_button_new (zoom_h_adj, 0, 1);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_h_step), TRUE);
+ zoom_h_step_label = gtk_label_new ("Horizontal step:");
+
+ zoom_v_adj = (GtkAdjustment * )gtk_adjustment_new ((gfloat)1.2, 1.0, 5, (gfloat)0.1, 1, 0);
+ zoom_v_step = gtk_spin_button_new (zoom_v_adj, 0, 1);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (zoom_v_step), TRUE);
+ zoom_v_step_label = gtk_label_new ("Vertical step:");
+
+ g->zoom.widget.h_step = (GtkSpinButton * )zoom_h_step;
+ g->zoom.widget.v_step = (GtkSpinButton * )zoom_v_step;
+
+ zoom_same_toggle = gtk_check_button_new_with_label("Keep them the same");
+ zoom_ratio_toggle = gtk_check_button_new_with_label("Preserve their ratio");
+ g_object_set_data(G_OBJECT(zoom_same_toggle), "flag", (gpointer)ZOOM_STEPS_SAME);
+ g_object_set_data(G_OBJECT(zoom_ratio_toggle), "flag",
+ (gpointer)ZOOM_STEPS_KEEP_RATIO);
+ g_signal_connect(zoom_same_toggle, "clicked", G_CALLBACK(callback_zoom_flags), g);
+ g_signal_connect(zoom_ratio_toggle, "clicked", G_CALLBACK(callback_zoom_flags), g);
+
+ zoom_step_table = gtk_table_new (4, 2, FALSE);
+ gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step_label, 0,1,0,1,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_h_step, 1, 2, 0, 1,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step_label, 0,1,1,2,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_v_step, 1, 2, 1, 2,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_same_toggle, 0,2,2,3,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (zoom_step_table), zoom_ratio_toggle, 0,2,3,4,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+
+ zoom_box = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (zoom_box), zoom_inout_box, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator1, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (zoom_box), zoom_table, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (zoom_box), zoom_separator2, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (zoom_box), zoom_step_table, TRUE, TRUE, 0);
+ zoom_frame = gtk_frame_new ("Zoom");
+ gtk_container_add (GTK_CONTAINER (zoom_frame), zoom_box);
+
+ g_object_set_data(G_OBJECT(zoom_h_step), "direction", GINT_TO_POINTER(0));
+ g_object_set_data(G_OBJECT(zoom_v_step), "direction", GINT_TO_POINTER(1));
+
+ g_signal_connect(zoom_in, "toggled", G_CALLBACK(callback_zoom_inout), g);
+ g_signal_connect(zoom_h_step, "changed", G_CALLBACK(callback_zoom_step), g);
+ g_signal_connect(zoom_v_step, "changed", G_CALLBACK(callback_zoom_step), g);
+
+ g->zoom.widget.in_toggle = (GtkToggleButton * )zoom_in;
+ g->zoom.widget.out_toggle = (GtkToggleButton * )zoom_out;
+ return zoom_frame;
+}
+
+static void callback_zoom_inout (GtkWidget *toggle, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
+ g->zoom.flags &= ~ZOOM_OUT;
+ else
+ g->zoom.flags |= ZOOM_OUT;
+}
+
+static void callback_zoom_step (GtkWidget *spin, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+ double value;
+ int direction;
+ double *zoom_this, *zoom_other;
+ GtkSpinButton *widget_this, *widget_other;
+ double old_this;
+
+ direction = (long)g_object_get_data(G_OBJECT(spin), "direction");
+ value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin));
+
+ if (direction) {
+ zoom_this = &g->zoom.step_y;
+ zoom_other = &g->zoom.step_x;
+ widget_this = g->zoom.widget.v_step;
+ widget_other = g->zoom.widget.h_step;
+ } else {
+ zoom_this = &g->zoom.step_x;
+ zoom_other = &g->zoom.step_y;
+ widget_this = g->zoom.widget.h_step;
+ widget_other = g->zoom.widget.v_step;
+ }
+
+ old_this = *zoom_this;
+ *zoom_this = value;
+ if (g->zoom.flags & ZOOM_STEPS_SAME) {
+ *zoom_other = value;
+ gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
+ } else if (g->zoom.flags & ZOOM_STEPS_KEEP_RATIO) {
+ double old_other = *zoom_other;
+ *zoom_other *= value / old_this;
+ if (*zoom_other < 1.0) {
+ *zoom_other = 1.0;
+ *zoom_this = old_this * 1.0 / old_other;
+ gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
+ } else if (*zoom_other > 5.0) {
+ *zoom_other = 5.0;
+ *zoom_this = old_this * 5.0 / old_other;
+ gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
+ }
+ gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
+ }
+}
+
+static void callback_zoom_flags (GtkWidget *toggle, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+ int flag = (long)g_object_get_data(G_OBJECT(toggle), "flag");
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
+ g->zoom.flags |= flag;
+ else
+ g->zoom.flags &= ~flag;
+}
+
+static void update_zoom_spins (struct graph *g)
+{
+ char s[32];
+
+ g_snprintf (s, sizeof(s), "%.3f", g->zoom.x / g->zoom.initial.x);
+ gtk_entry_set_text (g->zoom.widget.h_zoom, s);
+ g_snprintf (s, sizeof(s), "%.3f", g->zoom.y / g->zoom.initial.y);
+ gtk_entry_set_text (g->zoom.widget.v_zoom, s);
+}
+
+static GtkWidget *control_panel_create_magnify_group (struct graph *g)
+{
+ GtkWidget *mag_width_label, *mag_width;
+ GtkWidget *mag_height_label, *mag_height;
+ GtkWidget *mag_x_label, *mag_x;
+ GtkWidget *mag_y_label, *mag_y;
+ GtkWidget *mag_wh_table, *mag_zoom_frame, *mag_zoom_table;
+ GtkWidget *mag_h_zoom_label, *mag_h_zoom;
+ GtkWidget *mag_v_zoom_label, *mag_v_zoom;
+ GtkWidget *mag_zoom_same, *mag_zoom_ratio;
+ GtkAdjustment *mag_width_adj, *mag_height_adj, *mag_x_adj, *mag_y_adj;
+ GtkAdjustment *mag_h_zoom_adj, *mag_v_zoom_adj;
+ GtkWidget *mag_box, *mag_frame;
+
+ mag_width_label = gtk_label_new ("Width:");
+ mag_width_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
+ mag_width = gtk_spin_button_new (mag_width_adj, 0, 0);
+
+ mag_height_label = gtk_label_new ("Height:");
+ mag_height_adj = (GtkAdjustment * )gtk_adjustment_new (250,100,600,1,10,0);
+ mag_height = gtk_spin_button_new (mag_height_adj, 0, 0);
+
+ mag_x_label = gtk_label_new ("X:");
+ mag_x_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
+ mag_x = gtk_spin_button_new (mag_x_adj, 0, 0);
+
+ mag_y_label = gtk_label_new ("Y:");
+ mag_y_adj = (GtkAdjustment * )gtk_adjustment_new (0,-1000,1000,1,10,0);
+ mag_y = gtk_spin_button_new (mag_y_adj, 0, 0);
+
+ mag_wh_table = gtk_table_new (4, 2, FALSE);
+ gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width_label, 0,1,0,1,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (mag_wh_table), mag_width, 1,2,0,1,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height_label, 0,1,1,2,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (mag_wh_table), mag_height, 1,2,1,2,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x_label, 0,1,2,3,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (mag_wh_table), mag_x, 1,2,2,3,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y_label, 0,1,3,4,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+ gtk_table_attach (GTK_TABLE (mag_wh_table), mag_y, 1,2,3,4,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 5, 0);
+
+ mag_h_zoom_label = gtk_label_new ("Horizontal:");
+ mag_h_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0, 1.0, 25.0, (gfloat)0.1, 1, 0);
+ mag_h_zoom = gtk_spin_button_new (mag_h_zoom_adj, 0, 1);
+
+ mag_v_zoom_label = gtk_label_new ("Vertical:");
+ mag_v_zoom_adj = (GtkAdjustment *)gtk_adjustment_new(10.0, 1.0, 25.0, (gfloat)0.1, 1, 0);
+ mag_v_zoom = gtk_spin_button_new (mag_v_zoom_adj, 0, 1);
+
+ mag_zoom_same = gtk_check_button_new_with_label ("Keep them the same");
+ mag_zoom_ratio = gtk_check_button_new_with_label("Preserve their ratio");
+
+ mag_zoom_table = gtk_table_new (4, 2, FALSE);
+ gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom_label, 0,1,0,1,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+ gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_h_zoom, 1,2,0,1,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+ gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom_label, 0,1,1,2,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+ gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_v_zoom, 1,2,1,2,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+ gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_same, 0,2,2,3,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+ gtk_table_attach (GTK_TABLE (mag_zoom_table), mag_zoom_ratio, 0,2,3,4,
+ GTK_FILL|GTK_EXPAND, GTK_FILL|GTK_EXPAND, 0, 0);
+
+ mag_zoom_frame = gtk_frame_new ("Magnify zoom");
+ gtk_container_add (GTK_CONTAINER (mag_zoom_frame), mag_zoom_table);
+ gtk_container_set_border_width (GTK_CONTAINER (mag_zoom_frame), 3);
+
+ mag_box = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (mag_box), mag_wh_table, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (mag_box), mag_zoom_frame, TRUE, TRUE, 0);
+ mag_frame = gtk_frame_new ("Magnify");
+ gtk_container_add (GTK_CONTAINER (mag_frame), mag_box);
+
+ g->magnify.widget.h_zoom = (GtkSpinButton * )mag_h_zoom;
+ g->magnify.widget.v_zoom = (GtkSpinButton * )mag_v_zoom;
+ g_object_set_data(G_OBJECT(mag_h_zoom), "direction", GINT_TO_POINTER(0));
+ g_object_set_data(G_OBJECT(mag_v_zoom), "direction", GINT_TO_POINTER(1));
+ g_object_set_data(G_OBJECT(mag_zoom_same), "flag", (gpointer)MAGZOOMS_SAME);
+ g_object_set_data(G_OBJECT(mag_zoom_ratio), "flag", (gpointer)MAGZOOMS_SAME_RATIO);
+
+ g_signal_connect(mag_width, "changed", G_CALLBACK(callback_mag_width), g);
+ g_signal_connect(mag_height, "changed", G_CALLBACK(callback_mag_height), g);
+ g_signal_connect(mag_x, "changed", G_CALLBACK(callback_mag_x), g);
+ g_signal_connect(mag_y, "changed", G_CALLBACK(callback_mag_y), g);
+ g_signal_connect(mag_h_zoom, "changed", G_CALLBACK(callback_mag_zoom), g);
+ g_signal_connect(mag_v_zoom, "changed", G_CALLBACK(callback_mag_zoom), g);
+ g_signal_connect(mag_zoom_same, "clicked", G_CALLBACK(callback_mag_flags), g);
+ g_signal_connect(mag_zoom_ratio, "clicked", G_CALLBACK(callback_mag_flags), g);
+
+ return mag_frame;
+}
+
+static void callback_mag_width (GtkWidget *spin, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+
+ g->magnify.width = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin));
+}
+
+static void callback_mag_height (GtkWidget *spin, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+
+ g->magnify.height = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
+}
+
+static void callback_mag_x (GtkWidget *spin, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+
+ g->magnify.offset.x=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
+}
+
+static void callback_mag_y (GtkWidget *spin, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+
+ g->magnify.offset.y=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
+}
+
+static void callback_mag_zoom (GtkWidget *spin, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+ double value;
+ int direction;
+ double *zoom_this, *zoom_other;
+ GtkSpinButton *widget_this, *widget_other;
+ double old_this;
+
+ if (g->magnify.flags & MAGZOOMS_IGNORE) {
+ printf ("refusing callback for %s zoom widget.\n", (GtkSpinButton * )spin==g->magnify.widget.h_zoom ? "horizontal" : "vertical");
+ g->magnify.flags &= ~MAGZOOMS_IGNORE;
+ return;
+ }
+ direction = (long)g_object_get_data(G_OBJECT(spin), "direction");
+ value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin));
+
+ if (direction) {
+ zoom_this = &g->magnify.zoom.y;
+ zoom_other = &g->magnify.zoom.x;
+ widget_this = g->magnify.widget.v_zoom;
+ widget_other = g->magnify.widget.h_zoom;
+ } else {
+ zoom_this = &g->magnify.zoom.x;
+ zoom_other = &g->magnify.zoom.y;
+ widget_this = g->magnify.widget.h_zoom;
+ widget_other = g->magnify.widget.v_zoom;
+ }
+
+ old_this = *zoom_this;
+ *zoom_this = value;
+ if (g->magnify.flags & MAGZOOMS_SAME) {
+ *zoom_other = value;
+ /* g->magnify.flags |= MAGZOOMS_IGNORE; */
+ gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
+ } else if (g->magnify.flags & MAGZOOMS_SAME_RATIO) {
+ double old_other = *zoom_other;
+ *zoom_other *= value / old_this;
+ if (*zoom_other < 1.0) {
+ *zoom_other = 1.0;
+ *zoom_this = old_this * 1.0 / old_other;
+ /* g->magnify.flags |= MAGZOOMS_IGNORE; */
+ gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
+ } else if (*zoom_other > 25.0) {
+ *zoom_other = 25.0;
+ *zoom_this = old_this * 25.0 / old_other;
+ /* g->magnify.flags |= MAGZOOMS_IGNORE; */
+ gtk_spin_button_set_value (widget_this, (gfloat) *zoom_this);
+ }
+ /* g->magnify.flags |= MAGZOOMS_IGNORE; */
+ gtk_spin_button_set_value (widget_other, (gfloat) *zoom_other);
+ }
+}
+
+static void callback_mag_flags (GtkWidget *toggle, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+ int flag = (long)g_object_get_data(G_OBJECT(toggle), "flag");
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
+ g->magnify.flags |= flag;
+ else
+ g->magnify.flags &= ~flag;
+}
+
+static GtkWidget *control_panel_create_zoomlock_group (struct graph *g)
+{
+ GtkWidget *zoom_lock_h, *zoom_lock_v, *zoom_lock_none, *zoom_lock_box;
+ GtkWidget *zoom_lock_frame;
+
+ zoom_lock_none = gtk_radio_button_new_with_label (NULL, "none");
+ zoom_lock_h = gtk_radio_button_new_with_label (
+ gtk_radio_button_get_group (GTK_RADIO_BUTTON (zoom_lock_none)),
+ "horizontal");
+ zoom_lock_v = gtk_radio_button_new_with_label (
+ gtk_radio_button_get_group (GTK_RADIO_BUTTON (zoom_lock_none)),
+ "vertical");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (zoom_lock_none), TRUE);
+ zoom_lock_box = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_none,
+ TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_h, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(zoom_lock_box), zoom_lock_v, TRUE, TRUE, 0);
+ zoom_lock_frame = gtk_frame_new ("Zoom lock:");
+ gtk_container_add (GTK_CONTAINER (zoom_lock_frame), zoom_lock_box);
+
+ g_signal_connect(zoom_lock_h, "toggled", G_CALLBACK(callback_zoomlock_h), g);
+ g_signal_connect(zoom_lock_v, "toggled", G_CALLBACK(callback_zoomlock_v), g);
+
+ return zoom_lock_frame;
+}
+
+static void callback_zoomlock_h (GtkWidget *toggle, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
+ g->zoom.flags |= ZOOM_HLOCK;
+ else
+ g->zoom.flags &= ~ZOOM_HLOCK;
+}
+
+static void callback_zoomlock_v (GtkWidget *toggle, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
+ g->zoom.flags |= ZOOM_VLOCK;
+ else
+ g->zoom.flags &= ~ZOOM_VLOCK;
+}
+
+static GtkWidget *control_panel_create_cross_group (struct graph *g)
+{
+ GtkWidget *on, *off, *box, *frame, *vbox, *label;
+
+ label = gtk_label_new ("Crosshairs:");
+ off = gtk_radio_button_new_with_label (NULL, "off");
+ on = gtk_radio_button_new_with_label (
+ gtk_radio_button_get_group (GTK_RADIO_BUTTON (off)), "on");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (off), TRUE);
+ box = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 10);
+ gtk_box_pack_start (GTK_BOX (box), off, FALSE, FALSE, 10);
+ gtk_box_pack_start (GTK_BOX (box), on, FALSE, FALSE, 0);
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 15);
+ /* frame = gtk_frame_new ("Cross:"); */
+ frame = gtk_frame_new (NULL);
+ gtk_container_add (GTK_CONTAINER (frame), vbox);
+
+ g_signal_connect(on, "toggled", G_CALLBACK(callback_cross_on_off), g);
+
+ g->cross.on_toggle = (GtkToggleButton * )on;
+ g->cross.off_toggle = (GtkToggleButton * )off;
+
+ return frame;
+}
+
+static void callback_cross_on_off (GtkWidget *toggle, gpointer data)
+{
+ struct graph *g = (struct graph * )data;
+
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle))) {
+ int x, y;
+ g->cross.draw = TRUE;
+ gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &x, &y, 0);
+ cross_draw (g, x, y);
+ } else {
+ g->cross.draw = FALSE;
+ cross_erase (g);
+ }
+}
+
+static GtkWidget *control_panel_create_graph_type_group (struct graph *g)
+{
+ GtkWidget *graph_tseqttrace, *graph_tseqstevens;
+ GtkWidget *graph_tput, *graph_rtt, *graph_sep, *graph_init, *graph_box;
+ GtkWidget *graph_frame;
+ GtkWidget *graph_wscale;
+
+ graph_tput = gtk_radio_button_new_with_label (NULL, "Throughput");
+ graph_tseqttrace = gtk_radio_button_new_with_label (
+ gtk_radio_button_get_group (GTK_RADIO_BUTTON (graph_tput)),
+ "Time/Sequence (tcptrace-style)");
+ graph_tseqstevens = gtk_radio_button_new_with_label (
+ gtk_radio_button_get_group (GTK_RADIO_BUTTON (graph_tput)),
+ "Time/Sequence (Stevens'-style)");
+ graph_rtt = gtk_radio_button_new_with_label (
+ gtk_radio_button_get_group (GTK_RADIO_BUTTON (graph_tput)),
+ "Round-trip Time");
+ graph_wscale = gtk_radio_button_new_with_label (
+ gtk_radio_button_get_group (GTK_RADIO_BUTTON (graph_tput)),
+ "Window Scaling");
+
+ switch (g->type) {
+ case GRAPH_TSEQ_STEVENS:
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(graph_tseqstevens),TRUE);
+ break;
+ case GRAPH_TSEQ_TCPTRACE:
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(graph_tseqttrace),TRUE);
+ break;
+ case GRAPH_THROUGHPUT:
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_tput), TRUE);
+ break;
+ case GRAPH_RTT:
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_rtt), TRUE);
+ break;
+ case GRAPH_WSCALE:
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (graph_wscale), TRUE);
+ break;
+ }
+ graph_init = gtk_check_button_new_with_label ("Init on change");
+ graph_sep = gtk_hseparator_new ();
+ graph_box = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (graph_box), graph_rtt, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (graph_box), graph_tput, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqstevens, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (graph_box), graph_tseqttrace, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (graph_box), graph_wscale, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (graph_box), graph_sep, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (graph_box), graph_init, TRUE, TRUE, 0);
+ graph_frame = gtk_frame_new ("Graph type:");
+ gtk_container_add (GTK_CONTAINER (graph_frame), graph_box);
+
+ g_object_set_data(G_OBJECT(graph_tseqstevens), "new-graph-type",
+ GINT_TO_POINTER(0));
+ g_object_set_data(G_OBJECT(graph_tseqttrace), "new-graph-type", GINT_TO_POINTER(1));
+ g_object_set_data(G_OBJECT(graph_tput), "new-graph-type", GINT_TO_POINTER(2));
+ g_object_set_data(G_OBJECT(graph_rtt), "new-graph-type", GINT_TO_POINTER(3));
+ g_object_set_data(G_OBJECT(graph_wscale), "new-graph-type", GINT_TO_POINTER(GRAPH_WSCALE));
+
+ g->gt.graph_wscale = (GtkToggleButton *)graph_wscale;
+ g->gt.graph_rtt = (GtkToggleButton * )graph_rtt;
+ g->gt.graph_tput = (GtkToggleButton * )graph_tput;
+ g->gt.graph_tseqstevens = (GtkToggleButton * )graph_tseqstevens;
+ g->gt.graph_tseqttrace = (GtkToggleButton * )graph_tseqttrace;
+
+ g_signal_connect(graph_tseqttrace, "toggled", G_CALLBACK(callback_graph_type), g);
+ g_signal_connect(graph_tseqstevens, "toggled", G_CALLBACK(callback_graph_type), g);
+ g_signal_connect(graph_tput, "toggled", G_CALLBACK(callback_graph_type), g);
+ g_signal_connect(graph_rtt, "toggled", G_CALLBACK(callback_graph_type), g);
+ g_signal_connect(graph_wscale, "toggled", G_CALLBACK(callback_graph_type), g);
+ g_signal_connect(graph_init, "toggled", G_CALLBACK(callback_graph_init_on_typechg), g);
+
+ return graph_frame;
+}
+
+static void callback_graph_type (GtkWidget *toggle, gpointer data)
+{
+ int old_type, new_type;
+ struct graph *g = (struct graph * )data;
+
+ new_type = (long)g_object_get_data(G_OBJECT(toggle),"new-graph-type");
+
+ if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (toggle)))
+ return;
+
+ old_type = g->type;
+ g->type = new_type;
+
+ graph_element_lists_free (g);
+ graph_element_lists_initialize (g);
+
+ if (old_type == GRAPH_THROUGHPUT || new_type == GRAPH_THROUGHPUT) {
+ /* throughput graph uses differently constructed segment list so we
+ * need to recreate it */
+ graph_segment_list_free (g);
+ graph_segment_list_get (g);
+ }
+
+ if (g->flags & GRAPH_INIT_ON_TYPE_CHANGE) {
+ g->geom.width = g->wp.width;
+ g->geom.height = g->wp.height;
+ g->geom.x = g->wp.x;
+ g->geom.y = g->wp.y;
+ }
+ g->x_axis->min = g->y_axis->min = 0;
+ gtk_toggle_button_set_active (g->gui.time_orig_conn, TRUE);
+ gtk_toggle_button_set_active (g->gui.seq_orig_isn, TRUE);
+ graph_init_sequence (g);
+}
+
+static void callback_graph_init_on_typechg (GtkWidget *toggle _U_, gpointer data)
+{
+ ((struct graph * )data)->flags ^= GRAPH_INIT_ON_TYPE_CHANGE;
+}
+
+static struct graph *graph_new (void)
+{
+ struct graph *g;
+
+ g = (struct graph * )g_malloc0 (sizeof (struct graph));
+ graph_element_lists_initialize (g);
+
+ g->x_axis = (struct axis * )g_malloc0 (sizeof (struct axis));
+ g->y_axis = (struct axis * )g_malloc0 (sizeof (struct axis));
+ g->x_axis->g = g;
+ g->x_axis->flags = 0;
+ g->x_axis->flags |= AXIS_ORIENTATION;
+ g->x_axis->s.x = g->x_axis->s.y = 0;
+ g->x_axis->s.height = HAXIS_INIT_HEIGHT;
+ g->x_axis->p.x = VAXIS_INIT_WIDTH;
+ g->x_axis->p.height = HAXIS_INIT_HEIGHT;
+ g->y_axis->g = g;
+ g->y_axis->flags = 0;
+ g->y_axis->flags &= ~AXIS_ORIENTATION;
+ g->y_axis->p.x = g->y_axis->p.y = 0;
+ g->y_axis->p.width = VAXIS_INIT_WIDTH;
+ g->y_axis->s.x = 0;
+ g->y_axis->s.y = TITLEBAR_HEIGHT;
+ g->y_axis->s.width = VAXIS_INIT_WIDTH;
+
+ return g;
+}
+
+static void graph_initialize_values (struct graph *g)
+{
+ g->geom.width = g->wp.width = 750;
+ g->geom.height = g->wp.height = 550;
+ g->geom.x = g->wp.x = VAXIS_INIT_WIDTH;
+ g->geom.y = g->wp.y = TITLEBAR_HEIGHT;
+ g->flags = 0;
+ /* g->zoom.x = g->zoom.y = 1.0; */
+ g->zoom.step_x = g->zoom.step_y = 1.2;
+ g->zoom.flags = 0;
+ g->cross.draw = g->cross.erase_needed = 0;
+ g->grab.grabbed = 0;
+ g->magnify.active = 0;
+ g->magnify.offset.x = g->magnify.offset.y = 0;
+ g->magnify.width = g->magnify.height = 250;
+ g->magnify.zoom.x = g->magnify.zoom.y = 10.0;
+ g->magnify.flags = 0;
+}
+
+static void graph_init_sequence (struct graph *g)
+{
+ debug(DBS_FENTRY) puts ("graph_init_sequence()");
+
+ graph_type_dependent_initialize (g);
+ g->zoom.initial.x = g->zoom.x;
+ g->zoom.initial.y = g->zoom.y;
+ graph_element_lists_make (g);
+ g->x_axis->s.width = g->wp.width;
+ g->x_axis->p.width = g->x_axis->s.width + RMARGIN_WIDTH;
+ g->x_axis->p.y = TITLEBAR_HEIGHT + g->wp.height;
+ g->x_axis->s.height = g->x_axis->p.height = HAXIS_INIT_HEIGHT;
+ g->y_axis->s.height = g->wp.height;
+ g->y_axis->p.height = g->wp.height + TITLEBAR_HEIGHT;
+ graph_pixmaps_create (g);
+ axis_pixmaps_create (g->y_axis);
+ axis_pixmaps_create (g->x_axis);
+ graph_title_pixmap_create (g);
+ graph_title_pixmap_draw (g);
+ graph_title_pixmap_display (g);
+ graph_display (g);
+ axis_display (g->y_axis);
+ axis_display (g->x_axis);
+}
+
+static void graph_type_dependent_initialize (struct graph *g)
+{
+ switch (g->type) {
+ case GRAPH_TSEQ_STEVENS:
+ case GRAPH_TSEQ_TCPTRACE:
+ tseq_initialize (g);
+ break;
+ case GRAPH_THROUGHPUT:
+ tput_initialize (g);
+ break;
+ case GRAPH_RTT:
+ rtt_initialize (g);
+ break;
+ case GRAPH_WSCALE:
+ wscale_initialize (g);
+ break;
+ default:
+ break;
+ }
+}
+
+static void graph_destroy (struct graph *g)
+{
+ debug(DBS_FENTRY) puts ("graph_destroy()");
+
+ axis_destroy (g->x_axis);
+ axis_destroy (g->y_axis);
+ /* window_destroy (g->drawing_area); */
+ window_destroy (g->gui.control_panel);
+ window_destroy (g->toplevel);
+ /* window_destroy (g->text); */
+#if GTK_CHECK_VERSION(2,22,0)
+ if(g->title_surface){
+ cairo_surface_destroy (g->title_surface);
+ }
+ if(g->surface[0]){
+ cairo_surface_destroy (g->surface[0]);
+ }
+ if(g->surface[1]){
+ cairo_surface_destroy (g->surface[1]);
+ }
+#else
+ g_object_unref (g->pixmap[0]);
+ g_object_unref (g->pixmap[1]);
+#endif /* GTK_CHECK_VERSION(2,22,0) */
+ g_free (g->x_axis);
+ g_free (g->y_axis);
+ g_free ( (gpointer) (g->title) );
+ graph_segment_list_free (g);
+ graph_element_lists_free (g);
+
+ g_free (g);
+}
+
+
+typedef struct _tcp_scan_t {
+ struct segment *current;
+ int direction;
+ struct graph *g;
+ struct segment *last;
+} tcp_scan_t;
+
+static int
+tapall_tcpip_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ static struct segment *segment=NULL;
+ tcp_scan_t *ts=(tcp_scan_t *)pct;
+ struct tcpheader *tcphdr=(struct tcpheader *)vip;
+
+ if(!segment){
+ segment=g_malloc(sizeof (struct segment));
+ }
+
+
+ if (compare_headers(&ts->current->ip_src, &ts->current->ip_dst,
+ ts->current->th_sport, ts->current->th_dport,
+ &tcphdr->ip_src, &tcphdr->ip_dst,
+ tcphdr->th_sport, tcphdr->th_dport,
+ ts->direction)) {
+ segment->next = NULL;
+ segment->num = pinfo->fd->num;
+ segment->rel_secs = (guint32) pinfo->fd->rel_ts.secs;
+ segment->rel_usecs = pinfo->fd->rel_ts.nsecs/1000;
+ segment->abs_secs = (guint32) pinfo->fd->abs_ts.secs;
+ segment->abs_usecs = pinfo->fd->abs_ts.nsecs/1000;
+ segment->th_seq=tcphdr->th_seq;
+ segment->th_ack=tcphdr->th_ack;
+ segment->th_win=tcphdr->th_win;
+ segment->th_flags=tcphdr->th_flags;
+ segment->th_sport=tcphdr->th_sport;
+ segment->th_dport=tcphdr->th_dport;
+ segment->th_seglen=tcphdr->th_seglen;
+ COPY_ADDRESS(&segment->ip_src, &tcphdr->ip_src);
+ COPY_ADDRESS(&segment->ip_dst, &tcphdr->ip_dst);
+ if (ts->g->segments) {
+ ts->last->next = segment;
+ } else {
+ ts->g->segments = segment;
+ }
+ ts->last = segment;
+ if(pinfo->fd->num==ts->current->num){
+ ts->g->current = segment;
+ }
+
+ segment=NULL;
+ }
+
+ return 0;
+}
+
+
+
+/* here we collect all the external data we will ever need */
+static void graph_segment_list_get (struct graph *g)
+{
+ struct segment current;
+ GString *error_string;
+ tcp_scan_t ts;
+
+
+ debug(DBS_FENTRY) puts ("graph_segment_list_get()");
+ select_tcpip_session (&cfile, &current);
+ if (g->type == GRAPH_THROUGHPUT)
+ ts.direction = COMPARE_CURR_DIR;
+ else
+ ts.direction = COMPARE_ANY_DIR;
+
+ /* rescan all the packets and pick up all interesting tcp headers.
+ * we only filter for TCP here for speed and do the actual compare
+ * in the tap listener
+ */
+ ts.current=&current;
+ ts.g=g;
+ ts.last=NULL;
+ error_string=register_tap_listener("tcp", &ts, "tcp", 0, NULL, tapall_tcpip_packet, NULL);
+ if(error_string){
+ fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+ cf_retap_packets(&cfile);
+ remove_tap_listener(&ts);
+}
+
+
+typedef struct _th_t {
+ int num_hdrs;
+ struct tcpheader *tcphdr;
+} th_t;
+
+static int
+tap_tcpip_packet(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip)
+{
+ th_t *th=pct;
+
+ th->num_hdrs++;
+ th->tcphdr=(struct tcpheader *)vip;
+
+ return 0;
+}
+
+
+
+/* XXX should be enhanced so that if we have multiple TCP layers in the trace
+ * then present the user with a dialog where the user can select WHICH tcp
+ * session to graph.
+ */
+static struct tcpheader *select_tcpip_session (capture_file *cf, struct segment *hdrs)
+{
+ frame_data *fdata;
+ epan_dissect_t edt;
+ dfilter_t *sfcode;
+ GString *error_string;
+ th_t th = {0, NULL};
+
+ fdata = cf->current_frame;
+
+ /* no real filter yet */
+ if (!dfilter_compile("tcp", &sfcode)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
+ return NULL;
+ }
+
+ /* dissect the current frame */
+ if (!cf_read_frame(cf, fdata))
+ return NULL; /* error reading the frame */
+
+
+ error_string=register_tap_listener("tcp", &th, NULL, 0, NULL, tap_tcpip_packet, NULL);
+ if(error_string){
+ fprintf(stderr, "wireshark: Couldn't register tcp_graph tap: %s\n",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+
+ epan_dissect_init(&edt, TRUE, FALSE);
+ epan_dissect_prime_dfilter(&edt, sfcode);
+ tap_queue_init(&edt);
+ epan_dissect_run(&edt, &cf->pseudo_header, cf->pd, fdata, NULL);
+ tap_push_tapped_queue(&edt);
+ epan_dissect_cleanup(&edt);
+ remove_tap_listener(&th);
+
+ if(th.num_hdrs==0){
+ /* This "shouldn't happen", as our menu items shouldn't
+ * even be enabled if the selected packet isn't a TCP
+ * segment, as tcp_graph_selected_packet_enabled() is used
+ * to determine whether to enable any of our menu items. */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Selected packet isn't a TCP segment");
+ return NULL;
+ }
+ /* XXX fix this later, we should show a dialog allowing the user
+ to select which session he wants here
+ */
+ if(th.num_hdrs>1){
+ /* can only handle a single tcp layer yet */
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "The selected packet has more than one TCP"
+ "header in it.");
+ return NULL;
+ }
+
+ hdrs->num = fdata->num;
+ hdrs->rel_secs = (guint32) fdata->rel_ts.secs;
+ hdrs->rel_usecs = fdata->rel_ts.nsecs/1000;
+ hdrs->abs_secs = (guint32) fdata->abs_ts.secs;
+ hdrs->abs_usecs = fdata->abs_ts.nsecs/1000;
+ hdrs->th_seq=th.tcphdr->th_seq;
+ hdrs->th_ack=th.tcphdr->th_ack;
+ hdrs->th_win=th.tcphdr->th_win;
+ hdrs->th_flags=th.tcphdr->th_flags;
+ hdrs->th_sport=th.tcphdr->th_sport;
+ hdrs->th_dport=th.tcphdr->th_dport;
+ hdrs->th_seglen=th.tcphdr->th_seglen;
+ COPY_ADDRESS(&hdrs->ip_src, &th.tcphdr->ip_src);
+ COPY_ADDRESS(&hdrs->ip_dst, &th.tcphdr->ip_dst);
+ return th.tcphdr;
+
+}
+
+static int compare_headers (address *saddr1, address *daddr1, guint16 sport1, guint16 dport1, address *saddr2, address *daddr2, guint16 sport2, guint16 dport2, int dir)
+{
+ int dir1, dir2;
+
+ dir1 = ((!(CMP_ADDRESS(saddr1, saddr2))) &&
+ (!(CMP_ADDRESS(daddr1, daddr2))) &&
+ (sport1==sport2) &&
+ (dport1==dport2));
+
+ if(dir==COMPARE_CURR_DIR){
+ return dir1;
+ } else {
+ dir2 = ((!(CMP_ADDRESS(saddr1, daddr2))) &&
+ (!(CMP_ADDRESS(daddr1, saddr2))) &&
+ (sport1==dport2) &&
+ (dport1==sport2));
+ return dir1 || dir2;
+ }
+}
+
+static void graph_segment_list_free (struct graph *g)
+{
+ struct segment *segment;
+
+ while (g->segments) {
+ segment = g->segments->next;
+ g_free (g->segments);
+ g->segments = segment;
+ }
+ g->segments = NULL;
+}
+
+static void graph_element_lists_initialize (struct graph *g)
+{
+ g->elists = (struct element_list *)g_malloc0 (sizeof (struct element_list));
+}
+
+static void graph_element_lists_make (struct graph *g)
+{
+ debug(DBS_FENTRY) puts ("graph_element_lists_make()");
+
+ switch (g->type) {
+ case GRAPH_TSEQ_STEVENS:
+ tseq_stevens_make_elmtlist (g);
+ break;
+ case GRAPH_TSEQ_TCPTRACE:
+ tseq_tcptrace_make_elmtlist (g);
+ break;
+ case GRAPH_THROUGHPUT:
+ tput_make_elmtlist (g);
+ break;
+ case GRAPH_RTT:
+ rtt_make_elmtlist (g);
+ break;
+ case GRAPH_WSCALE:
+ wscale_make_elmtlist (g);
+ break;
+ default:
+ printf ("graph_element_lists_make: unknown graph type: %d\n", g->type);
+ break;
+ }
+}
+
+static void graph_element_lists_free (struct graph *g)
+{
+ struct element_list *list, *next_list;
+
+#if 0
+ for (list=g->elists; list; list=list->next)
+ g_free (list->elements);
+ while (g->elists->next) {
+ list = g->elists->next->next;
+ g_free (g->elists->next);
+ g->elists->next = list;
+ }
+#endif
+
+ for (list=g->elists; list; list=next_list) {
+ g_free (list->elements);
+ next_list = list->next;
+ g_free (list);
+ }
+ g->elists = NULL; /* just to make debugging easier */
+}
+
+static void graph_title_pixmap_create (struct graph *g)
+{
+#if GTK_CHECK_VERSION(2,22,0)
+ if(g->title_surface){
+ cairo_surface_destroy (g->title_surface);
+ g->title_surface = NULL;
+ }
+
+ g->title_surface = gdk_window_create_similar_surface (gtk_widget_get_window(g->drawing_area),
+ CAIRO_CONTENT_COLOR,
+ g->x_axis->p.width,
+ g->wp.y);
+
+#else
+ if (g->title_pixmap)
+ g_object_unref (g->title_pixmap);
+
+ g->title_pixmap = gdk_pixmap_new (gtk_widget_get_window(g->drawing_area),
+ g->x_axis->p.width, g->wp.y, -1);
+#endif
+}
+
+static void graph_title_pixmap_draw (struct graph *g)
+{
+ int i;
+ cairo_t *cr;
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (g->title_surface);
+#else
+ cr = gdk_cairo_create (g->title_pixmap);
+#endif
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_rectangle (cr, 0, 0, g->x_axis->p.width, g->wp.y);
+ cairo_fill (cr);
+
+ for (i=0; g->title[i]; i++) {
+ gint w, h;
+ PangoLayout *layout;
+ layout = gtk_widget_create_pango_layout(g->drawing_area,
+ g->title[i]);
+ pango_layout_get_pixel_size(layout, &w, &h);
+ cairo_move_to (cr, g->wp.width/2 - w/2, 20 + i*(h+3));
+ pango_cairo_show_layout (cr, layout);
+ g_object_unref(G_OBJECT(layout));
+ }
+ cairo_destroy (cr);
+}
+
+static void graph_title_pixmap_display (struct graph *g)
+{
+ cairo_t *cr;
+
+ cr = gdk_cairo_create (gtk_widget_get_window(g->drawing_area));
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, g->title_surface, g->wp.x, 0);
+#else
+ gdk_cairo_set_source_pixmap (cr, g->title_pixmap, g->wp.x, 0);
+#endif
+ cairo_rectangle (cr, g->wp.x, 0, g->x_axis->p.width, g->wp.y);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+}
+
+static void graph_pixmaps_create (struct graph *g)
+{
+ debug(DBS_FENTRY) puts ("graph_pixmaps_create()");
+#if GTK_CHECK_VERSION(2,22,0)
+ if(g->surface[0]){
+ cairo_surface_destroy (g->surface[0]);
+ g->surface[0] = NULL;
+ }
+
+ if(g->surface[1]){
+ cairo_surface_destroy (g->surface[1]);
+ g->surface[1] = NULL;
+ }
+
+ g->surface[0] = gdk_window_create_similar_surface (gtk_widget_get_window(g->drawing_area),
+ CAIRO_CONTENT_COLOR,
+ g->wp.width,
+ g->wp.height);
+
+ g->surface[1] = gdk_window_create_similar_surface (gtk_widget_get_window(g->drawing_area),
+ CAIRO_CONTENT_COLOR,
+ g->wp.width,
+ g->wp.height);
+
+ g->displayed = 0;
+#else
+ if (g->pixmap[0])
+ g_object_unref (g->pixmap[0]);
+ if (g->pixmap[1])
+ g_object_unref (g->pixmap[1]);
+
+ g->pixmap[0] = gdk_pixmap_new (gtk_widget_get_window(g->drawing_area),
+ g->wp.width, g->wp.height, -1);
+ g->pixmap[1] = gdk_pixmap_new (gtk_widget_get_window(g->drawing_area),
+ g->wp.width, g->wp.height, -1);
+
+ g->displayed = 0;
+#endif /* GTK_CHECK_VERSION(2,22,0) */
+}
+
+static void graph_display (struct graph *g)
+{
+ set_busy_cursor (gtk_widget_get_window(g->drawing_area));
+ graph_pixmap_draw (g);
+ unset_busy_cursor (gtk_widget_get_window(g->drawing_area));
+ graph_pixmaps_switch (g);
+ graph_pixmap_display (g);
+}
+
+static void graph_pixmap_display (struct graph *g)
+{
+ cairo_t *cr;
+
+ cr = gdk_cairo_create (gtk_widget_get_window(g->drawing_area));
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, g->surface[g->displayed], g->wp.x, g->wp.y);
+#else
+ gdk_cairo_set_source_pixmap (cr, g->pixmap[g->displayed], g->wp.x, g->wp.y);
+#endif /* GTK_CHECK_VERSION(2,22,0) */
+ cairo_rectangle (cr, g->wp.x, g->wp.y, g->wp.width, g->wp.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+ if (g->cross.erase_needed) {
+ cross_xor(g, g->cross.x, g->cross.y);
+ }
+}
+
+static void graph_pixmaps_switch (struct graph *g)
+{
+ g->displayed = 1 ^ g->displayed;
+}
+
+static void graph_pixmap_draw (struct graph *g)
+{
+ struct element_list *list;
+ struct element *e;
+ int not_disp;
+ cairo_t *cr;
+
+ debug(DBS_FENTRY) puts ("graph_display()");
+ not_disp = 1 ^ g->displayed;
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (g->surface[not_disp]);
+#else
+ cr = gdk_cairo_create (g->pixmap[not_disp]);
+#endif /* GTK_CHECK_VERSION(2,22,0) */
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_rectangle (cr, 0, 0, g->wp.width, g->wp.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+ cr = NULL;
+
+ for (list=g->elists; list; list=list->next)
+ for (e=list->elements; e->type != ELMT_NONE; e++) {
+ switch (e->type) {
+ case ELMT_RECT:
+ break;
+ case ELMT_LINE:
+ draw_element_line (g, e);
+ break;
+ case ELMT_ELLIPSE:
+ draw_element_ellipse (g, e);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void draw_element_line (struct graph *g, struct element *e)
+{
+ int xx1, xx2, yy1, yy2;
+ cairo_t *cr;
+
+ debug(DBS_GRAPH_DRAWING) printf ("line element: (%.2f,%.2f)->(%.2f,%.2f), "
+ "seg %d ... ", e->p.line.dim.x1, e->p.line.dim.y1,
+ e->p.line.dim.x2, e->p.line.dim.y2, e->parent->num);
+ xx1 = (int )rint (e->p.line.dim.x1 + g->geom.x - g->wp.x);
+ xx2 = (int )rint (e->p.line.dim.x2 + g->geom.x - g->wp.x);
+ yy1 = (int )rint ((g->geom.height-1-e->p.line.dim.y1) + g->geom.y-g->wp.y);
+ yy2 = (int )rint ((g->geom.height-1-e->p.line.dim.y2) + g->geom.y-g->wp.y);
+ if (xx1 > xx2) {
+ int tmp=xx2;
+ xx2=xx1;
+ xx1=tmp;
+ }
+ if (yy1 > yy2) {
+ int tmp=yy2;
+ yy2=yy1;
+ yy1=tmp;
+ }
+ if ((xx1<0 && xx2<0) || (xx1>=g->wp.width && xx2>=g->wp.width) ||
+ (yy1<0 && yy2<0) || (yy1>=g->wp.height && yy2>=g->wp.height)) {
+ debug(DBS_GRAPH_DRAWING) printf (" refusing: (%d,%d)->(%d,%d)\n",
+ xx1, yy1, xx2, yy2);
+ return;
+ }
+ if (xx2 > g->wp.width-1)
+ xx2 = g->wp.width-1;
+ if (xx1 < 0)
+ xx1 = 0;
+ if (yy2 > g->wp.height-1)
+ yy2 = g->wp.height-1;
+ if (yy1 < 0)
+ yy1 = 0;
+ debug(DBS_GRAPH_DRAWING) printf ("line: (%d,%d)->(%d,%d)\n", xx1, yy1, xx2,yy2);
+
+ g_assert(e->elment_color_p!=NULL);
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (g->surface[1^g->displayed]);
+#else
+ cr = gdk_cairo_create (g->pixmap[1^g->displayed]);
+#endif
+ cairo_set_line_width (cr, 1.0);
+ if(e->elment_color_p!=NULL){
+ gdk_cairo_set_source_color (cr, e->elment_color_p);
+ }
+ cairo_move_to(cr, xx1+0.5, yy1+0.5);
+ cairo_line_to(cr, xx2+0.5, yy2+0.5);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+}
+
+static void draw_element_ellipse (struct graph *g, struct element *e)
+{
+ cairo_t *cr;
+ gdouble w = e->p.ellipse.dim.width;
+ gdouble h = e->p.ellipse.dim.height;
+ gdouble x = e->p.ellipse.dim.x + g->geom.x - g->wp.x;
+ gdouble y = g->geom.height-1 - e->p.ellipse.dim.y + g->geom.y - g->wp.y;
+
+ debug(DBS_GRAPH_DRAWING) printf ("ellipse: (x, y) -> (w, h): (%f, %f) -> (%f, %f)\n", x, y, w, h);
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (g->surface[1^g->displayed]);
+#else
+ cr = gdk_cairo_create (g->pixmap[1^g->displayed]);
+#endif
+ cairo_translate (cr, x + w / 2., y + h / 2.);
+ cairo_scale (cr, w / 2., h / 2.);
+ cairo_arc (cr, 0., 0., 1., 0., 2 * G_PI);
+ cairo_fill(cr);
+ cairo_destroy(cr);
+}
+
+static void axis_pixmaps_create (struct axis *axis)
+{
+ debug(DBS_FENTRY) puts ("axis_pixmaps_create()");
+#if GTK_CHECK_VERSION(2,22,0)
+ if(axis->surface[0]){
+ cairo_surface_destroy (axis->surface[0]);
+ axis->surface[0] = NULL;
+ }
+ if(axis->surface[1]){
+ cairo_surface_destroy (axis->surface[1]);
+ axis->surface[1] = NULL;
+ }
+ axis->surface[0] = gdk_window_create_similar_surface (gtk_widget_get_window(axis->drawing_area),
+ CAIRO_CONTENT_COLOR,
+ axis->p.width,
+ axis->p.height);
+
+ axis->surface[1] = gdk_window_create_similar_surface (gtk_widget_get_window(axis->drawing_area),
+ CAIRO_CONTENT_COLOR,
+ axis->p.width,
+ axis->p.height);
+
+ axis->displayed = 0;
+#else
+ if (axis->pixmap[0])
+ g_object_unref (axis->pixmap[0]);
+ if (axis->pixmap[1])
+ g_object_unref (axis->pixmap[1]);
+
+ axis->pixmap[0] = gdk_pixmap_new (gtk_widget_get_window(axis->drawing_area),
+ axis->p.width, axis->p.height, -1);
+ axis->pixmap[1] = gdk_pixmap_new (gtk_widget_get_window(axis->drawing_area),
+ axis->p.width, axis->p.height, -1);
+
+ axis->displayed = 0;
+#endif
+}
+
+static void axis_destroy (struct axis *axis)
+{
+#if GTK_CHECK_VERSION(2,22,0)
+ if(axis->surface[0]){
+ cairo_surface_destroy (axis->surface[0]);
+ axis->surface[0] = NULL;
+ }
+ if(axis->surface[1]){
+ cairo_surface_destroy (axis->surface[1]);
+ axis->surface[1] = NULL;
+ }
+#else
+ g_object_unref (axis->pixmap[0]);
+ g_object_unref (axis->pixmap[1]);
+#endif
+ g_free ( (gpointer) (axis->label) );
+}
+
+static void axis_display (struct axis *axis)
+{
+ if (axis->flags & AXIS_ORIENTATION)
+ h_axis_pixmap_draw (axis);
+ else
+ v_axis_pixmap_draw (axis);
+ axis_pixmaps_switch (axis);
+ axis_pixmap_display (axis);
+}
+
+static void v_axis_pixmap_draw (struct axis *axis)
+{
+ struct graph *g = axis->g;
+ int i;
+ double major_tick;
+ int not_disp, rdigits, offset, imin, imax;
+ double bottom, top, j, fl, corr;
+ PangoLayout *layout;
+ cairo_t *cr;
+
+ debug(DBS_FENTRY) puts ("v_axis_pixmap_draw()");
+ bottom = (g->geom.height - (g->wp.height + g->wp.y + (-g->geom.y))) /
+ (double )g->geom.height * g->bounds.height;
+ bottom += axis->min;
+ top = (g->geom.height - (g->wp.y + (-g->geom.y))) /
+ (double )g->geom.height * g->bounds.height;
+ top += axis->min;
+ axis_compute_ticks (axis, bottom, top, AXIS_VERTICAL);
+
+ j = axis->major - floor (axis->major);
+ for (rdigits=0; rdigits<=6; rdigits++) {
+ j *= 10;
+ if (j<=0.000001)
+ break;
+ j = j - floor (j);
+ }
+
+ not_disp = 1 ^ axis->displayed;
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (axis->surface[not_disp]);
+#else
+ cr = gdk_cairo_create (axis->pixmap[not_disp]);
+#endif
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_rectangle (cr, 0, 0, axis->p.width, axis->p.height);
+ cairo_fill (cr);
+
+ /* axis */
+ cairo_set_source_rgb (cr, 0, 0, 0);
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, axis->p.width - 1.5, (axis->p.height-axis->s.height)/2.0);
+ cairo_line_to(cr, axis->s.width - 1.5, axis->p.height);
+ cairo_stroke(cr);
+
+ offset = g->wp.y + (-g->geom.y);
+ fl = floor (axis->min / axis->major) * axis->major;
+ corr = rint ((axis->min - fl) * g->zoom.y);
+
+ /* major ticks */
+ major_tick = axis->major * g->zoom.y;
+ imin = (int) ((g->geom.height - offset + corr - g->wp.height) / major_tick + 1);
+ imax = (int) ((g->geom.height - offset + corr) / major_tick);
+ for (i=imin; i <= imax; i++) {
+ gint w, h;
+ char desc[32];
+ int y = (int) (g->geom.height-1 - (int )rint (i * major_tick) -
+ offset + corr + axis->s.y);
+
+ debug(DBS_AXES_DRAWING) printf("%f @ %d\n",
+ i*axis->major + fl, y);
+ if (y < 0 || y > axis->p.height)
+ continue;
+
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, axis->p.width - 15, y+0.5);
+ cairo_line_to(cr, axis->s.width - 1, y+0.5);
+ cairo_stroke(cr);
+
+ g_snprintf (desc, sizeof(desc), "%.*f", rdigits, i*axis->major + fl);
+ layout = gtk_widget_create_pango_layout(g->drawing_area, desc);
+ pango_layout_get_pixel_size(layout, &w, &h);
+ cairo_move_to (cr, axis->s.width-14-4-w, y - h/2);
+ pango_cairo_show_layout (cr, layout);
+ g_object_unref(G_OBJECT(layout));
+ }
+ /* minor ticks */
+ if (axis->minor) {
+ double minor_tick = axis->minor * g->zoom.y;
+ imin = (int) ((g->geom.height - offset + corr - g->wp.height)/minor_tick + 1);
+ imax = (int) ((g->geom.height - offset + corr) / minor_tick);
+ for (i=imin; i <= imax; i++) {
+ int y = (int) (g->geom.height-1 - (int )rint (i*minor_tick) -
+ offset + corr + axis->s.y);
+
+ debug (DBS_AXES_DRAWING) printf ("%f @ %d\n", i*axis->minor+fl, y);
+ if (y > 0 && y < axis->p.height)
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, axis->s.width - 8, y+0.5);
+ cairo_line_to(cr, axis->s.width - 1, y+0.5);
+ cairo_stroke(cr);
+ }
+ }
+ for (i=0; axis->label[i]; i++) {
+ gint w, h;
+ layout = gtk_widget_create_pango_layout(g->drawing_area,
+ axis->label[i]);
+ pango_layout_get_pixel_size(layout, &w, &h);
+ cairo_move_to (cr, (axis->p.width - w)/2, TITLEBAR_HEIGHT-10 - i*(h+3) - h);
+ pango_cairo_show_layout (cr, layout);
+ g_object_unref(G_OBJECT(layout));
+ }
+ cairo_destroy (cr);
+}
+
+static void h_axis_pixmap_draw (struct axis *axis)
+{
+ struct graph *g = axis->g;
+ int i;
+ double major_tick, minor_tick;
+ int not_disp, rdigits, offset, imin, imax;
+ double left, right, j, fl, corr;
+ PangoLayout *layout;
+ cairo_t *cr;
+
+ debug(DBS_FENTRY) puts ("h_axis_pixmap_draw()");
+ left = (g->wp.x-g->geom.x) /
+ (double )g->geom.width * g->bounds.width;
+ left += axis->min;
+ right = (g->wp.x-g->geom.x+g->wp.width) /
+ (double )g->geom.width * g->bounds.width;
+ right += axis->min;
+ axis_compute_ticks (axis, left, right, AXIS_HORIZONTAL);
+
+ j = axis->major - floor (axis->major);
+ for (rdigits=0; rdigits<=6; rdigits++) {
+ j *= 10;
+ if (j<=0.000001)
+ break;
+ j = j - floor (j);
+ }
+
+ not_disp = 1 ^ axis->displayed;
+
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (axis->surface[not_disp]);
+#else
+ cr = gdk_cairo_create (axis->pixmap[not_disp]);
+#endif
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_rectangle (cr, 0, 0, axis->p.width, axis->p.height);
+ cairo_fill (cr);
+
+ /* axis */
+ cairo_set_source_rgb (cr, 0, 0, 0);
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, 0, 0.5);
+ cairo_line_to(cr, axis->s.width + (axis->p.width-axis->s.width)/2.0, 0.5);
+ cairo_stroke(cr);
+
+ offset = g->wp.x - g->geom.x;
+
+ fl = floor (axis->min / axis->major) * axis->major;
+ corr = rint ((axis->min - fl) * g->zoom.x);
+
+ /* major ticks */
+ major_tick = axis->major*g->zoom.x;
+ imin = (int) ((offset + corr) / major_tick + 1);
+ imax = (int) ((offset + corr + axis->s.width) / major_tick);
+ for (i=imin; i <= imax; i++) {
+ char desc[32];
+ int w, h;
+ int x = (int ) (rint (i * major_tick) - offset - corr);
+
+ /* printf ("%f @ %d\n", i*axis->major + fl, x); */
+ if (x < 0 || x > axis->s.width)
+ continue;
+ cairo_move_to(cr, x+0.5, 0);
+ cairo_line_to(cr, x+0.5, 15);
+ cairo_stroke(cr);
+
+ g_snprintf (desc, sizeof(desc), "%.*f", rdigits, i*axis->major + fl);
+ layout = gtk_widget_create_pango_layout(g->drawing_area, desc);
+ pango_layout_get_pixel_size(layout, &w, &h);
+ cairo_move_to (cr, x - w/2, 15+4);
+ pango_cairo_show_layout (cr, layout);
+ g_object_unref(G_OBJECT(layout));
+ }
+ if (axis->minor > 0) {
+ /* minor ticks */
+ minor_tick = axis->minor*g->zoom.x;
+ imin = (int) ((offset + corr) / minor_tick + 1);
+ imax = (int) ((offset + corr + g->wp.width) / minor_tick);
+ for (i=imin; i <= imax; i++) {
+ int x = (int) (rint (i * minor_tick) - offset - corr);
+ if (x > 0 && x < axis->s.width){
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, x+0.5, 0);
+ cairo_line_to(cr, x+0.5, 8);
+ cairo_stroke(cr);
+ }
+ }
+ }
+ for (i=0; axis->label[i]; i++) {
+ gint w, h;
+ layout = gtk_widget_create_pango_layout(g->drawing_area,
+ axis->label[i]);
+ pango_layout_get_pixel_size(layout, &w, &h);
+ cairo_move_to (cr, axis->s.width - w - 50, 15+h+15 + i*(h+3));
+ pango_cairo_show_layout (cr, layout);
+ g_object_unref(G_OBJECT(layout));
+ }
+ cairo_destroy (cr);
+}
+
+static void axis_pixmaps_switch (struct axis *axis)
+{
+ axis->displayed = 1 ^ axis->displayed;
+}
+
+static void axis_pixmap_display (struct axis *axis)
+{
+ cairo_t *cr;
+
+ cr = gdk_cairo_create (gtk_widget_get_window(axis->drawing_area));
+#if GTK_CHECK_VERSION(2,22,0)
+ cairo_set_source_surface (cr, axis->surface[axis->displayed], axis->p.x, axis->p.y);
+#else
+ gdk_cairo_set_source_pixmap (cr, axis->pixmap[axis->displayed], axis->p.x, axis->p.y);
+#endif
+ cairo_rectangle (cr, axis->p.x, axis->p.y, axis->p.width, axis->p.height);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+
+}
+
+static void axis_compute_ticks (struct axis *axis, double x0, double xmax, int dir)
+{
+ int i, j, ii, jj, ms;
+ double zoom, x, steps[3]={ 0.1, 0.5 };
+ int dim, check_needed, diminished;
+ double majthresh[2]={2.0, 3.0};
+
+ debug((DBS_FENTRY | DBS_AXES_TICKS)) puts ("axis_compute_ticks()");
+ debug(DBS_AXES_TICKS)
+ printf ("x0=%f xmax=%f dir=%s\n", x0,xmax, dir?"VERTICAL":"HORIZONTAL");
+
+ zoom = axis_zoom_get (axis, dir);
+ x = xmax-x0;
+ for (i=-9; i<=12; i++) {
+ if (x / pow (10, i) < 1)
+ break;
+ }
+ --i;
+ ms = (int )(x / pow (10, i));
+
+ if (ms > 5) {
+ j = 0;
+ ++i;
+ } else if (ms > 2)
+ j = 1;
+ else
+ j = 0;
+
+ axis->major = steps[j] * pow (10, i);
+
+ debug(DBS_AXES_TICKS) printf ("zoom=%.1f, x=%f -> i=%d -> ms=%d -> j=%d ->"
+ " axis->major=%f\n", zoom, x, i, ms, j, axis->major);
+
+ /* let's compute minor ticks */
+ jj = j;
+ ii = i;
+ axis_ticks_down (&ii, &jj);
+ axis->minor = steps[jj] * pow (10, ii);
+ /* we don't want minors if they would be less than 10 pixels apart */
+ if (axis->minor*zoom < 10) {
+ debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
+ "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
+ axis->minor = 0;
+ }
+
+ check_needed = TRUE;
+ diminished = FALSE;
+ while (check_needed) {
+ check_needed = FALSE;
+ dim = get_label_dim (axis, dir, xmax);
+ debug(DBS_AXES_TICKS) printf ("axis->major==%.1f, axis->minor==%.1f =>"
+ " axis->major*zoom/dim==%f, axis->minor*zoom/dim==%f\n",
+ axis->major, axis->minor, axis->major*zoom/dim,
+ axis->minor*zoom/dim);
+
+ /* corrections: if majors are less than majthresh[dir] times label
+ * dimension apart, we need to use bigger ones */
+ if (axis->major*zoom / dim < majthresh[dir]) {
+ axis_ticks_up (&ii, &jj);
+ axis->minor = axis->major;
+ axis_ticks_up (&i, &j);
+ axis->major = steps[j] * pow (10, i);
+ check_needed = TRUE;
+ debug(DBS_AXES_TICKS) printf ("axis->major enlarged to %.1f\n",
+ axis->major);
+ }
+ /* if minor ticks are bigger than majthresh[dir] times label dimension,
+ * we could promote them to majors as well */
+ if (axis->minor*zoom / dim > majthresh[dir] && !diminished) {
+ axis_ticks_down (&i, &j);
+ axis->major = axis->minor;
+ axis_ticks_down (&ii, &jj);
+ axis->minor = steps[jj] * pow (10, ii);
+ check_needed = TRUE;
+ diminished = TRUE;
+
+ debug(DBS_AXES_TICKS) printf ("axis->minor diminished to %.1f\n",
+ axis->minor);
+
+ if (axis->minor*zoom < 10) {
+ debug(DBS_AXES_TICKS) printf ("refusing axis->minor of %f: "
+ "axis->minor*zoom == %f\n", axis->minor, axis->minor*zoom);
+ axis->minor = 0;
+ }
+ }
+ }
+
+ debug(DBS_AXES_TICKS) printf ("corrected: axis->major == %.1f -> "
+ "axis->minor == %.1f\n", axis->major, axis->minor);
+}
+
+static void axis_ticks_up (int *i, int *j)
+{
+ (*j)++;
+ if (*j>1) {
+ (*i)++;
+ *j=0;
+ }
+}
+
+static void axis_ticks_down (int *i, int *j)
+{
+ (*j)--;
+ if (*j<0) {
+ (*i)--;
+ *j=1;
+ }
+}
+
+static int get_label_dim (struct axis *axis, int dir, double label)
+{
+ double y;
+ char str[32];
+ int rdigits, dim;
+ PangoLayout *layout;
+
+ /* First, let's compute how many digits to the right of radix
+ * we need to print */
+ y = axis->major - floor (axis->major);
+ for (rdigits=0; rdigits<=6; rdigits++) {
+ y *= 10;
+ if (y<=0.000001)
+ break;
+ y = y - floor (y);
+ }
+ g_snprintf (str, sizeof(str), "%.*f", rdigits, label);
+ switch (dir) {
+ case AXIS_HORIZONTAL:
+ layout = gtk_widget_create_pango_layout(axis->g->drawing_area,
+ str);
+ pango_layout_get_pixel_size(layout, &dim, NULL);
+ g_object_unref(G_OBJECT(layout));
+ break;
+ case AXIS_VERTICAL:
+ layout = gtk_widget_create_pango_layout(axis->g->drawing_area,
+ str);
+ pango_layout_get_pixel_size(layout, NULL, &dim);
+ g_object_unref(G_OBJECT(layout));
+ break;
+ default:
+ puts ("initialize axis: an axis must be either horizontal or vertical");
+ return -1;
+ }
+ return dim;
+}
+
+static double axis_zoom_get (struct axis *axis, int dir)
+{
+ switch (dir) {
+ case AXIS_HORIZONTAL:
+ return axis->g->zoom.x;
+ case AXIS_VERTICAL:
+ return axis->g->zoom.y;
+ default:
+ return -1;
+ }
+}
+
+static void graph_select_segment (struct graph *g, int x, int y)
+{
+ struct element_list *list;
+ struct element *e;
+ guint num = 0;
+
+ debug(DBS_FENTRY) puts ("graph_select_segment()");
+
+ x -= g->geom.x;
+ y = g->geom.height-1 - (y - g->geom.y);
+
+ set_busy_cursor (gtk_widget_get_window(g->drawing_area));
+
+ for (list=g->elists; list; list=list->next)
+ for (e=list->elements; e->type != ELMT_NONE; e++) {
+ switch (e->type) {
+ case ELMT_RECT:
+ break;
+ case ELMT_LINE:
+ if (line_detect_collision (e, x, y)) {
+ num = e->parent->num;
+ }
+ break;
+ case ELMT_ELLIPSE:
+ if (ellipse_detect_collision (e, x, y)) {
+ num = e->parent->num;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ if (num) {
+ cf_goto_frame(&cfile, num);
+ }
+ unset_busy_cursor (gtk_widget_get_window(g->drawing_area));
+}
+
+static int line_detect_collision (struct element *e, int x, int y)
+{
+ int xx1, yy1, xx2, yy2;
+
+ if (e->p.line.dim.x1 < e->p.line.dim.x2) {
+ xx1 = (int )rint (e->p.line.dim.x1);
+ xx2 = (int )rint (e->p.line.dim.x2);
+ } else {
+ xx1 = (int )rint (e->p.line.dim.x2);
+ xx2 = (int )rint (e->p.line.dim.x1);
+ }
+ if (e->p.line.dim.y1 < e->p.line.dim.y2) {
+ yy1 = (int )rint (e->p.line.dim.y1);
+ yy2 = (int )rint (e->p.line.dim.y2);
+ } else {
+ yy1 = (int )rint (e->p.line.dim.y2);
+ yy2 = (int )rint (e->p.line.dim.y1);
+ }
+ /*
+ printf ("line: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", xx1, yy1, xx2, yy2, x, y);
+ */
+ if ((xx1==x && xx2==x && yy1<=y && y<=yy2)||(yy1==y && yy2==y && xx1<=x && x<=xx2))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static int ellipse_detect_collision (struct element *e, int x, int y)
+{
+ int xx1, yy1, xx2, yy2;
+
+ xx1 = (int )rint (e->p.ellipse.dim.x);
+ xx2 = (int )rint (e->p.ellipse.dim.x + e->p.ellipse.dim.width);
+ yy1 = (int )rint (e->p.ellipse.dim.y - e->p.ellipse.dim.height);
+ yy2 = (int )rint (e->p.ellipse.dim.y);
+ /*
+ printf ("ellipse: (%d,%d)->(%d,%d), clicked: (%d,%d)\n", xx1, yy1, xx2, yy2, x, y);
+ */
+ if (xx1<=x && x<=xx2 && yy1<=y && y<=yy2)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static void cross_xor (struct graph *g, int x, int y)
+{
+#if GTK_CHECK_VERSION(3,0,0)
+ GdkColor color_gray15 = {0x0, 0x2626, 0x2626, 0x2626};
+ cairo_t *cr;
+
+ /* XXX Fix me: lines do not disappear */
+ if (x > g->wp.x && x < g->wp.x+g->wp.width &&
+ y >= g->wp.y && y < g->wp.y+g->wp.height) {
+ /* Draw horizontal line */
+ cr = gdk_cairo_create (gtk_widget_get_window(g->drawing_area));
+ cairo_set_operator (cr, CAIRO_OPERATOR_XOR);
+ gdk_cairo_set_source_color (cr, &color_gray15);
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, g->wp.x, y);
+ cairo_line_to(cr, g->wp.x + g->wp.width, y);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ /* draw vertical line */
+ cr = gdk_cairo_create (gtk_widget_get_window(g->drawing_area));
+ cairo_set_operator (cr, CAIRO_OPERATOR_XOR);
+ gdk_cairo_set_source_color (cr, &color_gray15);
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, x, g->wp.y);
+ cairo_line_to(cr, x, g->wp.y + g->wp.height);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+ }
+
+#else
+
+ if (x > g->wp.x && x < g->wp.x+g->wp.width &&
+ y >= g->wp.y && y < g->wp.y+g->wp.height) {
+ gdk_draw_line (gtk_widget_get_window(g->drawing_area), xor_gc, g->wp.x,
+ y, g->wp.x + g->wp.width, y);
+ gdk_draw_line (gtk_widget_get_window(g->drawing_area), xor_gc, x,
+ g->wp.y, x, g->wp.y + g->wp.height);
+ }
+#endif
+}
+
+static void cross_draw (struct graph *g, int x, int y)
+{
+ cross_xor (g, x, y);
+ g->cross.x = x;
+ g->cross.y = y;
+ g->cross.erase_needed = 1;
+}
+
+static void cross_erase (struct graph *g)
+{
+ cross_xor (g, g->cross.x, g->cross.y);
+ g->cross.erase_needed = 0;
+}
+
+static void magnify_create (struct graph *g, int x, int y)
+{
+ struct graph *mg;
+ struct element_list *list, *new_list;
+ struct ipoint pos, offsetpos;
+ GdkEvent *e=NULL;
+
+ mg = g->magnify.g = (struct graph * )g_malloc (sizeof (struct graph));
+ memcpy ((void * )mg, (void * )g, sizeof (struct graph));
+
+ mg->toplevel = dlg_window_new("tcp graph magnify");
+ mg->drawing_area = mg->toplevel;
+ gtk_window_set_default_size(GTK_WINDOW(mg->toplevel), g->magnify.width, g->magnify.height);
+ gtk_widget_set_events (mg->drawing_area, GDK_EXPOSURE_MASK
+ /* | GDK_ENTER_NOTIFY_MASK */
+ /* | GDK_ALL_EVENTS_MASK */
+ );
+
+ mg->wp.x = 0;
+ mg->wp.y = 0;
+ mg->wp.width = g->magnify.width;
+ mg->wp.height = g->magnify.height;
+ mg->geom.width = (int )rint (g->geom.width * g->magnify.zoom.x);
+ mg->geom.height = (int )rint (g->geom.height * g->magnify.zoom.y);
+ mg->zoom.x = (mg->geom.width - 1) / g->bounds.width;
+ mg->zoom.y = (mg->geom.height- 1) / g->bounds.height;
+
+ /* in order to keep original element lists intact we need our own */
+ graph_element_lists_initialize (mg);
+ list = g->elists->next;
+ new_list = mg->elists;
+ for ( ; list; list=list->next) {
+ new_list->next =
+ (struct element_list * )g_malloc (sizeof (struct element_list));
+ new_list = new_list->next;
+ new_list->next = NULL;
+ new_list->elements = NULL;
+ }
+ graph_element_lists_make (mg);
+
+ gdk_window_get_position (gtk_widget_get_window(GTK_WIDGET (g->toplevel)), &pos.x, &pos.y);
+ g->magnify.x = pos.x + x - g->magnify.width/2;
+ g->magnify.y = pos.y + y - g->magnify.height/2;
+ offsetpos.x = g->magnify.x + g->magnify.offset.x;
+ offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
+ offsetpos.y = g->magnify.y + g->magnify.offset.y;
+ offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
+ gtk_window_set_position (GTK_WINDOW(mg->drawing_area), GTK_WIN_POS_NONE);
+ magnify_get_geom (g, x, y);
+
+ gtk_widget_show (mg->drawing_area);
+
+ /* we need to wait for the first expose event before we start drawing */
+ while (!gdk_events_pending ());
+ do {
+ e = gdk_event_get ();
+ if (e) {
+ if (e->any.type == GDK_EXPOSE) {
+ gdk_event_free (e);
+ break;
+ }
+ gdk_event_free (e);
+ }
+ } while (e);
+
+#if GTK_CHECK_VERSION(2,22,0)
+ mg->surface[0] = mg->surface[1] = NULL;
+#else
+ mg->pixmap[0] = mg->pixmap[1] = NULL;
+#endif /* GTK_CHECK_VERSION(2,22,0) */
+ graph_pixmaps_create (mg);
+ magnify_draw (g);
+ g->magnify.active = 1;
+}
+
+static void magnify_move (struct graph *g, int x, int y)
+{
+ struct ipoint pos, offsetpos;
+
+ gdk_window_get_position (gtk_widget_get_window(GTK_WIDGET (g->toplevel)), &pos.x, &pos.y);
+ g->magnify.x = pos.x + x - g->magnify.width/2;
+ g->magnify.y = pos.y + y - g->magnify.height/2;
+ offsetpos.x = g->magnify.x + g->magnify.offset.x;
+ offsetpos.x = offsetpos.x >= 0 ? offsetpos.x : 0;
+ offsetpos.y = g->magnify.y + g->magnify.offset.y;
+ offsetpos.y = offsetpos.y >= 0 ? offsetpos.y : 0;
+ magnify_get_geom (g, x, y);
+ magnify_draw (g);
+}
+
+static void magnify_destroy (struct graph *g)
+{
+ struct element_list *list;
+ struct graph *mg = g->magnify.g;
+
+ window_destroy (GTK_WIDGET (mg->drawing_area));
+
+#if GTK_CHECK_VERSION(2,22,0)
+ if(mg->surface[0]){
+ cairo_surface_destroy (mg->surface[0]);
+ }
+ if(mg->surface[1]){
+ cairo_surface_destroy (mg->surface[1]);
+ }
+#else
+ g_object_unref (mg->pixmap[0]);
+ g_object_unref (mg->pixmap[1]);
+#endif /* GTK_CHECK_VERSION(2,22,0) */
+ for (list=mg->elists; list; list=list->next)
+ g_free (list->elements);
+
+ if (mg->elists) {
+ while (mg->elists->next) {
+ list = mg->elists->next->next;
+ g_free (mg->elists->next);
+ mg->elists->next = list;
+ }
+ }
+ g_free (g->magnify.g);
+ g->magnify.active = 0;
+}
+
+static void magnify_get_geom (struct graph *g, int x, int y)
+{
+ int posx, posy;
+
+ gdk_window_get_position (gtk_widget_get_window(GTK_WIDGET (g->toplevel)), &posx, &posy);
+
+ g->magnify.g->geom.x = g->geom.x;
+ g->magnify.g->geom.y = g->geom.y;
+
+ g->magnify.g->geom.x -=
+ (int )rint ((g->magnify.g->geom.width - g->geom.width) *
+ ((x-g->geom.x)/(double )g->geom.width));
+ g->magnify.g->geom.y -=
+ (int )rint ((g->magnify.g->geom.height - g->geom.height) *
+ ((y-g->geom.y)/(double )g->geom.height));
+
+ /* we have coords of origin of graph relative to origin of g->toplevel.
+ * now we need them to relate to origin of magnify window */
+ g->magnify.g->geom.x -= (g->magnify.x - posx);
+ g->magnify.g->geom.y -= (g->magnify.y - posy);
+}
+
+static void magnify_draw (struct graph *g)
+{
+ cairo_t *cr;
+ int not_disp = 1 ^ g->magnify.g->displayed;
+
+ graph_pixmap_draw (g->magnify.g);
+ /* graph pixmap is almost ready, just add border */
+#if GTK_CHECK_VERSION(2,22,0)
+ cr = cairo_create (g->magnify.g->surface[not_disp]);
+#else
+ cr = gdk_cairo_create (g->magnify.g->pixmap[not_disp]);
+#endif /* GTK_CHECK_VERSION(2,22,0) */
+ cairo_set_line_width (cr, 1.0);
+ cairo_move_to(cr, 0, 0);
+ cairo_line_to(cr, g->magnify.width - 1, 0);
+ cairo_stroke(cr);
+
+ cairo_move_to(cr, g->magnify.width - 1, 0);
+ cairo_line_to(cr, g->magnify.width - 1, g->magnify.height);
+ cairo_stroke(cr);
+
+ cairo_move_to(cr, 0, 0);
+ cairo_line_to(cr, 0,g->magnify.height - 1);
+ cairo_stroke(cr);
+
+ cairo_move_to(cr, 0, g->magnify.height - 1);
+ cairo_line_to(cr, g->magnify.width - 1, g->magnify.height - 1);
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+ graph_pixmaps_switch (g->magnify.g);
+ graph_pixmap_display (g->magnify.g);
+
+}
+
+static gboolean configure_event (GtkWidget *widget _U_, GdkEventConfigure *event, gpointer user_data)
+{
+ struct graph *g = user_data;
+ struct {
+ double x, y;
+ } zoom;
+ int cur_g_width, cur_g_height;
+ int cur_wp_width, cur_wp_height;
+
+ debug(DBS_FENTRY) puts ("configure_event()");
+
+ cur_wp_width = g->wp.width;
+ cur_wp_height = g->wp.height;
+ g->wp.width = event->width - g->y_axis->p.width - RMARGIN_WIDTH;
+ g->wp.height = event->height - g->x_axis->p.height - g->wp.y;
+ g->x_axis->s.width = g->wp.width;
+ g->x_axis->p.width = g->wp.width + RMARGIN_WIDTH;
+ g->y_axis->p.height = g->wp.height + g->wp.y;
+ g->y_axis->s.height = g->wp.height;
+ g->x_axis->p.y = g->y_axis->p.height;
+ zoom.x = (double )g->wp.width / cur_wp_width;
+ zoom.y = (double )g->wp.height / cur_wp_height;
+ cur_g_width = g->geom.width;
+ cur_g_height = g->geom.height;
+ g->geom.width = (int )rint (g->geom.width * zoom.x);
+ g->geom.height = (int )rint (g->geom.height * zoom.y);
+ g->zoom.x = (double )(g->geom.width - 1) / g->bounds.width;
+ g->zoom.y = (double )(g->geom.height -1) / g->bounds.height;
+ /* g->zoom.initial.x = g->zoom.x; */
+ /* g->zoom.initial.y = g->zoom.y; */
+
+ g->geom.x = (int) (g->wp.x - (double )g->geom.width/cur_g_width *
+ (g->wp.x - g->geom.x));
+ g->geom.y = (int) (g->wp.y - (double )g->geom.height/cur_g_height *
+ (g->wp.y - g->geom.y));
+#if 0
+ printf ("configure: graph: (%d,%d), (%d,%d); viewport: (%d,%d), (%d,%d); "
+ "zooms: (%f,%f)\n", g->geom.x, g->geom.y, g->geom.width,
+ g->geom.height, g->wp.x, g->wp.y, g->wp.width, g->wp.height,
+ g->zoom.x, g->zoom.y);
+#endif
+
+ update_zoom_spins (g);
+ graph_element_lists_make (g);
+ graph_pixmaps_create (g);
+ graph_title_pixmap_create (g);
+ axis_pixmaps_create (g->y_axis);
+ axis_pixmaps_create (g->x_axis);
+ /* we don't do actual drawing here; we leave it to expose handler */
+ graph_pixmap_draw (g);
+ graph_pixmaps_switch (g);
+ graph_title_pixmap_draw (g);
+ h_axis_pixmap_draw (g->x_axis);
+ axis_pixmaps_switch (g->x_axis);
+ v_axis_pixmap_draw (g->y_axis);
+ axis_pixmaps_switch (g->y_axis);
+ return TRUE;
+}
+#if GTK_CHECK_VERSION(3,0,0)
+static gboolean
+draw_event(GtkWidget *widget _U_, cairo_t *cr, gpointer user_data)
+{
+ struct graph *g = user_data;
+
+ debug(DBS_FENTRY) puts ("draw_event()");
+
+ /* lower left corner */
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_rectangle (cr, 0, g->wp.y + g->wp.height, g->y_axis->p.width, g->x_axis->p.height);
+ cairo_fill (cr);
+
+ /* right margin */
+ cairo_rectangle (cr, g->wp.x + g->wp.width, g->wp.y, RMARGIN_WIDTH, g->wp.height);
+ cairo_fill (cr);
+
+ /* Should these routines be copied here, or be given the cairo_t ?? */
+ graph_pixmap_display (g);
+ graph_title_pixmap_display (g);
+ axis_pixmap_display (g->x_axis);
+ axis_pixmap_display (g->y_axis);
+
+ return TRUE;
+}
+#else
+static gboolean expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
+{
+ struct graph *g = user_data;
+ cairo_t *cr;
+
+ debug(DBS_FENTRY) puts ("expose_event()");
+
+ if (event->count)
+ return TRUE;
+
+ /* lower left corner */
+ cr = gdk_cairo_create (gtk_widget_get_window(widget));
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_rectangle (cr, 0, g->wp.y + g->wp.height, g->y_axis->p.width, g->x_axis->p.height);
+ cairo_fill (cr);
+ cairo_destroy(cr);
+ cr = NULL;
+
+ /* right margin */
+ cr = gdk_cairo_create (gtk_widget_get_window(widget));
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_rectangle (cr, g->wp.x + g->wp.width, g->wp.y, RMARGIN_WIDTH, g->wp.height);
+ cairo_fill (cr);
+ cairo_destroy(cr);
+ cr = NULL;
+
+ graph_pixmap_display (g);
+ graph_title_pixmap_display (g);
+ axis_pixmap_display (g->x_axis);
+ axis_pixmap_display (g->y_axis);
+
+ return TRUE;
+}
+#endif
+
+static void do_zoom_mouse (struct graph *g, GdkEventButton *event)
+{
+ int cur_width = g->geom.width, cur_height = g->geom.height;
+ struct { double x, y; } factor;
+
+ if (g->zoom.flags & ZOOM_OUT) {
+ if (g->zoom.flags & ZOOM_HLOCK)
+ factor.x = 1.0;
+ else
+ factor.x = 1 / g->zoom.step_x;
+ if (g->zoom.flags & ZOOM_VLOCK)
+ factor.y = 1.0;
+ else
+ factor.y = 1 / g->zoom.step_y;
+ } else {
+ if (g->zoom.flags & ZOOM_HLOCK)
+ factor.x = 1.0;
+ else
+ factor.x = g->zoom.step_x;
+ if (g->zoom.flags & ZOOM_VLOCK)
+ factor.y = 1.0;
+ else
+ factor.y = g->zoom.step_y;
+ }
+
+ g->geom.width = (int )rint (g->geom.width * factor.x);
+ g->geom.height = (int )rint (g->geom.height * factor.y);
+ if (g->geom.width < g->wp.width)
+ g->geom.width = g->wp.width;
+ if (g->geom.height < g->wp.height)
+ g->geom.height = g->wp.height;
+ g->zoom.x = (g->geom.width - 1) / g->bounds.width;
+ g->zoom.y = (g->geom.height- 1) / g->bounds.height;
+
+ g->geom.x -= (int )rint ((g->geom.width - cur_width) *
+ ((event->x-g->geom.x)/(double )cur_width));
+ g->geom.y -= (int )rint ((g->geom.height - cur_height) *
+ ((event->y-g->geom.y)/(double )cur_height));
+
+ if (g->geom.x > g->wp.x)
+ g->geom.x = g->wp.x;
+ if (g->geom.y > g->wp.y)
+ g->geom.y = g->wp.y;
+ if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
+ g->geom.x = g->wp.width + g->wp.x - g->geom.width;
+ if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
+ g->geom.y = g->wp.height + g->wp.y - g->geom.height;
+#if 0
+ printf ("button press: graph: (%d,%d), (%d,%d); viewport: (%d,%d), "
+ "(%d,%d); zooms: (%f,%f)\n", g->geom.x, g->geom.y,
+ g->geom.width, g->geom.height, g->wp.x, g->wp.y, g->wp.width,
+ g->wp.height, g->zoom.x, g->zoom.y);
+#endif
+ graph_element_lists_make (g);
+ g->cross.erase_needed = 0;
+ graph_display (g);
+ axis_display (g->y_axis);
+ axis_display (g->x_axis);
+ update_zoom_spins (g);
+ if (g->cross.draw)
+ cross_draw (g, (int) event->x, (int) event->y);
+}
+
+static void do_zoom_keyboard (struct graph *g)
+{
+ int cur_width = g->geom.width, cur_height = g->geom.height;
+ struct { double x, y; } factor;
+ int pointer_x, pointer_y;
+
+ gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &pointer_x, &pointer_y, 0);
+
+ if (g->zoom.flags & ZOOM_OUT) {
+ if (g->zoom.flags & ZOOM_HLOCK)
+ factor.x = 1.0;
+ else
+ factor.x = 1 / g->zoom.step_x;
+ if (g->zoom.flags & ZOOM_VLOCK)
+ factor.y = 1.0;
+ else
+ factor.y = 1 / g->zoom.step_y;
+ } else {
+ if (g->zoom.flags & ZOOM_HLOCK)
+ factor.x = 1.0;
+ else
+ factor.x = g->zoom.step_x;
+ if (g->zoom.flags & ZOOM_VLOCK)
+ factor.y = 1.0;
+ else
+ factor.y = g->zoom.step_y;
+ }
+
+ g->geom.width = (int )rint (g->geom.width * factor.x);
+ g->geom.height = (int )rint (g->geom.height * factor.y);
+ if (g->geom.width < g->wp.width)
+ g->geom.width = g->wp.width;
+ if (g->geom.height < g->wp.height)
+ g->geom.height = g->wp.height;
+ g->zoom.x = (g->geom.width - 1) / g->bounds.width;
+ g->zoom.y = (g->geom.height- 1) / g->bounds.height;
+
+ g->geom.x -= (int )rint ((g->geom.width - cur_width) *
+ ((pointer_x - g->geom.x)/(double )cur_width));
+ g->geom.y -= (int )rint ((g->geom.height - cur_height) *
+ ((pointer_y - g->geom.y)/(double )cur_height));
+
+ if (g->geom.x > g->wp.x)
+ g->geom.x = g->wp.x;
+ if (g->geom.y > g->wp.y)
+ g->geom.y = g->wp.y;
+ if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
+ g->geom.x = g->wp.width + g->wp.x - g->geom.width;
+ if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
+ g->geom.y = g->wp.height + g->wp.y - g->geom.height;
+#if 0
+ printf ("key press: graph: (%d,%d), (%d,%d); viewport: (%d,%d), "
+ "(%d,%d); zooms: (%f,%f)\n", g->geom.x, g->geom.y,
+ g->geom.width, g->geom.height, g->wp.x, g->wp.y, g->wp.width,
+ g->wp.height, g->zoom.x, g->zoom.y);
+#endif
+
+ graph_element_lists_make (g);
+ g->cross.erase_needed = 0;
+ graph_display (g);
+ axis_display (g->y_axis);
+ axis_display (g->x_axis);
+ update_zoom_spins (g);
+ if (g->cross.draw)
+ cross_draw (g, pointer_x, pointer_y);
+}
+
+static void do_zoom_in_keyboard (struct graph *g)
+{
+ gtk_toggle_button_set_active (g->zoom.widget.in_toggle, TRUE);
+ do_zoom_keyboard (g);
+}
+
+static void do_zoom_out_keyboard (struct graph *g)
+{
+ gtk_toggle_button_set_active (g->zoom.widget.out_toggle, TRUE);
+ do_zoom_keyboard (g);
+ gtk_toggle_button_set_active (g->zoom.widget.in_toggle, TRUE);
+}
+
+static void do_select_segment (struct graph *g)
+{
+ int pointer_x, pointer_y;
+
+ gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &pointer_x, &pointer_y, 0);
+ graph_select_segment (g, pointer_x, pointer_y);
+}
+
+static void do_wscale_graph (struct graph *g)
+{
+ gtk_toggle_button_set_active (g->gt.graph_wscale, TRUE);
+}
+
+static void do_rtt_graph (struct graph *g)
+{
+ gtk_toggle_button_set_active (g->gt.graph_rtt, TRUE);
+}
+
+static void do_throughput_graph (struct graph *g)
+{
+ gtk_toggle_button_set_active (g->gt.graph_tput, TRUE);
+}
+
+static void do_ts_graph_stevens (struct graph *g)
+{
+ gtk_toggle_button_set_active (g->gt.graph_tseqstevens, TRUE);
+}
+
+static void do_ts_graph_tcptrace (struct graph *g)
+{
+ gtk_toggle_button_set_active (g->gt.graph_tseqttrace, TRUE);
+}
+
+static void do_magnify_create (struct graph *g)
+{
+ int pointer_x, pointer_y;
+
+ gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &pointer_x, &pointer_y, 0);
+
+ magnify_create (g, (int )rint (pointer_x), (int )rint (pointer_y));
+}
+
+static void do_key_motion (struct graph *g)
+{
+ if (g->geom.x > g->wp.x)
+ g->geom.x = g->wp.x;
+ if (g->geom.y > g->wp.y)
+ g->geom.y = g->wp.y;
+ if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
+ g->geom.x = g->wp.width + g->wp.x - g->geom.width;
+ if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
+ g->geom.y = g->wp.height + g->wp.y - g->geom.height;
+ g->cross.erase_needed = 0;
+ graph_display (g);
+ axis_display (g->y_axis);
+ axis_display (g->x_axis);
+ if (g->cross.draw) {
+ int pointer_x, pointer_y;
+ gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &pointer_x, &pointer_y, 0);
+ cross_draw (g, pointer_x, pointer_y);
+ }
+}
+
+static void do_key_motion_up (struct graph *g, int step)
+{
+ g->geom.y += step;
+ do_key_motion (g);
+}
+
+static void do_key_motion_down (struct graph *g, int step)
+{
+ g->geom.y -= step;
+ do_key_motion (g);
+}
+
+static void do_key_motion_left (struct graph *g, int step)
+{
+ g->geom.x += step;
+ do_key_motion (g);
+}
+
+static void do_key_motion_right (struct graph *g, int step)
+{
+ g->geom.x -= step;
+ do_key_motion (g);
+}
+
+static gboolean button_press_event (GtkWidget *widget _U_, GdkEventButton *event, gpointer user_data)
+{
+ struct graph *g = user_data;
+
+ debug(DBS_FENTRY) puts ("button_press_event()");
+
+ if (event->button == MOUSE_BUTTON_RIGHT) {
+ if (event->state & GDK_CONTROL_MASK) {
+ magnify_create (g, (int )rint (event->x), (int )rint (event->y));
+ } else {
+ g->grab.x = (int )rint (event->x) - g->geom.x;
+ g->grab.y = (int )rint (event->y) - g->geom.y;
+ g->grab.grabbed = TRUE;
+ }
+#ifdef ORIGINAL_WIN32_BUTTONS
+ /* Windows mouse control: */
+ /* [<ctrl>-left] - select packet */
+ /* [left] - zoom in */
+ /* [<shift>-left] - zoom out */
+ } else if (event->button == MOUSE_BUTTON_LEFT) {
+ if (event->state & GDK_CONTROL_MASK) {
+ graph_select_segment (g, (int)event->x, (int)event->y);
+ } else {
+#else /* !ORIGINAL_WIN32_BUTTONS */
+ } else if (event->button == MOUSE_BUTTON_MIDDLE) {
+#endif
+ do_zoom_mouse(g, event);
+#ifndef ORIGINAL_WIN32_BUTTONS
+ } else if (event->button == MOUSE_BUTTON_LEFT) {
+ graph_select_segment (g, (int )event->x, (int )event->y);
+#else /* ORIGINAL_WIN32_BUTTONS*/
+ }
+#endif
+ }
+ return TRUE;
+}
+
+static gboolean motion_notify_event (GtkWidget *widget _U_, GdkEventMotion *event, gpointer user_data)
+{
+ struct graph *g = user_data;
+ int x, y;
+ GdkModifierType state;
+
+ /* debug(DBS_FENTRY) puts ("motion_notify_event()"); */
+
+ if (event->is_hint)
+ gdk_window_get_pointer (event->window, &x, &y, &state);
+ else {
+ x = (int) event->x;
+ y = (int) event->y;
+ state = event->state;
+ }
+
+ /* Testing just (state & GDK_BUTTON1_MASK) is not enough since when button1
+ * is pressed while pointer is in motion, we will receive one more motion
+ * notify *before* we get the button press. This last motion notify works
+ * with stale grab coordinates */
+ if (state & GDK_BUTTON3_MASK) {
+ if (g->grab.grabbed) {
+ g->geom.x = x-g->grab.x;
+ g->geom.y = y-g->grab.y;
+
+ if (g->geom.x > g->wp.x)
+ g->geom.x = g->wp.x;
+ if (g->geom.y > g->wp.y)
+ g->geom.y = g->wp.y;
+ if (g->wp.x + g->wp.width > g->geom.x + g->geom.width)
+ g->geom.x = g->wp.width + g->wp.x - g->geom.width;
+ if (g->wp.y + g->wp.height > g->geom.y + g->geom.height)
+ g->geom.y = g->wp.height + g->wp.y - g->geom.height;
+ g->cross.erase_needed = 0;
+ graph_display (g);
+ axis_display (g->y_axis);
+ axis_display (g->x_axis);
+ if (g->cross.draw)
+ cross_draw (g, x, y);
+ } else if (g->magnify.active)
+ magnify_move (g, x, y);
+ } else if (state & GDK_BUTTON1_MASK) {
+ graph_select_segment (g, x, y);
+ if (g->cross.erase_needed)
+ cross_erase (g);
+ if (g->cross.draw)
+ cross_draw (g, x, y);
+ } else {
+ if (g->cross.erase_needed)
+ cross_erase (g);
+ if (g->cross.draw)
+ cross_draw (g, x, y);
+ }
+
+ return TRUE;
+}
+
+static gboolean button_release_event (GtkWidget *widget _U_, GdkEventButton *event, gpointer user_data)
+{
+ struct graph *g = user_data;
+
+ debug(DBS_FENTRY) puts ("button_release_event()");
+
+ if (event->button == MOUSE_BUTTON_RIGHT)
+ g->grab.grabbed = FALSE;
+
+ if (g->magnify.active)
+ magnify_destroy (g);
+ return TRUE;
+}
+
+static gboolean key_press_event (GtkWidget *widget _U_, GdkEventKey *event, gpointer user_data)
+{
+ struct graph *g = user_data;
+ int step;
+
+ debug(DBS_FENTRY) puts ("key_press_event()");
+
+ if((event->state & GDK_CONTROL_MASK) && (event->state & GDK_SHIFT_MASK))
+ step = 0;
+ else if (event->state & GDK_CONTROL_MASK)
+ step = 1;
+ else if (event->state & GDK_SHIFT_MASK)
+ step = 10;
+ else
+ step = 100;
+
+ switch (event->keyval) {
+ case ' ':
+ toggle_crosshairs (g);
+ break;
+ case 't':
+ toggle_time_origin (g);
+ break;
+ case 's':
+ toggle_seq_origin (g);
+ break;
+ case 'r':
+ case GDK_Home:
+ restore_initial_graph_view (g);
+ break;
+ case 'i':
+ case '+':
+ do_zoom_in_keyboard (g);
+ break;
+ case 'o':
+ case '-':
+ do_zoom_out_keyboard (g);
+ break;
+ case 'm':
+ do_magnify_create (g);
+ break;
+ case 'g':
+ do_select_segment (g);
+ break;
+ case '1':
+ do_rtt_graph (g);
+ break;
+ case '2':
+ do_throughput_graph (g);
+ break;
+ case '3':
+ do_ts_graph_stevens (g);
+ break;
+ case '4':
+ do_ts_graph_tcptrace (g);
+ break;
+ case '5':
+ do_wscale_graph(g);
+ break;
+ case GDK_Left:
+ do_key_motion_left (g, step);
+ break;
+ case GDK_Up:
+ do_key_motion_up (g, step);
+ break;
+ case GDK_Right:
+ do_key_motion_right (g, step);
+ break;
+ case GDK_Down:
+ do_key_motion_down (g, step);
+ break;
+ case GDK_F1:
+ callback_create_help (NULL, NULL);
+ break;
+ default:
+ break;
+ }
+ return TRUE;
+}
+
+static gboolean key_release_event (GtkWidget *widget _U_, GdkEventKey *event, gpointer user_data)
+{
+ struct graph *g = user_data;
+
+ debug(DBS_FENTRY) puts ("key_release_event()");
+
+ if (event->keyval == GDK_Shift_L || event->keyval == GDK_ISO_Prev_Group) {
+ /* g->zoom.flags &= ~ZOOM_OUT; */
+ gtk_toggle_button_set_active (g->zoom.widget.in_toggle, TRUE);
+ }
+ return TRUE;
+}
+
+static gboolean leave_notify_event (GtkWidget *widget _U_, GdkEventCrossing *event _U_, gpointer user_data)
+{
+ struct graph *g = user_data;
+
+ if (g->cross.erase_needed)
+ cross_erase (g);
+
+ return TRUE;
+}
+
+static gboolean enter_notify_event (GtkWidget *widget, GdkEventCrossing *event _U_, gpointer user_data)
+{
+ struct graph *g = user_data;
+
+ /* graph_pixmap_display (g); */
+ if (g->cross.draw) {
+ int x, y;
+ gdk_window_get_pointer (gtk_widget_get_window(widget), &x, &y, 0);
+ cross_draw (g, x, y);
+ }
+ return TRUE;
+}
+
+static void toggle_crosshairs (struct graph *g)
+{
+ g->cross.draw ^= 1;
+#if 0
+ if (g->cross.draw) {
+ int x, y;
+ gdk_window_get_pointer (gtk_widget_get_window(g->drawing_area), &x, &y, 0);
+ cross_draw (g);
+ } else if (g->cross.erase_needed) {
+ cross_erase (g);
+ }
+#endif
+ /* toggle buttons emit their "toggled" signals so don't bother doing
+ * any real work here, it will be done in signal handlers */
+ if (g->cross.draw)
+ gtk_toggle_button_set_active (g->cross.on_toggle, TRUE);
+ else
+ gtk_toggle_button_set_active (g->cross.off_toggle, TRUE);
+}
+
+static void toggle_time_origin (struct graph *g)
+{
+ switch (g->type) {
+ case GRAPH_TSEQ_STEVENS:
+ tseq_stevens_toggle_time_origin (g);
+ break;
+ case GRAPH_TSEQ_TCPTRACE:
+ tseq_tcptrace_toggle_time_origin (g);
+ break;
+ case GRAPH_THROUGHPUT:
+ tput_toggle_time_origin (g);
+ break;
+ default:
+ break;
+ }
+ axis_display (g->x_axis);
+}
+
+static void toggle_seq_origin (struct graph *g)
+{
+ switch (g->type) {
+ case GRAPH_TSEQ_STEVENS:
+ tseq_stevens_toggle_seq_origin (g);
+ axis_display (g->y_axis);
+ break;
+ case GRAPH_TSEQ_TCPTRACE:
+ tseq_tcptrace_toggle_seq_origin (g);
+ axis_display (g->y_axis);
+ break;
+ case GRAPH_RTT:
+ rtt_toggle_seq_origin (g);
+ axis_display (g->x_axis);
+ break;
+ default:
+ break;
+ }
+}
+
+static void restore_initial_graph_view (struct graph *g)
+{
+ g->geom.width = g->wp.width;
+ g->geom.height = g->wp.height;
+ g->geom.x = g->wp.x;
+ g->geom.y = g->wp.y;
+ graph_init_sequence (g);
+}
+
+static int get_num_dsegs (struct graph *g)
+{
+ int count;
+ struct segment *tmp;
+
+ for (tmp=g->segments, count=0; tmp; tmp=tmp->next) {
+ if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
+ g->current->th_sport, g->current->th_dport,
+ &tmp->ip_src, &tmp->ip_dst,
+ tmp->th_sport, tmp->th_dport,
+ COMPARE_CURR_DIR)) {
+ count++;
+ }
+ }
+ return count;
+}
+
+static int get_num_acks (struct graph *g)
+{
+ int count;
+ struct segment *tmp;
+
+ for (tmp=g->segments, count=0; tmp; tmp=tmp->next) {
+ if(!compare_headers(&g->current->ip_src, &g->current->ip_dst,
+ g->current->th_sport, g->current->th_dport,
+ &tmp->ip_src, &tmp->ip_dst,
+ tmp->th_sport, tmp->th_dport,
+ COMPARE_CURR_DIR)) {
+ count++;
+ }
+ }
+ return count;
+}
+
+/*
+ * Stevens-style time-sequence grapH
+ */
+
+static void tseq_stevens_read_config (struct graph *g)
+{
+ debug(DBS_FENTRY) puts ("tseq_stevens_read_config()");
+
+ g->s.tseq_stevens.seq_width = 4;
+ g->s.tseq_stevens.seq_height = 4;
+ g->s.tseq_stevens.flags = 0;
+
+ g->title = (const char ** )g_malloc (2 * sizeof (char *));
+ g->title[0] = "Time/Sequence Graph (Stevens)";
+ g->title[1] = NULL;
+ g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
+ g->y_axis->label[0] = "number[B]";
+ g->y_axis->label[1] = "Sequence";
+ g->y_axis->label[2] = NULL;
+ g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
+ g->x_axis->label[0] = "Time[s]";
+ g->x_axis->label[1] = NULL;
+}
+
+/* Used by both 'stevens' and 'tcptrace' */
+static void tseq_initialize (struct graph *g)
+{
+ debug(DBS_FENTRY) puts ("tseq_initialize()");
+ tseq_get_bounds (g);
+
+ g->x_axis->min = 0;
+ g->y_axis->min = 0;
+
+ switch (g->type) {
+ case GRAPH_TSEQ_STEVENS:
+ tseq_stevens_read_config(g);
+ break;
+ case GRAPH_TSEQ_TCPTRACE:
+ tseq_tcptrace_read_config(g);
+ break;
+ }
+}
+
+
+/* Determine "bounds"
+ * Essentially: look for lowest/highest time and seq in the list of segments
+ * Note that for tcptrace the "(ack + window) sequence number" would normally be expected
+ * to be the upper bound; However, just to be safe, include the data seg sequence numbers
+ * in the comparison for tcptrace
+ * (e.g. to handle the case of only data segments).
+ */
+
+/* ToDo: worry about handling cases such as trying to plot seq of just 1 frame */
+
+static void tseq_get_bounds (struct graph *g)
+{
+ struct segment *tmp;
+ double tim;
+ gboolean data_frame_seen=FALSE;
+ double data_tim_low=0;
+ double data_tim_high=0;
+ guint32 data_seq_cur;
+ guint32 data_seq_nxt;
+ guint32 data_seq_low=0;
+ guint32 data_seq_high=0;
+ gboolean ack_frame_seen=FALSE;
+ double ack_tim_low=0;
+ double ack_tim_high=0;
+ guint32 ack_seq_cur;
+ guint32 ack_seq_low=0;
+ guint32 win_seq_cur;
+ guint32 win_seq_high=0;
+
+ /* go thru all segments to determine "bounds" */
+ for (tmp=g->segments; tmp; tmp=tmp->next) {
+ if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
+ g->current->th_sport, g->current->th_dport,
+ &tmp->ip_src, &tmp->ip_dst,
+ tmp->th_sport, tmp->th_dport,
+ COMPARE_CURR_DIR)) {
+
+ /* "data" seg */
+ tim = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
+ data_seq_cur = tmp->th_seq;
+ data_seq_nxt = data_seq_cur + tmp->th_seglen;
+ if (! data_frame_seen) {
+ data_tim_low = data_tim_high = tim;
+ data_seq_low = data_seq_cur;
+ data_seq_high = data_seq_nxt;
+ data_frame_seen = TRUE;
+ }
+ if (tim < data_tim_low) data_tim_low = tim;
+ if (tim > data_tim_high) data_tim_high = tim;
+ if (data_seq_cur < data_seq_low) data_seq_low = data_seq_cur;
+ if (data_seq_nxt > data_seq_high) data_seq_high = data_seq_nxt;
+ }
+ else { /* ack seg */
+ /* skip ack processing if no ACK (e.g. in RST) */
+ if (TCP_ACK (tmp->th_flags)) {
+ tim = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
+ ack_seq_cur = tmp->th_ack;
+ win_seq_cur = ack_seq_cur + tmp->th_win;
+ if (! ack_frame_seen) {
+ ack_tim_low = ack_tim_high = tim;
+ ack_seq_low = ack_seq_cur;
+ win_seq_high = win_seq_cur;
+ ack_frame_seen = TRUE;
+ }
+ if (tim < ack_tim_low) ack_tim_low = tim;
+ if (tim > ack_tim_high) ack_tim_high = tim;
+ if (ack_seq_cur < ack_seq_low) ack_seq_low = ack_seq_cur;
+ if (win_seq_cur > win_seq_high) win_seq_high = win_seq_cur;
+ }
+ }
+ }
+
+ /* if 'stevens': use only data segments to determine bounds */
+ /* if 'tcptrace': use both data and ack segments to determine bounds */
+ switch (g->type) {
+ case GRAPH_TSEQ_STEVENS:
+ g->bounds.x0 = data_tim_low;
+ g->bounds.width = data_tim_high - data_tim_low;
+ g->bounds.y0 = data_seq_low;
+ g->bounds.height = data_seq_high - data_seq_low;
+ break;
+ case GRAPH_TSEQ_TCPTRACE:
+ /* If (ack_frame_seen == false) -> use 'data' segments.
+ * Else If (data_frame_seen == false) -> use 'ack' segments.
+ * Else -> use both data and ack segments.
+ */
+ g->bounds.x0 = ((data_tim_low <= ack_tim_low && data_frame_seen) || (! ack_frame_seen)) ? data_tim_low : ack_tim_low;
+ g->bounds.width = (((data_tim_high >= ack_tim_high && data_frame_seen) || (! ack_frame_seen)) ? data_tim_high : ack_tim_high) - g->bounds.x0;
+ g->bounds.y0 = ((data_seq_low <= ack_seq_low && data_frame_seen) || (! ack_frame_seen)) ? data_seq_low : ack_seq_low;
+ g->bounds.height = (((data_seq_high >= win_seq_high && data_frame_seen) || (! ack_frame_seen)) ? data_seq_high : win_seq_high) - g->bounds.y0;
+ break;
+ }
+
+ g->zoom.x = (g->geom.width - 1) / g->bounds.width;
+ g->zoom.y = (g->geom.height -1) / g->bounds.height;
+}
+
+
+static void tseq_stevens_make_elmtlist (struct graph *g)
+{
+ struct segment *tmp;
+ struct element *elements, *e;
+ double xx0 = g->bounds.x0, yy0 = g->bounds.y0;
+ guint32 seq_base = (guint32) yy0;
+ guint32 seq_cur;
+
+ debug(DBS_FENTRY) puts ("tseq_stevens_make_elmtlist()");
+ if (g->elists->elements == NULL) {
+ int n = 1 + get_num_dsegs (g);
+ e = elements = (struct element * )g_malloc (n*sizeof (struct element));
+ } else
+ e = elements = g->elists->elements;
+
+ for (tmp=g->segments; tmp; tmp=tmp->next) {
+ double secs, seqno;
+
+ if(!compare_headers(&g->current->ip_src, &g->current->ip_dst,
+ g->current->th_sport, g->current->th_dport,
+ &tmp->ip_src, &tmp->ip_dst,
+ tmp->th_sport, tmp->th_dport,
+ COMPARE_CURR_DIR)) {
+ continue;
+ }
+ /* data seg */
+ seq_cur = tmp->th_seq - seq_base;
+ secs = g->zoom.x * (tmp->rel_secs + tmp->rel_usecs / 1000000.0 - xx0);
+ seqno = g->zoom.y * seq_cur;
+
+ e->type = ELMT_ELLIPSE;
+ e->parent = tmp;
+ e->p.ellipse.dim.width = g->s.tseq_stevens.seq_width;
+ e->p.ellipse.dim.height = g->s.tseq_stevens.seq_height;
+ e->p.ellipse.dim.x = secs - g->s.tseq_stevens.seq_width/2.0;
+ e->p.ellipse.dim.y = seqno + g->s.tseq_stevens.seq_height/2.0;
+ e++;
+ }
+ e->type = ELMT_NONE;
+ g->elists->elements = elements;
+}
+
+static void tseq_stevens_toggle_seq_origin (struct graph *g)
+{
+ g->s.tseq_stevens.flags ^= SEQ_ORIGIN;
+
+ if ((g->s.tseq_stevens.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
+ g->y_axis->min = g->bounds.y0;
+ else /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
+ g->y_axis->min = 0;
+}
+
+static void tseq_stevens_toggle_time_origin (struct graph *g)
+{
+ g->s.tseq_stevens.flags ^= TIME_ORIGIN;
+
+ if ((g->s.tseq_stevens.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
+ g->x_axis->min = g->bounds.x0;
+ else /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
+ g->x_axis->min = 0;
+}
+
+/*
+ * tcptrace-style time-sequence graph
+ */
+
+static void tseq_tcptrace_read_config (struct graph *g)
+{
+
+ /* Black */
+ g->s.tseq_tcptrace.seq_color.pixel=0;
+ g->s.tseq_tcptrace.seq_color.red=0;
+ g->s.tseq_tcptrace.seq_color.green=0;
+ g->s.tseq_tcptrace.seq_color.blue=0;
+
+ /* LightSlateGray */
+ g->s.tseq_tcptrace.ack_color[0].pixel=0;
+ g->s.tseq_tcptrace.ack_color[0].red=0x7777;
+ g->s.tseq_tcptrace.ack_color[0].green=0x8888;
+ g->s.tseq_tcptrace.ack_color[0].blue=0x9999;
+
+ /* LightGray */
+ g->s.tseq_tcptrace.ack_color[1].pixel=0;
+ g->s.tseq_tcptrace.ack_color[1].red=0xd3d3;
+ g->s.tseq_tcptrace.ack_color[1].green=0xd3d3;
+ g->s.tseq_tcptrace.ack_color[1].blue=0xd3d3;
+
+ g->s.tseq_tcptrace.flags = 0;
+
+ g->elists->next = (struct element_list * )
+ g_malloc (sizeof (struct element_list));
+ g->elists->next->next = NULL;
+ g->elists->next->elements = NULL;
+
+ g->title = (const char ** )g_malloc (2 * sizeof (char *));
+ g->title[0] = "Time/Sequence Graph (tcptrace)";
+ g->title[1] = NULL;
+ g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
+ g->y_axis->label[0] = "number[B]";
+ g->y_axis->label[1] = "Sequence";
+ g->y_axis->label[2] = NULL;
+ g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
+ g->x_axis->label[0] = "Time[s]";
+ g->x_axis->label[1] = NULL;
+}
+
+static void tseq_tcptrace_make_elmtlist (struct graph *g)
+{
+ struct segment *tmp;
+ struct element *elements0, *e0; /* list of elmts with prio 0 */
+ struct element *elements1, *e1; /* list of elmts with prio 1 */
+ double xx0, yy0;
+ double p_t = 0; /* ackno, window and time of previous segment */
+ double p_ackno = 0, p_win = 0;
+ gboolean ack_seen=FALSE;
+ int toggle=0;
+ guint32 seq_base;
+ guint32 seq_cur;
+
+ debug(DBS_FENTRY) puts ("tseq_tcptrace_make_elmtlist()");
+
+ if (g->elists->elements == NULL) {
+ int n = 1 + 4*get_num_acks(g);
+ e0 = elements0 = (struct element * )g_malloc (n*sizeof (struct element));
+ } else
+ e0 = elements0 = g->elists->elements;
+
+ if (g->elists->next->elements == NULL ) {
+ int n = 1 + 3*get_num_dsegs(g);
+ e1 = elements1 = (struct element * )g_malloc (n*sizeof (struct element));
+ } else
+ e1 = elements1 = g->elists->next->elements;
+
+ xx0 = g->bounds.x0;
+ yy0 = g->bounds.y0;
+ seq_base = (guint32) yy0;
+
+ for (tmp=g->segments; tmp; tmp=tmp->next) {
+ double secs, data;
+ double x;
+
+ secs = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
+ x = secs - xx0;
+ x *= g->zoom.x;
+ if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
+ g->current->th_sport, g->current->th_dport,
+ &tmp->ip_src, &tmp->ip_dst,
+ tmp->th_sport, tmp->th_dport,
+ COMPARE_CURR_DIR)) {
+ /* forward direction -> we need seqno and amount of data */
+ double yy1, yy2;
+
+ seq_cur = tmp->th_seq - seq_base;
+ if (TCP_SYN (tmp->th_flags) || TCP_FIN (tmp->th_flags))
+ data = 1;
+ else
+ data = tmp->th_seglen;
+
+ yy1 = g->zoom.y * (seq_cur);
+ yy2 = g->zoom.y * (seq_cur + data);
+ e1->type = ELMT_LINE;
+ e1->parent = tmp;
+ /* Set the drawing color */
+ e1->elment_color_p = &g->s.tseq_tcptrace.seq_color;
+ e1->p.line.dim.x1 = e1->p.line.dim.x2 = x;
+ e1->p.line.dim.y1 = yy1;
+ e1->p.line.dim.y2 = yy2;
+ e1++;
+ e1->type = ELMT_LINE;
+ e1->parent = tmp;
+ /* Set the drawing color */
+ e1->elment_color_p = &g->s.tseq_tcptrace.seq_color;
+ e1->p.line.dim.x1 = x - 1;
+ e1->p.line.dim.x2 = x + 1;
+ e1->p.line.dim.y1 = e1->p.line.dim.y2 = yy1;
+ e1++;
+ e1->type = ELMT_LINE;
+ e1->parent = tmp;
+ /* Set the drawing color */
+ e1->elment_color_p = &g->s.tseq_tcptrace.seq_color;
+ e1->p.line.dim.x1 = x + 1;
+ e1->p.line.dim.x2 = x - 1;
+ e1->p.line.dim.y1 = e1->p.line.dim.y2 = yy2;
+ e1++;
+ } else {
+ double ackno, win;
+ if (! TCP_ACK (tmp->th_flags))
+ /* SYN's and RST's do not necessarily have ACK's*/
+ continue;
+ /* backward direction -> we need ackno and window */
+ seq_cur = tmp->th_ack - seq_base;
+ ackno = seq_cur * g->zoom.y;
+ win = tmp->th_win * g->zoom.y;
+
+ /* ack line */
+ if (ack_seen == TRUE) { /* don't plot the first ack */
+ e0->type = ELMT_LINE;
+ e0->parent = tmp;
+ /* Set the drawing color */
+ e0->elment_color_p = &g->s.tseq_tcptrace.ack_color[toggle];
+ e0->p.line.dim.x1 = p_t;
+ e0->p.line.dim.y1 = p_ackno;
+ e0->p.line.dim.x2 = x;
+ e0->p.line.dim.y2 = p_ackno;
+ e0++;
+ e0->type = ELMT_LINE;
+ e0->parent = tmp;
+ /* Set the drawing color */
+ e0->elment_color_p = &g->s.tseq_tcptrace.ack_color[toggle];
+ e0->p.line.dim.x1 = x;
+ e0->p.line.dim.y1 = p_ackno;
+ e0->p.line.dim.x2 = x;
+ e0->p.line.dim.y2 = ackno!=p_ackno || ackno<4 ? ackno : ackno-4;
+ e0++;
+ /* window line */
+ e0->type = ELMT_LINE;
+ e0->parent = tmp;
+ /* Set the drawing color */
+ e0->elment_color_p = &g->s.tseq_tcptrace.ack_color[toggle];
+ e0->p.line.dim.x1 = p_t;
+ e0->p.line.dim.y1 = p_win + p_ackno;
+ e0->p.line.dim.x2 = x;
+ e0->p.line.dim.y2 = p_win + p_ackno;
+ e0++;
+ e0->type = ELMT_LINE;
+ e0->parent = tmp;
+ /* Set the drawing color */
+ e0->elment_color_p = &g->s.tseq_tcptrace.ack_color[toggle];
+ e0->p.line.dim.x1 = x;
+ e0->p.line.dim.y1 = p_win + p_ackno;
+ e0->p.line.dim.x2 = x;
+ e0->p.line.dim.y2 = win + ackno;
+ e0++;
+ toggle = 1^toggle;
+ }
+ ack_seen = TRUE;
+ p_ackno = ackno;
+ p_win = win;
+ p_t = x;
+ }
+ }
+ e0->type = ELMT_NONE;
+ e1->type = ELMT_NONE;
+ g->elists->elements = elements0;
+ g->elists->next->elements = elements1;
+}
+
+static void tseq_tcptrace_toggle_seq_origin (struct graph *g)
+{
+ g->s.tseq_tcptrace.flags ^= SEQ_ORIGIN;
+
+ if ((g->s.tseq_tcptrace.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
+ g->y_axis->min = g->bounds.y0;
+ else /* g->tseq_stevens.flags & SEQ_ORIGIN == SEQ_ORIGIN_ISN */
+ g->y_axis->min = 0;
+}
+
+static void tseq_tcptrace_toggle_time_origin (struct graph *g)
+{
+ g->s.tseq_tcptrace.flags ^= TIME_ORIGIN;
+
+ if ((g->s.tseq_tcptrace.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
+ g->x_axis->min = g->bounds.x0;
+ else /* g->tseq_stevens.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
+ g->x_axis->min = 0;
+}
+
+/*
+ * throughput graph
+ */
+
+static void tput_make_elmtlist (struct graph *g)
+{
+ struct segment *tmp, *oldest;
+ struct element *elements, *e;
+ int i, sum=0;
+ double dtime, tput;
+
+ if (g->elists->elements == NULL) {
+ int n = 1 + get_num_dsegs (g);
+ e = elements = (struct element * )g_malloc (n*sizeof (struct element));
+ } else
+ e = elements = g->elists->elements;
+
+ for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) {
+ double time_val = tmp->rel_secs + tmp->rel_usecs/1000000.0;
+ dtime = time_val - (oldest->rel_secs + oldest->rel_usecs/1000000.0);
+ if (i>g->s.tput.nsegs) {
+ sum -= oldest->th_seglen;
+ oldest=oldest->next;
+ }
+ sum += tmp->th_seglen;
+ tput = sum / dtime;
+ /* debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput); */
+
+ e->type = ELMT_ELLIPSE;
+ e->parent = tmp;
+ e->p.ellipse.dim.width = g->s.tput.width;
+ e->p.ellipse.dim.height = g->s.tput.height;
+ e->p.ellipse.dim.x = g->zoom.x*(time_val - g->bounds.x0) - g->s.tput.width/2.0;
+ e->p.ellipse.dim.y = g->zoom.y*tput + g->s.tput.height/2.0;
+ e++;
+ }
+ e->type = ELMT_NONE;
+ g->elists->elements = elements;
+}
+
+/* Purpose of <graph_type>_initialize functions:
+ * - find maximum and minimum for both axes
+ * - call setup routine for style struct */
+static void tput_initialize (struct graph *g)
+{
+ struct segment *tmp, *oldest, *last;
+ int i, sum=0;
+ double dtime, tput, tputmax=0;
+ double t, t0, tmax = 0, yy0, ymax;
+
+ debug(DBS_FENTRY) puts ("tput_initialize()");
+
+ tput_read_config(g);
+
+ for (last=g->segments; last->next; last=last->next);
+ for (oldest=g->segments,tmp=g->segments->next,i=0; tmp; tmp=tmp->next,i++) {
+ dtime = tmp->rel_secs + tmp->rel_usecs/1000000.0 -
+ (oldest->rel_secs + oldest->rel_usecs/1000000.0);
+ if (i>g->s.tput.nsegs) {
+ sum -= oldest->th_seglen;
+ oldest=oldest->next;
+ }
+ sum += tmp->th_seglen;
+ tput = sum / dtime;
+ debug(DBS_TPUT_ELMTS) printf ("tput=%f\n", tput);
+ if (tput > tputmax)
+ tputmax = tput;
+ t = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
+ if (t > tmax)
+ tmax = t;
+ }
+
+ t0 = g->segments->rel_secs + g->segments->rel_usecs / 1000000.0;
+ yy0 = 0;
+ ymax = tputmax;
+
+ g->bounds.x0 = t0;
+ g->bounds.y0 = yy0;
+ g->bounds.width = tmax - t0;
+ g->bounds.height = ymax - yy0;
+ g->zoom.x = (g->geom.width - 1) / g->bounds.width;
+ g->zoom.y = (g->geom.height -1) / g->bounds.height;
+}
+
+static void tput_read_config (struct graph *g)
+{
+ debug(DBS_FENTRY) puts ("tput_read_config()");
+
+ g->s.tput.width = 4;
+ g->s.tput.height = 4;
+ g->s.tput.nsegs = 20;
+
+ g->title = (const char ** )g_malloc (2 * sizeof (char *));
+ g->title[0] = "Throughput Graph";
+ g->title[1] = NULL;
+ g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
+ g->y_axis->label[0] = "[B/s]";
+ g->y_axis->label[1] = "Throughput";
+ g->y_axis->label[2] = NULL;
+ g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
+ g->x_axis->label[0] = "Time[s]";
+ g->x_axis->label[1] = NULL;
+ g->s.tput.flags = 0;
+}
+
+static void tput_toggle_time_origin (struct graph *g)
+{
+ g->s.tput.flags ^= TIME_ORIGIN;
+
+ if ((g->s.tput.flags & TIME_ORIGIN) == TIME_ORIGIN_CAP)
+ g->x_axis->min = g->bounds.x0;
+ else /* g->s.tput.flags & TIME_ORIGIN == TIME_ORIGIN_CONN */
+ g->x_axis->min = 0;
+}
+
+/* RTT graph */
+
+static void rtt_read_config (struct graph *g)
+{
+ debug(DBS_FENTRY) puts ("rtt_read_config()");
+
+ g->s.rtt.width = 4;
+ g->s.rtt.height = 4;
+ g->s.rtt.flags = 0;
+
+ g->title = (const char ** )g_malloc (2 * sizeof (char *));
+ g->title[0] = "Round Trip Time Graph";
+ g->title[1] = NULL;
+ g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
+ g->y_axis->label[0] = "RTT [s]";
+ g->y_axis->label[1] = NULL;
+ g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
+ g->x_axis->label[0] = "Sequence Number[B]";
+ g->x_axis->label[1] = NULL;
+}
+
+static void rtt_initialize (struct graph *g)
+{
+ struct segment *tmp, *first=NULL;
+ struct unack *unack = NULL, *u;
+ double rttmax=0;
+ double xx0, yy0, ymax;
+ guint32 xmax = 0;
+ guint32 seq_base = 0;
+
+ debug(DBS_FENTRY) puts ("rtt_initialize()");
+
+ rtt_read_config (g);
+
+ for (tmp=g->segments; tmp; tmp=tmp->next) {
+ if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
+ g->current->th_sport, g->current->th_dport,
+ &tmp->ip_src, &tmp->ip_dst,
+ tmp->th_sport, tmp->th_dport,
+ COMPARE_CURR_DIR)) {
+ guint32 seqno = tmp->th_seq;
+
+ if (!first) {
+ first= tmp;
+ seq_base = seqno;
+ }
+ seqno -= seq_base;
+ if (tmp->th_seglen && !rtt_is_retrans (unack, seqno)) {
+ double time_val = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
+ u = rtt_get_new_unack (time_val, seqno);
+ if (!u) return;
+ rtt_put_unack_on_list (&unack, u);
+ }
+
+ if (seqno + tmp->th_seglen > xmax)
+ xmax = seqno + tmp->th_seglen;
+ } else if (first) {
+ guint32 ackno = tmp->th_ack -seq_base;
+ double time_val = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
+ struct unack *v;
+
+ for (u=unack; u; u=v)
+ if (ackno > u->seqno) {
+ double rtt = time_val - u->time;
+ if (rtt > rttmax)
+ rttmax = rtt;
+ v=u->next;
+ rtt_delete_unack_from_list (&unack, u);
+ } else
+ v=u->next;
+ }
+ }
+
+ xx0 = seq_base;
+ yy0 = 0;
+ ymax = rttmax;
+
+ g->bounds.x0 = xx0;
+ g->bounds.y0 = yy0;
+ g->bounds.width = xmax;
+ g->bounds.height = ymax - yy0;
+ g->zoom.x = g->geom.width / g->bounds.width;
+ g->zoom.y = g->geom.height / g->bounds.height;
+}
+
+static int rtt_is_retrans (struct unack *list, unsigned int seqno)
+{
+ struct unack *u;
+
+ for (u=list; u; u=u->next)
+ if (u->seqno== seqno)
+ return TRUE;
+
+ return FALSE;
+}
+
+static struct unack *rtt_get_new_unack (double time_val, unsigned int seqno)
+{
+ struct unack *u;
+
+ u = (struct unack * )g_malloc (sizeof (struct unack));
+ if (!u)
+ return NULL;
+ u->next = NULL;
+ u->time = time_val;
+ u->seqno = seqno;
+ return u;
+}
+
+static void rtt_put_unack_on_list (struct unack **l, struct unack *new)
+{
+ struct unack *u, *list = *l;
+
+ for (u=list; u; u=u->next)
+ if (!u->next)
+ break;
+
+ if (u)
+ u->next = new;
+ else
+ *l = new;
+}
+
+static void rtt_delete_unack_from_list (struct unack **l, struct unack *dead)
+{
+ struct unack *u, *list = *l;
+
+ if (!dead || !list)
+ return;
+
+ if (dead==list) {
+ *l = list->next;
+ g_free (list);
+ } else
+ for (u=list; u; u=u->next)
+ if (u->next == dead) {
+ u->next = u->next->next;
+ g_free (dead);
+ break;
+ }
+}
+
+static void rtt_make_elmtlist (struct graph *g)
+{
+ struct segment *tmp;
+ struct unack *unack = NULL, *u;
+ struct element *elements, *e;
+ guint32 seq_base = (guint32) g->bounds.x0;
+
+ debug(DBS_FENTRY) puts ("rtt_make_elmtlist()");
+
+ if (g->elists->elements == NULL) {
+ int n = 1 + get_num_dsegs (g);
+ e = elements = (struct element * )g_malloc (n*sizeof (struct element));
+ } else {
+ e = elements = g->elists->elements;
+ }
+
+ for (tmp=g->segments; tmp; tmp=tmp->next) {
+ if(compare_headers(&g->current->ip_src, &g->current->ip_dst,
+ g->current->th_sport, g->current->th_dport,
+ &tmp->ip_src, &tmp->ip_dst,
+ tmp->th_sport, tmp->th_dport,
+ COMPARE_CURR_DIR)) {
+ guint32 seqno = tmp->th_seq -seq_base;
+
+ if (tmp->th_seglen && !rtt_is_retrans (unack, seqno)) {
+ double time_val = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
+ u = rtt_get_new_unack (time_val, seqno);
+ if (!u) return;
+ rtt_put_unack_on_list (&unack, u);
+ }
+ } else {
+ guint32 ackno = tmp->th_ack -seq_base;
+ double time_val = tmp->rel_secs + tmp->rel_usecs / 1000000.0;
+ struct unack *v;
+
+ for (u=unack; u; u=v)
+ if (ackno > u->seqno) {
+ double rtt = time_val - u->time;
+
+ e->type = ELMT_ELLIPSE;
+ e->parent = tmp;
+ e->p.ellipse.dim.width = g->s.rtt.width;
+ e->p.ellipse.dim.height = g->s.rtt.height;
+ e->p.ellipse.dim.x = g->zoom.x * u->seqno - g->s.rtt.width/2.0;
+ e->p.ellipse.dim.y = g->zoom.y * rtt + g->s.rtt.height/2.0;
+ e++;
+
+ v=u->next;
+ rtt_delete_unack_from_list (&unack, u);
+ } else
+ v=u->next;
+ }
+ }
+ e->type = ELMT_NONE;
+ g->elists->elements = elements;
+}
+
+static void rtt_toggle_seq_origin (struct graph *g)
+{
+ g->s.rtt.flags ^= SEQ_ORIGIN;
+
+ if ((g->s.rtt.flags & SEQ_ORIGIN) == SEQ_ORIGIN_ZERO)
+ g->x_axis->min = g->bounds.x0;
+ else
+ g->x_axis->min = 0;
+}
+
+/* WSCALE Graph */
+
+static void wscale_read_config(struct graph* g)
+{
+ debug(DBS_FENTRY) puts ("wscale_read_config()");
+
+ g->s.wscale.win_width = 4;
+ g->s.wscale.win_height = 4;
+ g->s.wscale.flags = 0;
+
+ g->title = (const char ** )g_malloc (2 * sizeof (char *));
+ g->title[0] = "Window Scaling Graph";
+ g->title[1] = NULL;
+ g->y_axis->label = (const char ** )g_malloc (3 * sizeof (char * ));
+ g->y_axis->label[0] = "[bytes]";
+ g->y_axis->label[1] = "Windowsize";
+ g->y_axis->label[2] = NULL;
+ g->x_axis->label = (const char ** )g_malloc (2 * sizeof (char * ));
+ g->x_axis->label[0] = "Time [s]";
+ g->x_axis->label[1] = NULL;
+}
+
+/*
+ (1) Find maximum and minimum values for Window-Size(scaled) and seconds
+ (2) call function to define window related values
+*/
+static void wscale_initialize(struct graph* g)
+{
+
+ struct segment* segm = NULL;
+ guint32 wsize_max = 0;
+ guint32 wsize_min = 0;
+ gdouble sec_max = 0.0;
+ gdouble sec_base = -1.0;
+
+ wscale_read_config (g);
+
+ debug(DBS_FENTRY) puts ("wscale_initialize()");
+
+ for (segm = g->segments; segm; segm = segm->next)
+ {
+ if (compare_headers(&g->current->ip_src, &g->current->ip_dst,
+ g->current->th_sport, g->current->th_dport,
+ &segm->ip_src, &segm->ip_dst,
+ segm->th_sport, segm->th_dport,
+ COMPARE_CURR_DIR))
+ {
+ gdouble sec = segm->rel_secs + ( segm->rel_usecs / 1000000.0 );
+ guint16 flags = segm->th_flags;
+ guint32 wsize = segm->th_win;
+
+ /* only data segments */
+ if ( (flags & (TH_SYN|TH_RST)) == 0 )
+ if ( wsize > wsize_max )
+ wsize_max = wsize;
+
+ /* remind time of first probe */
+ if ( sec_base < 0 && sec > 0 )
+ sec_base = sec;
+
+ if ( sec_max < sec )
+ sec_max = sec;
+
+ }
+
+ }
+
+ g->bounds.x0 = 0;
+ g->bounds.y0 = wsize_min;
+ g->bounds.width = sec_max - sec_base + 5;
+ g->bounds.height = wsize_max + 5;
+ g->zoom.x = g->geom.width / g->bounds.width;
+ g->zoom.y = g->geom.height / g->bounds.height;
+
+}
+
+/*
+ (1) Fill & allocate memory for segments times elements,
+*/
+static void wscale_make_elmtlist(struct graph* g)
+{
+ struct segment* segm = NULL;
+ struct element* elements = NULL;
+ struct element* e = NULL;
+ gdouble sec_base = -1.0;
+
+ debug(DBS_FENTRY) puts ("wscale_make_elmtlist()");
+
+ /* Allocate memory for elements if not already done */
+ if (g->elists->elements == NULL)
+ {
+ int n = 1 + get_num_dsegs(g);
+ e = elements = (struct element*)g_malloc(n*sizeof(struct element));
+ }
+ else
+ e = elements = g->elists->elements;
+
+
+ for ( segm = g->segments; segm; segm = segm->next )
+ {
+ if (compare_headers(&g->current->ip_src, &g->current->ip_dst,
+ g->current->th_sport, g->current->th_dport,
+ &segm->ip_src, &segm->ip_dst,
+ segm->th_sport, segm->th_dport,
+ COMPARE_CURR_DIR))
+ {
+ gdouble sec = segm->rel_secs + (segm->rel_usecs / 1000000.0);
+ guint16 flags = segm->th_flags;
+ guint32 wsize = segm->th_win;
+
+ /* remind time of first probe */
+ if ( sec_base < 0 && sec > 0 )
+ sec_base = sec;
+
+ /* only data or ack segments */
+ if ( (flags & (TH_SYN|TH_RST)) == 0 )
+ {
+ e->type = ELMT_ELLIPSE;
+ e->parent = segm;
+ e->p.ellipse.dim.width = g->s.wscale.win_width;
+ e->p.ellipse.dim.height = g->s.wscale.win_height;
+ e->p.ellipse.dim.x = g->zoom.x * (sec - sec_base) - g->s.wscale.win_width / 2.0;
+ e->p.ellipse.dim.y = g->zoom.y * wsize - g->s.wscale.win_height / 2.0;
+ e++;
+ }
+ }
+ }
+ /* finished populating element list */
+ e->type = ELMT_NONE;
+ g->elists->elements = elements;
+}
+
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+/* replacement of Unix rint() for Windows */
+static int rint (double x)
+{
+ char *buf;
+ int i,dec,sig;
+
+ buf = _fcvt(x, 0, &dec, &sig);
+ i = atoi(buf);
+ if(sig == 1) {
+ i = i * -1;
+ }
+ return(i);
+}
+#endif
+
+gboolean tcp_graph_selected_packet_enabled(frame_data *current_frame, epan_dissect_t *edt, gpointer callback_data _U_)
+{
+ return current_frame != NULL ? (edt->pi.ipproto == IP_PROTO_TCP) : FALSE;
+}
+
+
+void
+register_tap_listener_tcp_graph(void)
+{
+}
+
diff --git a/ui/gtk/text_import.c b/ui/gtk/text_import.c
new file mode 100644
index 0000000000..5c032739e7
--- /dev/null
+++ b/ui/gtk/text_import.c
@@ -0,0 +1,974 @@
+/* text_import.c
+ * State machine for text import
+ * November 2010, Jaap Keuter <jaap.keuter@xs4all.nl>
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * Based on text2pcap.c by Ashok Narayanan <ashokn@cisco.com>
+ *
+ * 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.
+ */
+
+/*******************************************************************************
+ *
+ * This utility reads in an ASCII hexdump of this common format:
+ *
+ * 00000000 00 E0 1E A7 05 6F 00 10 5A A0 B9 12 08 00 46 00 .....o..Z.....F.
+ * 00000010 03 68 00 00 00 00 0A 2E EE 33 0F 19 08 7F 0F 19 .h.......3......
+ * 00000020 03 80 94 04 00 00 10 01 16 A2 0A 00 03 50 00 0C .............P..
+ * 00000030 01 01 0F 19 03 80 11 01 1E 61 00 0C 03 01 0F 19 .........a......
+ *
+ * Each bytestring line consists of an offset, one or more bytes, and
+ * text at the end. An offset is defined as a hex string of more than
+ * two characters. A byte is defined as a hex string of exactly two
+ * characters. The text at the end is ignored, as is any text before
+ * the offset. Bytes read from a bytestring line are added to the
+ * current packet only if all the following conditions are satisfied:
+ *
+ * - No text appears between the offset and the bytes (any bytes appearing after
+ * such text would be ignored)
+ *
+ * - The offset must be arithmetically correct, i.e. if the offset is 00000020, then
+ * exactly 32 bytes must have been read into this packet before this. If the offset
+ * is wrong, the packet is immediately terminated
+ *
+ * A packet start is signalled by a zero offset.
+ *
+ * Lines starting with #TEXT2PCAP are directives. These allow the user
+ * to embed instructions into the capture file which allows text2pcap
+ * to take some actions (e.g. specifying the encapsulation
+ * etc.). Currently no directives are implemented.
+ *
+ * Lines beginning with # which are not directives are ignored as
+ * comments. Currently all non-hexdump text is ignored by text2pcap;
+ * in the future, text processing may be added, but lines prefixed
+ * with '#' will still be ignored.
+ *
+ * The output is a libpcap packet containing Ethernet frames by
+ * default. This program takes options which allow the user to add
+ * dummy Ethernet, IP and UDP or TCP headers to the packets in order
+ * to allow dumps of L3 or higher protocols to be decoded.
+ *
+ * Considerable flexibility is built into this code to read hexdumps
+ * of slightly different formats. For example, any text prefixing the
+ * hexdump line is dropped (including mail forwarding '>'). The offset
+ * can be any hex number of four digits or greater.
+ *
+ * This converter cannot read a single packet greater than 64K. Packet
+ * snaplength is automatically set to 64K.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/*
+ * Just make sure we include the prototype for strptime as well
+ * (needed for glibc 2.2) but make sure we do this only if not
+ * yet defined.
+ */
+#ifndef __USE_XOPEN
+# define __USE_XOPEN
+#endif
+#ifndef _XOPEN_SOURCE
+# define _XOPEN_SOURCE 600
+#endif
+
+/*
+ * Defining _XOPEN_SOURCE is needed on some platforms, e.g. platforms
+ * using glibc, to expand the set of things system header files define.
+ *
+ * Unfortunately, on other platforms, such as some versions of Solaris
+ * (including Solaris 10), it *reduces* that set as well, causing
+ * strptime() not to be declared, presumably because the version of the
+ * X/Open spec that _XOPEN_SOURCE implies doesn't include strptime() and
+ * blah blah blah namespace pollution blah blah blah.
+ *
+ * So we define __EXTENSIONS__ so that "strptime()" is declared.
+ */
+#ifndef __EXTENSIONS__
+# define __EXTENSIONS__
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wsutil/file_util.h>
+
+#include <time.h>
+#include <glib.h>
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <errno.h>
+#include <assert.h>
+
+#include <epan/tvbuff.h>
+#include <wsutil/crc32.h>
+#include <epan/in_cksum.h>
+
+#ifdef NEED_STRPTIME_H
+# include "wsutil/strptime.h"
+#endif
+
+#include "text_import.h"
+#include "text_import_scanner.h"
+
+/*--- Options --------------------------------------------------------------------*/
+
+/* Debug level */
+static int debug = 0;
+
+/* Dummy Ethernet header */
+static int hdr_ethernet = FALSE;
+static unsigned long hdr_ethernet_proto = 0;
+
+/* Dummy IP header */
+static int hdr_ip = FALSE;
+static long hdr_ip_proto = 0;
+
+/* Dummy UDP header */
+static int hdr_udp = FALSE;
+static unsigned long hdr_dest_port = 0;
+static unsigned long hdr_src_port = 0;
+
+/* Dummy TCP header */
+static int hdr_tcp = FALSE;
+
+/* Dummy SCTP header */
+static int hdr_sctp = FALSE;
+static unsigned long hdr_sctp_src = 0;
+static unsigned long hdr_sctp_dest = 0;
+static unsigned long hdr_sctp_tag = 0;
+
+/* Dummy DATA chunk header */
+static int hdr_data_chunk = FALSE;
+static unsigned char hdr_data_chunk_type = 0;
+static unsigned char hdr_data_chunk_bits = 3;
+static unsigned long hdr_data_chunk_tsn = 0;
+static unsigned short hdr_data_chunk_sid = 0;
+static unsigned short hdr_data_chunk_ssn = 0;
+static unsigned long hdr_data_chunk_ppid = 0;
+
+
+/*--- Local data -----------------------------------------------------------------*/
+
+/* This is where we store the packet currently being built */
+static unsigned char *packet_buf;
+static unsigned long curr_offset = 0;
+static unsigned long max_offset = IMPORT_MAX_PACKET;
+static unsigned long packet_start = 0;
+static void start_new_packet (void);
+
+/* This buffer contains strings present before the packet offset 0 */
+#define PACKET_PREAMBLE_MAX_LEN 2048
+static unsigned char packet_preamble[PACKET_PREAMBLE_MAX_LEN+1];
+static int packet_preamble_len = 0;
+
+/* Time code of packet, derived from packet_preamble */
+static time_t ts_sec = 0;
+static guint32 ts_usec = 0;
+static char *ts_fmt = NULL;
+static struct tm timecode_default;
+
+static wtap_dumper* wdh;
+
+/* HDR_ETH Offset base to parse */
+static unsigned long offset_base = 16;
+
+/* ----- State machine -----------------------------------------------------------*/
+
+/* Current state of parser */
+typedef enum {
+ INIT, /* Waiting for start of new packet */
+ START_OF_LINE, /* Starting from beginning of line */
+ READ_OFFSET, /* Just read the offset */
+ READ_BYTE, /* Just read a byte */
+ READ_TEXT /* Just read text - ignore until EOL */
+} parser_state_t;
+static parser_state_t state = INIT;
+
+static const char *state_str[] = {"Init",
+ "Start-of-line",
+ "Offset",
+ "Byte",
+ "Text"
+};
+
+static const char *token_str[] = {"",
+ "Byte",
+ "Offset",
+ "Directive",
+ "Text",
+ "End-of-line"
+};
+
+/* ----- Skeleton Packet Headers --------------------------------------------------*/
+
+typedef struct {
+ guint8 dest_addr[6];
+ guint8 src_addr[6];
+ guint16 l3pid;
+} hdr_ethernet_t;
+
+static hdr_ethernet_t HDR_ETHERNET = {
+ {0x20, 0x52, 0x45, 0x43, 0x56, 0x00},
+ {0x20, 0x53, 0x45, 0x4E, 0x44, 0x00},
+ 0};
+
+typedef struct {
+ guint8 ver_hdrlen;
+ guint8 dscp;
+ guint16 packet_length;
+ guint16 identification;
+ guint8 flags;
+ guint8 fragment;
+ guint8 ttl;
+ guint8 protocol;
+ guint16 hdr_checksum;
+ guint32 src_addr;
+ guint32 dest_addr;
+} hdr_ip_t;
+
+static hdr_ip_t HDR_IP =
+ {0x45, 0, 0, 0x3412, 0, 0, 0xff, 0, 0, 0x01010101, 0x02020202};
+
+static struct { /* pseudo header for checksum calculation */
+ guint32 src_addr;
+ guint32 dest_addr;
+ guint8 zero;
+ guint8 protocol;
+ guint16 length;
+} pseudoh;
+
+typedef struct {
+ guint16 source_port;
+ guint16 dest_port;
+ guint16 length;
+ guint16 checksum;
+} hdr_udp_t;
+
+static hdr_udp_t HDR_UDP = {0, 0, 0, 0};
+
+typedef struct {
+ guint16 source_port;
+ guint16 dest_port;
+ guint32 seq_num;
+ guint32 ack_num;
+ guint8 hdr_length;
+ guint8 flags;
+ guint16 window;
+ guint16 checksum;
+ guint16 urg;
+} hdr_tcp_t;
+
+static hdr_tcp_t HDR_TCP = {0, 0, 0, 0, 0x50, 0, 0, 0, 0};
+
+typedef struct {
+ guint16 src_port;
+ guint16 dest_port;
+ guint32 tag;
+ guint32 checksum;
+} hdr_sctp_t;
+
+static hdr_sctp_t HDR_SCTP = {0, 0, 0, 0};
+
+typedef struct {
+ guint8 type;
+ guint8 bits;
+ guint16 length;
+ guint32 tsn;
+ guint16 sid;
+ guint16 ssn;
+ guint32 ppid;
+} hdr_data_chunk_t;
+
+static hdr_data_chunk_t HDR_DATA_CHUNK = {0, 0, 0, 0, 0, 0, 0};
+
+/* Link-layer type; see net/bpf.h for details */
+static unsigned long pcap_link_type = 1; /* Default is DLT-EN10MB */
+
+/*----------------------------------------------------------------------
+ * Parse a single hex number
+ * Will abort the program if it can't parse the number
+ * Pass in TRUE if this is an offset, FALSE if not
+ */
+static unsigned long
+parse_num (const char *str, int offset)
+{
+ unsigned long num;
+ char *c;
+
+ num = strtoul(str, &c, offset ? offset_base : 16);
+ if (c==str) {
+ fprintf(stderr, "FATAL ERROR: Bad hex number? [%s]\n", str);
+ }
+ return num;
+}
+
+/*----------------------------------------------------------------------
+ * Write this byte into current packet
+ */
+static void
+write_byte (const char *str)
+{
+ unsigned long num;
+
+ num = parse_num(str, FALSE);
+ packet_buf[curr_offset] = (unsigned char) num;
+ curr_offset ++;
+ if (curr_offset >= max_offset) /* packet full */
+ start_new_packet();
+}
+
+/*----------------------------------------------------------------------
+ * Remove bytes from the current packet
+ */
+static void
+unwrite_bytes (unsigned long nbytes)
+{
+ curr_offset -= nbytes;
+}
+
+/*----------------------------------------------------------------------
+ * Determin SCTP chunk padding length
+ */
+static unsigned long
+number_of_padding_bytes (unsigned long length)
+{
+ unsigned long remainder;
+
+ remainder = length % 4;
+
+ if (remainder == 0)
+ return 0;
+ else
+ return 4 - remainder;
+}
+
+/*----------------------------------------------------------------------
+ * Write current packet out
+ */
+void
+write_current_packet (void)
+{
+ int prefix_length = 0;
+ int proto_length = 0;
+ int ip_length = 0;
+ int eth_trailer_length = 0;
+ int prefix_index = 0;
+ int i, padding_length;
+
+ if (curr_offset > 0) {
+ /* Write the packet */
+
+ /* Compute packet length */
+ prefix_length = 0;
+ if (hdr_data_chunk) { prefix_length += sizeof(HDR_DATA_CHUNK); }
+ if (hdr_sctp) { prefix_length += sizeof(HDR_SCTP); }
+ if (hdr_udp) { prefix_length += sizeof(HDR_UDP); proto_length = prefix_length + curr_offset; }
+ if (hdr_tcp) { prefix_length += sizeof(HDR_TCP); proto_length = prefix_length + curr_offset; }
+ if (hdr_ip) {
+ prefix_length += sizeof(HDR_IP);
+ ip_length = prefix_length + curr_offset + ((hdr_data_chunk) ? number_of_padding_bytes(curr_offset) : 0);
+ }
+ if (hdr_ethernet) { prefix_length += sizeof(HDR_ETHERNET); }
+
+ /* Make room for dummy header */
+ memmove(&packet_buf[prefix_length], packet_buf, curr_offset);
+
+ if (hdr_ethernet) {
+ /* Pad trailer */
+ if (prefix_length + curr_offset < 60) {
+ eth_trailer_length = 60 - (prefix_length + curr_offset);
+ }
+ }
+
+ /* Write Ethernet header */
+ if (hdr_ethernet) {
+ HDR_ETHERNET.l3pid = g_htons(hdr_ethernet_proto);
+ memcpy(&packet_buf[prefix_index], &HDR_ETHERNET, sizeof(HDR_ETHERNET));
+ prefix_index += sizeof(HDR_ETHERNET);
+ }
+
+ /* Write IP header */
+ if (hdr_ip) {
+ vec_t cksum_vector[1];
+
+ HDR_IP.packet_length = g_htons(ip_length);
+ HDR_IP.protocol = (guint8) hdr_ip_proto;
+ HDR_IP.hdr_checksum = 0;
+ cksum_vector[0].ptr = (guint8 *)&HDR_IP; cksum_vector[0].len = sizeof(HDR_IP);
+ HDR_IP.hdr_checksum = in_cksum(cksum_vector, 1);
+
+ memcpy(&packet_buf[prefix_index], &HDR_IP, sizeof(HDR_IP));
+ prefix_index += sizeof(HDR_IP);
+ }
+
+ /* initialize pseudo header for checksum calculation */
+ pseudoh.src_addr = HDR_IP.src_addr;
+ pseudoh.dest_addr = HDR_IP.dest_addr;
+ pseudoh.zero = 0;
+ pseudoh.protocol = (guint8) hdr_ip_proto;
+ pseudoh.length = g_htons(proto_length);
+
+ /* Write UDP header */
+ if (hdr_udp) {
+ vec_t cksum_vector[3];
+
+ HDR_UDP.source_port = g_htons(hdr_src_port);
+ HDR_UDP.dest_port = g_htons(hdr_dest_port);
+ HDR_UDP.length = g_htons(proto_length);
+
+ HDR_UDP.checksum = 0;
+ cksum_vector[0].ptr = (guint8 *)&pseudoh; cksum_vector[0].len = sizeof(pseudoh);
+ cksum_vector[1].ptr = (guint8 *)&HDR_UDP; cksum_vector[1].len = sizeof(HDR_UDP);
+ cksum_vector[2].ptr = &packet_buf[prefix_length]; cksum_vector[2].len = curr_offset;
+ HDR_UDP.checksum = in_cksum(cksum_vector, 3);
+
+ memcpy(&packet_buf[prefix_index], &HDR_UDP, sizeof(HDR_UDP));
+ prefix_index += sizeof(HDR_UDP);
+ }
+
+ /* Write TCP header */
+ if (hdr_tcp) {
+ vec_t cksum_vector[3];
+
+ HDR_TCP.source_port = g_htons(hdr_src_port);
+ HDR_TCP.dest_port = g_htons(hdr_dest_port);
+ /* HDR_TCP.seq_num already correct */
+ HDR_TCP.window = g_htons(0x2000);
+
+ HDR_TCP.checksum = 0;
+ cksum_vector[0].ptr = (guint8 *)&pseudoh; cksum_vector[0].len = sizeof(pseudoh);
+ cksum_vector[1].ptr = (guint8 *)&HDR_TCP; cksum_vector[1].len = sizeof(HDR_TCP);
+ cksum_vector[2].ptr = &packet_buf[prefix_length]; cksum_vector[2].len = curr_offset;
+ HDR_TCP.checksum = in_cksum(cksum_vector, 3);
+
+ memcpy(&packet_buf[prefix_index], &HDR_TCP, sizeof(HDR_TCP));
+ prefix_index += sizeof(HDR_TCP);
+ }
+
+ /* Compute DATA chunk header and append padding */
+ if (hdr_data_chunk) {
+ HDR_DATA_CHUNK.type = hdr_data_chunk_type;
+ HDR_DATA_CHUNK.bits = hdr_data_chunk_bits;
+ HDR_DATA_CHUNK.length = g_htons(curr_offset + sizeof(HDR_DATA_CHUNK));
+ HDR_DATA_CHUNK.tsn = g_htonl(hdr_data_chunk_tsn);
+ HDR_DATA_CHUNK.sid = g_htons(hdr_data_chunk_sid);
+ HDR_DATA_CHUNK.ssn = g_htons(hdr_data_chunk_ssn);
+ HDR_DATA_CHUNK.ppid = g_htonl(hdr_data_chunk_ppid);
+
+ padding_length = number_of_padding_bytes(curr_offset);
+ for (i=0; i<padding_length; i++)
+ packet_buf[prefix_length+curr_offset+i] = 0;
+ curr_offset += padding_length;
+ }
+
+ /* Write SCTP header */
+ if (hdr_sctp) {
+ HDR_SCTP.src_port = g_htons(hdr_sctp_src);
+ HDR_SCTP.dest_port = g_htons(hdr_sctp_dest);
+ HDR_SCTP.tag = g_htonl(hdr_sctp_tag);
+ HDR_SCTP.checksum = g_htonl(0);
+
+ HDR_SCTP.checksum = crc32c_calculate(&HDR_SCTP, sizeof(HDR_SCTP), CRC32C_PRELOAD);
+ if (hdr_data_chunk)
+ HDR_SCTP.checksum = crc32c_calculate(&HDR_DATA_CHUNK, sizeof(HDR_DATA_CHUNK), HDR_SCTP.checksum);
+ HDR_SCTP.checksum = g_htonl(~crc32c_calculate(&packet_buf[prefix_length], curr_offset, HDR_SCTP.checksum));
+
+ memcpy(&packet_buf[prefix_index], &HDR_SCTP, sizeof(HDR_SCTP));
+ prefix_index += sizeof(HDR_SCTP);
+ }
+
+ /* Write DATA chunk header */
+ if (hdr_data_chunk) {
+ memcpy(&packet_buf[prefix_index], &HDR_DATA_CHUNK, sizeof(HDR_DATA_CHUNK));
+ prefix_index += sizeof(HDR_DATA_CHUNK);
+ }
+
+ /* Write Ethernet trailer */
+ if (hdr_ethernet && eth_trailer_length > 0) {
+ memset(&packet_buf[prefix_length+curr_offset], 0, eth_trailer_length);
+ }
+
+ HDR_TCP.seq_num = g_htonl(g_ntohl(HDR_TCP.seq_num) + curr_offset);
+
+ {
+ /* Write the packet */
+ struct wtap_pkthdr pkthdr;
+ int err;
+
+ pkthdr.ts.secs = (guint32)ts_sec;
+ pkthdr.ts.nsecs = ts_usec * 1000;
+ if (ts_fmt == NULL) { ts_usec++; } /* fake packet counter */
+ pkthdr.caplen = pkthdr.len = prefix_length + curr_offset + eth_trailer_length;;
+ pkthdr.pkt_encap = pcap_link_type;
+
+ wtap_dump(wdh, &pkthdr, NULL, packet_buf, &err);
+ }
+ }
+
+ packet_start += curr_offset;
+ curr_offset = 0;
+}
+
+
+/*----------------------------------------------------------------------
+ * Append a token to the packet preamble.
+ */
+static void
+append_to_preamble(char *str)
+{
+ size_t toklen;
+
+ if (packet_preamble_len != 0) {
+ if (packet_preamble_len == PACKET_PREAMBLE_MAX_LEN)
+ return; /* no room to add more preamble */
+ /* Add a blank separator between the previous token and this token. */
+ packet_preamble[packet_preamble_len++] = ' ';
+ }
+ toklen = strlen(str);
+ if (toklen != 0) {
+ if (packet_preamble_len + toklen > PACKET_PREAMBLE_MAX_LEN)
+ return; /* no room to add the token to the preamble */
+ g_strlcpy(&packet_preamble[packet_preamble_len], str, PACKET_PREAMBLE_MAX_LEN);
+ packet_preamble_len += (int) toklen;
+ if (debug >= 2) {
+ char *c;
+ char xs[PACKET_PREAMBLE_MAX_LEN];
+ g_strlcpy(xs, packet_preamble, PACKET_PREAMBLE_MAX_LEN);
+ while ((c = strchr(xs, '\r')) != NULL) *c=' ';
+ fprintf (stderr, "[[append_to_preamble: \"%s\"]]", xs);
+ }
+ }
+}
+
+/*----------------------------------------------------------------------
+ * Parse the preamble to get the timecode.
+ */
+
+static void
+parse_preamble (void)
+{
+ struct tm timecode;
+ char *subsecs;
+ char *p;
+ int subseclen;
+ int i;
+
+ /*
+ * If no "-t" flag was specified, don't attempt to parse a packet
+ * preamble to extract a time stamp.
+ */
+ if (ts_fmt == NULL)
+ return;
+
+ /*
+ * Initialize to today localtime, just in case not all fields
+ * of the date and time are specified.
+ */
+
+ timecode = timecode_default;
+ ts_usec = 0;
+
+ /*
+ * Null-terminate the preamble.
+ */
+ packet_preamble[packet_preamble_len] = '\0';
+
+ /* Ensure preamble has more than two chars before atempting to parse.
+ * This should cover line breaks etc that get counted.
+ */
+ if ( strlen(packet_preamble) > 2 ) {
+ /* Get Time leaving subseconds */
+ subsecs = strptime( packet_preamble, ts_fmt, &timecode );
+ if (subsecs != NULL) {
+ /* Get the long time from the tm structure */
+ /* (will return -1 if failure) */
+ ts_sec = mktime( &timecode );
+ } else
+ ts_sec = -1; /* we failed to parse it */
+
+ /* This will ensure incorrectly parsed dates get set to zero */
+ if ( -1 == ts_sec )
+ {
+ /* Sanitize - remove all '\r' */
+ char *c;
+ while ((c = strchr(packet_preamble, '\r')) != NULL) *c=' ';
+ fprintf (stderr, "Failure processing time \"%s\" using time format \"%s\"\n (defaulting to Jan 1,1970 00:00:00 GMT)\n",
+ packet_preamble, ts_fmt);
+ if (debug >= 2) {
+ fprintf(stderr, "timecode: %02d/%02d/%d %02d:%02d:%02d %d\n",
+ timecode.tm_mday, timecode.tm_mon, timecode.tm_year,
+ timecode.tm_hour, timecode.tm_min, timecode.tm_sec, timecode.tm_isdst);
+ }
+ ts_sec = 0; /* Jan 1,1970: 00:00 GMT; tshark/wireshark will display date/time as adjusted by timezone */
+ ts_usec = 0;
+ }
+ else
+ {
+ /* Parse subseconds */
+ ts_usec = strtol(subsecs, &p, 10);
+ if (subsecs == p) {
+ /* Error */
+ ts_usec = 0;
+ } else {
+ /*
+ * Convert that number to a number
+ * of microseconds; if it's N digits
+ * long, it's in units of 10^(-N) seconds,
+ * so, to convert it to units of
+ * 10^-6 seconds, we multiply by
+ * 10^(6-N).
+ */
+ subseclen = (int) (p - subsecs);
+ if (subseclen > 6) {
+ /*
+ * *More* than 6 digits; 6-N is
+ * negative, so we divide by
+ * 10^(N-6).
+ */
+ for (i = subseclen - 6; i != 0; i--)
+ ts_usec /= 10;
+ } else if (subseclen < 6) {
+ for (i = 6 - subseclen; i != 0; i--)
+ ts_usec *= 10;
+ }
+ }
+ }
+ }
+ if (debug >= 2) {
+ char *c;
+ while ((c = strchr(packet_preamble, '\r')) != NULL) *c=' ';
+ fprintf(stderr, "[[parse_preamble: \"%s\"]]\n", packet_preamble);
+ fprintf(stderr, "Format(%s), time(%u), subsecs(%u)\n", ts_fmt, (guint32)ts_sec, ts_usec);
+ }
+
+
+ /* Clear Preamble */
+ packet_preamble_len = 0;
+}
+
+/*----------------------------------------------------------------------
+ * Start a new packet
+ */
+static void
+start_new_packet (void)
+{
+ if (debug>=1)
+ fprintf(stderr, "Start new packet\n");
+
+ /* Write out the current packet, if required */
+ write_current_packet();
+
+ /* Ensure we parse the packet preamble as it may contain the time */
+ parse_preamble();
+}
+
+/*----------------------------------------------------------------------
+ * Process a directive
+ */
+static void
+process_directive (char *str)
+{
+ fprintf(stderr, "\n--- Directive [%s] currently unsupported ---\n", str+10);
+
+}
+
+/*----------------------------------------------------------------------
+ * Parse a single token (called from the scanner)
+ */
+void
+parse_token (token_t token, char *str)
+{
+ unsigned long num;
+
+ /*
+ * This is implemented as a simple state machine of five states.
+ * State transitions are caused by tokens being received from the
+ * scanner. The code should be self_documenting.
+ */
+
+ if (debug>=2) {
+ /* Sanitize - remove all '\r' */
+ char *c;
+ if (str!=NULL) { while ((c = strchr(str, '\r')) != NULL) *c=' '; }
+
+ fprintf(stderr, "(%s, %s \"%s\") -> (",
+ state_str[state], token_str[token], str ? str : "");
+ }
+
+ switch(state) {
+
+ /* ----- Waiting for new packet -------------------------------------------*/
+ case INIT:
+ switch(token) {
+ case T_TEXT:
+ append_to_preamble(str);
+ break;
+ case T_DIRECTIVE:
+ process_directive(str);
+ break;
+ case T_OFFSET:
+ num = parse_num(str, TRUE);
+ if (num==0) {
+ /* New packet starts here */
+ start_new_packet();
+ state = READ_OFFSET;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ /* ----- Processing packet, start of new line -----------------------------*/
+ case START_OF_LINE:
+ switch(token) {
+ case T_TEXT:
+ append_to_preamble(str);
+ break;
+ case T_DIRECTIVE:
+ process_directive(str);
+ break;
+ case T_OFFSET:
+ num = parse_num(str, TRUE);
+ if (num==0) {
+ /* New packet starts here */
+ start_new_packet();
+ packet_start = 0;
+ state = READ_OFFSET;
+ } else if ((num - packet_start) != curr_offset) {
+ /*
+ * The offset we read isn't the one we expected.
+ * This may only mean that we mistakenly interpreted
+ * some text as byte values (e.g., if the text dump
+ * of packet data included a number with spaces around
+ * it). If the offset is less than what we expected,
+ * assume that's the problem, and throw away the putative
+ * extra byte values.
+ */
+ if (num < curr_offset) {
+ unwrite_bytes(curr_offset - num);
+ state = READ_OFFSET;
+ } else {
+ /* Bad offset; switch to INIT state */
+ if (debug>=1)
+ fprintf(stderr, "Inconsistent offset. Expecting %0lX, got %0lX. Ignoring rest of packet\n",
+ curr_offset, num);
+ write_current_packet();
+ state = INIT;
+ }
+ } else
+ state = READ_OFFSET;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ /* ----- Processing packet, read offset -----------------------------------*/
+ case READ_OFFSET:
+ switch(token) {
+ case T_BYTE:
+ /* Record the byte */
+ state = READ_BYTE;
+ write_byte(str);
+ break;
+ case T_TEXT:
+ case T_DIRECTIVE:
+ case T_OFFSET:
+ state = READ_TEXT;
+ break;
+ case T_EOL:
+ state = START_OF_LINE;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ /* ----- Processing packet, read byte -------------------------------------*/
+ case READ_BYTE:
+ switch(token) {
+ case T_BYTE:
+ /* Record the byte */
+ write_byte(str);
+ break;
+ case T_TEXT:
+ case T_DIRECTIVE:
+ case T_OFFSET:
+ state = READ_TEXT;
+ break;
+ case T_EOL:
+ state = START_OF_LINE;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ /* ----- Processing packet, read text -------------------------------------*/
+ case READ_TEXT:
+ switch(token) {
+ case T_EOL:
+ state = START_OF_LINE;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ fprintf(stderr, "FATAL ERROR: Bad state (%d)", state);
+ exit(-1);
+ }
+
+ if (debug>=2)
+ fprintf(stderr, ", %s)\n", state_str[state]);
+
+}
+
+/*----------------------------------------------------------------------
+ * take in the import config information
+ */
+void
+text_import_setup(text_import_info_t *info)
+{
+ packet_buf = (unsigned char *)g_malloc(sizeof(HDR_ETHERNET) + sizeof(HDR_IP) +
+ sizeof(HDR_SCTP) + sizeof(HDR_DATA_CHUNK) +
+ IMPORT_MAX_PACKET);
+
+ if (!packet_buf)
+ {
+ fprintf(stderr, "FATAL ERROR: no memory for packet buffer");
+ exit(-1);
+ }
+
+ /* Lets start from the beginning */
+ state = INIT;
+ curr_offset = 0;
+ packet_start = 0;
+ packet_preamble_len = 0;
+ ts_sec = time(0); /* initialize to current time */
+ timecode_default = *localtime(&ts_sec);
+ timecode_default.tm_isdst = -1; /* Unknown for now, depends on time given to the strptime() function */
+ ts_usec = 0;
+
+ /* Dummy headers */
+ hdr_ethernet = FALSE;
+ hdr_ip = FALSE;
+ hdr_udp = FALSE;
+ hdr_tcp = FALSE;
+ hdr_sctp = FALSE;
+ hdr_data_chunk = FALSE;
+
+ offset_base = (info->offset_type == OFFSET_HEX) ? 16 :
+ (info->offset_type == OFFSET_OCT) ? 8 :
+ (info->offset_type == OFFSET_DEC) ? 10 :
+ 16;
+
+ if (info->date_timestamp)
+ {
+ ts_fmt = info->date_timestamp_format;
+ }
+
+ pcap_link_type = info->encapsulation;
+
+ wdh = info->wdh;
+
+ switch (info->dummy_header_type)
+ {
+ case HEADER_ETH:
+ hdr_ethernet = TRUE;
+ hdr_ethernet_proto = info->pid;
+ break;
+
+ case HEADER_IPV4:
+ hdr_ip = TRUE;
+ hdr_ip_proto = info->protocol;
+ hdr_ethernet = TRUE;
+ hdr_ethernet_proto = 0x800;
+ break;
+
+ case HEADER_UDP:
+ hdr_udp = TRUE;
+ hdr_tcp = FALSE;
+ hdr_src_port = info->src_port;
+ hdr_dest_port = info->dst_port;
+ hdr_ip = TRUE;
+ hdr_ip_proto = 17;
+ hdr_ethernet = TRUE;
+ hdr_ethernet_proto = 0x800;
+ break;
+
+ case HEADER_TCP:
+ hdr_tcp = TRUE;
+ hdr_udp = FALSE;
+ hdr_src_port = info->src_port;
+ hdr_dest_port = info->dst_port;
+ hdr_ip = TRUE;
+ hdr_ip_proto = 6;
+ hdr_ethernet = TRUE;
+ hdr_ethernet_proto = 0x800;
+ break;
+
+ case HEADER_SCTP:
+ hdr_sctp = TRUE;
+ hdr_sctp_src = info->src_port;
+ hdr_sctp_dest = info->dst_port;
+ hdr_sctp_tag = info->tag;
+ hdr_ip = TRUE;
+ hdr_ip_proto = 132;
+ hdr_ethernet = TRUE;
+ hdr_ethernet_proto = 0x800;
+ break;
+
+ case HEADER_SCTP_DATA:
+ hdr_sctp = TRUE;
+ hdr_data_chunk = TRUE;
+ hdr_sctp_src = info->src_port;
+ hdr_sctp_dest = info->dst_port;
+ hdr_data_chunk_ppid = info->ppi;
+ hdr_ip = TRUE;
+ hdr_ip_proto = 132;
+ hdr_ethernet = TRUE;
+ hdr_ethernet_proto = 0x800;
+ break;
+
+ default:
+ break;
+ }
+
+ max_offset = info->max_frame_length;
+}
+
+/*----------------------------------------------------------------------
+ * Clean up after text import
+ */
+void
+text_import_cleanup(void)
+{
+ g_free(packet_buf);
+}
diff --git a/ui/gtk/text_import.h b/ui/gtk/text_import.h
new file mode 100644
index 0000000000..c64d462665
--- /dev/null
+++ b/ui/gtk/text_import.h
@@ -0,0 +1,87 @@
+/**-*-C-*-**********************************************************************
+ * text_import.h
+ * State machine for text import
+ * November 2010, Jaap Keuter <jaap.keuter@xs4all.nl>
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * Based on text2pcap.h by Ashok Narayanan <ashokn@cisco.com>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *******************************************************************************/
+
+
+#ifndef TEXT_IMPORT_H
+#define TEXT_IMPORT_H
+
+#include "glib.h"
+#include "wtap.h"
+
+#define IMPORT_MAX_PACKET 64000
+
+/* The parameter interface */
+
+enum offset_type
+{
+ OFFSET_HEX,
+ OFFSET_OCT,
+ OFFSET_DEC
+};
+
+enum dummy_header_type
+{
+ HEADER_NONE,
+ HEADER_ETH,
+ HEADER_IPV4,
+ HEADER_UDP,
+ HEADER_TCP,
+ HEADER_SCTP,
+ HEADER_SCTP_DATA
+};
+
+typedef struct
+{
+ /* Input info */
+ guchar *import_text_filename;
+ FILE *import_text_file;
+ enum offset_type offset_type;
+ gboolean date_timestamp;
+ guchar *date_timestamp_format;
+
+ /* Import info */
+ guint encapsulation;
+ wtap_dumper* wdh;
+
+ /* Dummy header info (if encapsulation == 1) */
+ enum dummy_header_type dummy_header_type;
+ guint pid;
+ guint protocol;
+ guint src_port;
+ guint dst_port;
+ guint tag;
+ guint ppi;
+
+ guint max_frame_length;
+} text_import_info_t;
+
+void text_import_setup(text_import_info_t *info);
+void text_import_cleanup(void);
+
+#endif
diff --git a/ui/gtk/text_import_scanner.h b/ui/gtk/text_import_scanner.h
new file mode 100644
index 0000000000..70eba44626
--- /dev/null
+++ b/ui/gtk/text_import_scanner.h
@@ -0,0 +1,50 @@
+/**-*-C-*-**********************************************************************
+ * text_import_scanner.h
+ * Scanner for text import
+ * November 2010, Jaap Keuter <jaap.keuter@xs4all.nl>
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * Based on text2pcap.h by Ashok Narayanan <ashokn@cisco.com>
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *******************************************************************************/
+
+
+#ifndef TEXT_IMPORT_SCANNER_H
+#define TEXT_IMPORT_SCANNER_H
+
+typedef enum {
+ T_BYTE = 1,
+ T_OFFSET,
+ T_DIRECTIVE,
+ T_TEXT,
+ T_EOL
+} token_t;
+
+
+void parse_token(token_t token, char *str);
+void write_current_packet(void);
+
+extern FILE *text_importin;
+
+int text_importlex(void);
+
+#endif
diff --git a/ui/gtk/text_import_scanner.l b/ui/gtk/text_import_scanner.l
new file mode 100644
index 0000000000..bd59c749d6
--- /dev/null
+++ b/ui/gtk/text_import_scanner.l
@@ -0,0 +1,102 @@
+/* -*-mode: flex-*- */
+
+/*
+ * We don't use unput, so don't generate code for it.
+ */
+%option nounput noinput
+
+/*
+ * We don't read from the terminal.
+ */
+%option never-interactive
+
+/*
+ * Prefix scanner routines with "text_import" rather than "yy", so this scanner
+ * can coexist with other scanners.
+ */
+%option prefix="text_import"
+
+%{
+
+/********************************************************************************
+ *
+ * text_import_scanner.l
+ * Scanner for text import
+ * November 2010, Jaap Keuter <jaap.keuter@xs4all.nl>
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * Based on text2pcap-scanner.l by Ashok Narayanan <ashokn@cisco.com>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+
+#include "text_import_scanner.h"
+#include "text_import_scanner_lex.h"
+
+/*
+ * Flex (v 2.5.35) uses this symbol to "exclude" unistd.h
+ */
+#ifdef _WIN32
+#define YY_NO_UNISTD_H
+#endif
+
+#ifdef _WIN32
+/* disable Windows VC compiler warning "signed/unsigned mismatch" associated */
+/* with YY_INPUT code generated by flex versions such as 2.5.35. */
+#pragma warning (disable:4018)
+#endif
+
+%}
+
+hexdigit [0-9A-Fa-f]
+directive #TEXT2PCAP.*
+comment #[^W].*
+byte [0-9A-Fa-f][0-9A-Fa-f][ \t]
+byte_eol [0-9A-Fa-f][0-9A-Fa-f]\r?\n
+offset [0-9A-Fa-f]+[: \t]
+offset_eol [0-9A-Fa-f]+\r?\n
+text [^ \n\t]+
+mailfwd >
+eol \r?\n\r?
+
+%%
+
+{byte} { parse_token(T_BYTE, yytext); }
+{byte_eol} { parse_token(T_BYTE, yytext); parse_token(T_EOL, NULL); }
+{offset} { parse_token(T_OFFSET, yytext); }
+{offset_eol} { parse_token(T_OFFSET, yytext); parse_token(T_EOL, NULL); }
+{mailfwd}{offset} { parse_token(T_OFFSET, yytext+1); }
+{eol} { parse_token(T_EOL, NULL); }
+[ \t] ; /* ignore whitespace */
+{directive} { parse_token(T_DIRECTIVE, yytext); }
+{comment} ; /* ignore comments */
+{text} { parse_token(T_TEXT, yytext); }
+
+<<EOF>> { write_current_packet(); yyterminate(); }
+
+%%
+int yywrap(void)
+{
+ return 1;
+}
diff --git a/ui/gtk/text_page_utils.c b/ui/gtk/text_page_utils.c
new file mode 100644
index 0000000000..753e7e296d
--- /dev/null
+++ b/ui/gtk/text_page_utils.c
@@ -0,0 +1,155 @@
+/* text_page_utils.c
+ * Construct a simple text page widget from a file.
+ *
+ * $Id$
+ *
+ * Ulf Lamping
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2000 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <gtk/gtk.h>
+
+#include "epan/filesystem.h"
+
+#include "../simple_dialog.h"
+#include <wsutil/file_util.h>
+
+#include "ui/gtk/text_page_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/font_utils.h"
+
+
+#define TEXT_KEY "txt_key"
+
+static void text_page_insert(GtkWidget *page, const char *buffer, int nchars);
+static void text_page_set_text(GtkWidget *page, const char *absolute_path);
+
+
+/*
+ * Construct a simple text page widget from a file.
+ */
+GtkWidget * text_page_new(const char *absolute_path)
+{
+ GtkWidget *page_vb, *txt_scrollw, *txt;
+
+ page_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_set_border_width(GTK_CONTAINER(page_vb), 1);
+ txt_scrollw = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
+ GTK_SHADOW_IN);
+ gtk_box_pack_start(GTK_BOX(page_vb), txt_scrollw, TRUE, TRUE, 0);
+
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(txt_scrollw),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ txt = gtk_text_view_new();
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(txt), FALSE);
+ gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(txt), GTK_WRAP_WORD);
+ gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(txt), FALSE);
+ /* XXX: there seems to be no way to add a small border *around* the whole text,
+ * so the text will be "bump" against the edges.
+ * the following is only working for left and right edges,
+ * there is no such thing for top and bottom :-( */
+ /* gtk_text_view_set_left_margin(GTK_TEXT_VIEW(txt), 3); */
+ /* gtk_text_view_set_right_margin(GTK_TEXT_VIEW(txt), 3); */
+
+ g_object_set_data(G_OBJECT(page_vb), TEXT_KEY, txt);
+
+ text_page_set_text(page_vb, absolute_path);
+ gtk_container_add(GTK_CONTAINER(txt_scrollw), txt);
+ gtk_widget_show(txt_scrollw);
+ gtk_widget_show(txt);
+
+ return page_vb;
+}
+
+
+/*
+ * Insert some text to a text page.
+ */
+static void text_page_insert(GtkWidget *page, const char *buffer, int nchars)
+{
+ GtkWidget *txt = g_object_get_data(G_OBJECT(page), TEXT_KEY);
+
+ GtkTextBuffer *buf= gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt));
+ GtkTextIter iter;
+
+ gtk_text_buffer_get_end_iter(buf, &iter);
+#if GTK_CHECK_VERSION(3,0,0)
+ gtk_widget_override_font(GTK_WIDGET(txt), user_font_get_regular());
+#else
+ gtk_widget_modify_font(GTK_WIDGET(txt), user_font_get_regular());
+#endif
+ if (!g_utf8_validate(buffer, -1, NULL))
+ printf("Invalid utf8 encoding: %s\n", buffer);
+ gtk_text_buffer_insert(buf, &iter, buffer, nchars);
+}
+
+
+/*
+ * Put the complete text file into a text page.
+ */
+static void text_page_set_text(GtkWidget *page, const char *absolute_path)
+{
+ FILE *text_file;
+ char line[4096+1]; /* XXX - size? */
+
+ text_file = ws_fopen(absolute_path, "r");
+ if (text_file != NULL) {
+ while (fgets(line, sizeof line, text_file) != NULL) {
+ text_page_insert(page, line, (int) strlen(line));
+ }
+ if(ferror(text_file)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Error reading file \"%s\": %s",
+ absolute_path, g_strerror(errno));
+ }
+ fclose(text_file);
+ } else {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Could not open file \"%s\": %s",
+ absolute_path, g_strerror(errno));
+ }
+}
+
+
+/**
+ * Clear the text from the text page.
+ */
+static void text_page_clear(GtkWidget *page)
+{
+ GtkTextBuffer *buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(g_object_get_data(G_OBJECT(page), TEXT_KEY)));
+
+ gtk_text_buffer_set_text(buf, "", 0);
+}
+
+
+/**
+ * Redraw a single text page, e.g. to use a new font.
+ */
+void text_page_redraw(GtkWidget *page, const char *absolute_path)
+{
+ text_page_clear(page);
+ text_page_set_text(page, absolute_path);
+}
diff --git a/ui/gtk/text_page_utils.h b/ui/gtk/text_page_utils.h
new file mode 100644
index 0000000000..dc864ab68c
--- /dev/null
+++ b/ui/gtk/text_page_utils.h
@@ -0,0 +1,46 @@
+/* text_page.h
+ * Declarations of routine to construct a simple text page from a file.
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __TEXT_PAGE_H__
+#define __TEXT_PAGE_H__
+
+/** @file
+ * Construct a simple text page widget from a file.
+ */
+
+/** Construct a simple text page widget from a file (UTF8 encoded).
+ *
+ * @param absolute_path the path to the text file
+ * @return the new widget
+ */
+extern GtkWidget * text_page_new(const char *absolute_path);
+
+/** Clear and insert the file content (again).
+ *
+ * @param page the text_page from text_page_new()
+ * @param absolute_path the path to the text file
+ */
+extern void text_page_redraw(GtkWidget *page, const char *absolute_path);
+
+#endif /* __TEXT_PAGE_H__ */
diff --git a/ui/gtk/time_shift_dlg.c b/ui/gtk/time_shift_dlg.c
new file mode 100644
index 0000000000..51d8db68f6
--- /dev/null
+++ b/ui/gtk/time_shift_dlg.c
@@ -0,0 +1,1002 @@
+/* time_shift_dlg.c
+ * Routines for "Time Shift" window
+ * Submitted by Edwin Groothuis <wireshark@mavetju.org>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/proto.h>
+#include <epan/dfilter/dfilter.h>
+#include <epan/nstime.h>
+#include <epan/strutil.h>
+#include <epan/prefs.h>
+
+#include "../globals.h"
+#include "../alert_box.h"
+#include "../simple_dialog.h"
+#include "../main_statusbar.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/time_shift_dlg.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/prefs_dlg.h"
+#include "ui/gtk/keys.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui_util.h"
+
+/* Capture callback data keys */
+#define E_TIMESHIFT_SELECT "timeshift_select"
+#define E_TIMESHIFT_OFFSET_KEY "timeshift_offset_te"
+#define E_SETTIME_SELECT "settime_select"
+#define E_SETTIME_TIME_KEY "settime_time_te"
+#define E_SETTIME_PACKETNUMBER_KEY "settime_packetnumber_te"
+#define E_ADJTIME_SELECT "adjtime_select"
+#define E_ADJTIME_TIME1_KEY "adjtime_time1_te"
+#define E_ADJTIME_PACKETNUMBER1_KEY "adjtime_packetnumber1_te"
+#define E_ADJTIME_TIME2_KEY "adjtime_time2_te"
+#define E_ADJTIME_PACKETNUMBER2_KEY "adjtime_packetnumber2_te"
+#define E_UNDO_SELECT "undo_select"
+#define E_UNDO_SHIFT_KEY "undo_shift_cb"
+
+static void time_shift_apply_cb(GtkWidget *ok_bt, GtkWindow *parent_w);
+static void time_shift_close_cb(GtkWidget *close_bt, gpointer parent_w);
+static void time_shift_frame_destroy_cb(GtkWidget *win, gpointer user_data);
+
+static void error_message(const gchar *msg);
+
+#define SHIFT_POS 0
+#define SHIFT_NEG 1
+#define SHIFT_SETTOZERO 1
+#define SHIFT_KEEPOFFSET 0
+static void modify_time_init(frame_data *fd);
+static void modify_time_perform(frame_data *fd, int neg, nstime_t *offset,
+ int settozero);
+
+/*
+ * Keep a static pointer to the current "Time Shift" window, if any, so
+ * that if somebody tries to do "Time Shift" while there's already a
+ * "Time Shift" window up, we just pop up the existing one, rather than
+ * creating a new one.
+ */
+static GtkWidget *time_shift_frame_w;
+
+void
+time_shift_cb(GtkWidget *w _U_, gpointer d _U_)
+{
+ GtkWidget *main_vb, *main_hb, *label,
+ *types_frame, *types_vb,
+
+ *timeshift_offset_hb,
+ *timeshift_offset_text_box,
+
+ *settime_time_hb,
+ *settime_packetnumber_text_box,
+ *settime_time_text_box,
+
+ *adjtime_offset_hb,
+ *adjtime_packetnumber1_text_box,
+ *adjtime_packetnumber2_text_box,
+ *adjtime_time1_text_box,
+ *adjtime_time2_text_box,
+
+ *undo_offset_hb,
+ *undo_type_hb,
+
+ *timeshift_rb, *settime_rb,
+ *adjtime_rb, *undo_rb,
+
+ *bbox, *apply_bt, *close_bt, *help_bt;
+
+ if (time_shift_frame_w != NULL) {
+ /* There's already a "Time Shift" dialog box; reactivate it. */
+ reactivate_window(time_shift_frame_w);
+ return;
+ }
+
+ time_shift_frame_w = dlg_window_new("Wireshark: Time Shift");
+
+ /* Container for each row of widgets */
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 5);
+ gtk_container_add(GTK_CONTAINER(time_shift_frame_w), main_vb);
+ gtk_widget_show(main_vb);
+
+
+ /*
+ * Shift All Packets frame
+ */
+ main_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(main_vb), main_hb);
+ gtk_widget_show(main_hb);
+
+ types_frame = gtk_frame_new(NULL);
+ gtk_box_pack_start(GTK_BOX(main_hb), types_frame, TRUE, TRUE, 0);
+ gtk_widget_show(types_frame);
+
+ types_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(types_vb), 3);
+ gtk_container_add(GTK_CONTAINER(types_frame), types_vb);
+ gtk_widget_show(types_vb);
+
+ /* Radio button row */
+ timeshift_offset_hb = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(types_vb), timeshift_offset_hb, FALSE, FALSE, 0);
+ gtk_widget_show(timeshift_offset_hb);
+
+ timeshift_rb = gtk_radio_button_new_with_label (NULL, "Shift all packets");
+ gtk_box_pack_start(GTK_BOX(timeshift_offset_hb), timeshift_rb, TRUE, TRUE, 0);
+ gtk_widget_show(timeshift_rb);
+ gtk_widget_set_tooltip_text(timeshift_rb, "Shift the time on the frames.");
+
+ /* Time Shift entry row */
+ timeshift_offset_hb = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(types_vb), timeshift_offset_hb, FALSE, FALSE, 0);
+ gtk_widget_show(timeshift_offset_hb);
+
+ label = gtk_label_new("Time offset in the format [+-][[hh:]mm:]ss[.ddd]");
+ gtk_box_pack_start(GTK_BOX(timeshift_offset_hb), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+
+ timeshift_offset_text_box = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(timeshift_offset_hb), timeshift_offset_text_box,
+ TRUE, TRUE, 0);
+ gtk_widget_show(timeshift_offset_text_box);
+ gtk_widget_set_tooltip_text(timeshift_offset_text_box,
+ "Enter the time to shift here. The format is "
+ "[+-][[hh:]mm:]ss.[.ddddddddd].");
+
+ /*
+ * Set Packet Number to Time frame
+ */
+ main_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(main_vb), main_hb);
+ gtk_widget_show(main_hb);
+
+ types_frame = gtk_frame_new(NULL);
+ gtk_box_pack_start(GTK_BOX(main_hb), types_frame, TRUE, TRUE, 0);
+ gtk_widget_show(types_frame);
+
+ types_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(types_vb), 3);
+ gtk_container_add(GTK_CONTAINER(types_frame), types_vb);
+ gtk_widget_show(types_vb);
+
+ /* time shift type row */
+ settime_time_hb = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(types_vb), settime_time_hb, FALSE,
+ FALSE, 0);
+ gtk_widget_show(settime_time_hb);
+
+ settime_rb = gtk_radio_button_new_with_label(gtk_radio_button_get_group(
+ GTK_RADIO_BUTTON(timeshift_rb)), "Set packet to time");
+ gtk_box_pack_start(GTK_BOX(settime_time_hb), settime_rb, TRUE, TRUE, 0);
+ gtk_widget_show(settime_rb);
+ gtk_widget_set_tooltip_text(settime_rb,
+ "Set the time of a certain frame and adjust the rest of the frames "
+ "automatically.");
+
+ settime_time_hb = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(types_vb), settime_time_hb, FALSE,
+ FALSE, 0);
+ gtk_widget_show(settime_time_hb);
+
+ label = gtk_label_new("Packet number");
+ gtk_box_pack_start(GTK_BOX(settime_time_hb), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+
+ settime_packetnumber_text_box = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(settime_time_hb), settime_packetnumber_text_box,
+ TRUE, TRUE, 0);
+ gtk_entry_set_text(GTK_ENTRY(settime_packetnumber_text_box), "");
+ gtk_widget_show(settime_packetnumber_text_box);
+ gtk_widget_set_tooltip_text(settime_packetnumber_text_box,
+ "The frame which will be set to the time.");
+
+ /* time shift row */
+ settime_time_hb = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(types_vb), settime_time_hb, FALSE, FALSE,
+ 0);
+ gtk_widget_show(settime_time_hb);
+
+ label = gtk_label_new("Set packet to time [YYYY-MM-DD] hh:mm:ss[.ddd]");
+ gtk_box_pack_start(GTK_BOX(settime_time_hb), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+
+ settime_time_text_box = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(settime_time_hb), settime_time_text_box, TRUE,
+ TRUE, 0);
+ gtk_widget_show(settime_time_text_box);
+ gtk_widget_set_tooltip_text(settime_time_text_box,
+ "The time for the frame in the format of [YYYY-MM-DD] "
+ "hh:mm:ss[.ddddddddd]");
+
+ /*
+ * Set two Packet Numbers to Time frame and extrapolate
+ */
+ main_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(main_vb), main_hb);
+ gtk_widget_show(main_hb);
+
+ types_frame = gtk_frame_new(NULL);
+ gtk_box_pack_start(GTK_BOX(main_hb), types_frame, TRUE, TRUE, 0);
+ gtk_widget_show(types_frame);
+
+ types_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(types_vb), 3);
+ gtk_container_add(GTK_CONTAINER(types_frame), types_vb);
+ gtk_widget_show(types_vb);
+
+ /* packet number row 1 */
+ adjtime_offset_hb = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(types_vb), adjtime_offset_hb, FALSE, FALSE, 0);
+ gtk_widget_show(adjtime_offset_hb);
+
+ adjtime_rb = gtk_radio_button_new_with_label(gtk_radio_button_get_group(
+ GTK_RADIO_BUTTON(timeshift_rb)), "Set packets to time and extrapolate");
+ gtk_box_pack_start(GTK_BOX(adjtime_offset_hb), adjtime_rb, TRUE, TRUE, 0);
+ gtk_widget_show(adjtime_rb);
+ gtk_widget_set_tooltip_text(adjtime_rb,
+ "Set the time of two frames and adjust the rest of the frames "
+ "automatically.");
+
+ adjtime_offset_hb = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(types_vb), adjtime_offset_hb, FALSE, FALSE, 0);
+ gtk_widget_show(adjtime_offset_hb);
+
+ label = gtk_label_new("Packet number");
+ gtk_box_pack_start(GTK_BOX(adjtime_offset_hb), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+
+ adjtime_packetnumber1_text_box = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(adjtime_offset_hb), adjtime_packetnumber1_text_box,
+ TRUE, TRUE, 0);
+ gtk_entry_set_text(GTK_ENTRY(adjtime_packetnumber1_text_box), "");
+ gtk_widget_show(adjtime_packetnumber1_text_box);
+ gtk_widget_set_tooltip_text(adjtime_packetnumber1_text_box,
+ "The frame which will be set to the time.");
+
+ /* time shift row */
+ adjtime_offset_hb = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(types_vb), adjtime_offset_hb, FALSE, FALSE,
+ 0);
+ gtk_widget_show(adjtime_offset_hb);
+
+ label = gtk_label_new("Set packet to time [YYYY-MM-DD] hh:mm:ss[.ddd]");
+ gtk_box_pack_start(GTK_BOX(adjtime_offset_hb), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+
+ adjtime_time1_text_box = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(adjtime_offset_hb), adjtime_time1_text_box, TRUE,
+ TRUE, 0);
+ gtk_entry_set_text(GTK_ENTRY(adjtime_time1_text_box), "");
+ gtk_widget_show(adjtime_time1_text_box);
+ gtk_widget_set_tooltip_text(adjtime_time1_text_box,
+ "The time for the frame in the format of [YYYY-MM-DD] "
+ "hh:mm:ss[.ddddddddd]");
+
+ /* packet number row 2 */
+ adjtime_offset_hb = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(types_vb), adjtime_offset_hb, FALSE,
+ FALSE, 0);
+ gtk_widget_show(adjtime_offset_hb);
+
+ label = gtk_label_new("Packet number");
+ gtk_box_pack_start(GTK_BOX(adjtime_offset_hb), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+
+ adjtime_packetnumber2_text_box = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(adjtime_offset_hb), adjtime_packetnumber2_text_box,
+ TRUE, TRUE, 0);
+ gtk_entry_set_text(GTK_ENTRY(adjtime_packetnumber2_text_box), "");
+ gtk_widget_show(adjtime_packetnumber2_text_box);
+ gtk_widget_set_tooltip_text(adjtime_packetnumber2_text_box,
+ "The frame which will be set to the time.");
+
+ /* time shift row */
+ adjtime_offset_hb = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(types_vb), adjtime_offset_hb, FALSE, FALSE,
+ 0);
+ gtk_widget_show(adjtime_offset_hb);
+
+ label = gtk_label_new("Set packet to time [YYYY-MM-DD] hh:mm:ss[.ddd]");
+ gtk_box_pack_start(GTK_BOX(adjtime_offset_hb), label, FALSE, FALSE, 0);
+ gtk_widget_show(label);
+
+ adjtime_time2_text_box = gtk_entry_new();
+ gtk_box_pack_start(GTK_BOX(adjtime_offset_hb), adjtime_time2_text_box, TRUE,
+ TRUE, 0);
+ gtk_entry_set_text(GTK_ENTRY(adjtime_time2_text_box), "");
+ gtk_widget_show(adjtime_time2_text_box);
+ gtk_widget_set_tooltip_text(adjtime_time2_text_box,
+ "The time for the frame in the format of [YYYY-MM-DD] "
+ "hh:mm:ss[.ddddddddd]");
+
+ /*
+ * Undo all shifts
+ */
+ main_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(main_vb), main_hb);
+ gtk_widget_show(main_hb);
+
+ types_frame = gtk_frame_new(NULL);
+ gtk_box_pack_start(GTK_BOX(main_hb), types_frame, TRUE, TRUE, 0);
+ gtk_widget_show(types_frame);
+
+ types_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(types_vb), 3);
+ gtk_container_add(GTK_CONTAINER(types_frame), types_vb);
+ gtk_widget_show(types_vb);
+
+ /* time shift type row */
+ undo_type_hb = gtk_hbox_new(FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(types_vb), undo_type_hb);
+ gtk_widget_show(undo_type_hb);
+
+ /* time shift row */
+ undo_offset_hb = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(types_vb), undo_offset_hb, FALSE,
+ FALSE, 0);
+ gtk_widget_show(undo_offset_hb);
+
+ undo_rb = gtk_radio_button_new_with_label(gtk_radio_button_get_group(
+ GTK_RADIO_BUTTON(timeshift_rb)), "Undo all shifts");
+ gtk_box_pack_start(GTK_BOX(undo_offset_hb), undo_rb, TRUE, TRUE, 0);
+ gtk_widget_show(undo_rb);
+ gtk_widget_set_tooltip_text(undo_rb,
+ "Undo all the Time Shift offsets on the frames.");
+
+ /*
+ * Button row
+ */
+ bbox = dlg_button_row_new(GTK_STOCK_APPLY, GTK_STOCK_CLOSE, GTK_STOCK_HELP,
+ NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+ gtk_widget_show(bbox);
+
+ apply_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_APPLY);
+ g_signal_connect(apply_bt, "clicked", G_CALLBACK(time_shift_apply_cb),
+ time_shift_frame_w);
+ gtk_widget_set_tooltip_text(apply_bt,
+ "Apply the Time Shift options to the frame data.");
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ g_signal_connect(close_bt, "clicked", G_CALLBACK(time_shift_close_cb),
+ time_shift_frame_w);
+ gtk_widget_set_tooltip_text(close_bt, "Close this dialogbox.");
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb),
+ (gpointer)HELP_TIME_SHIFT_DIALOG);
+ gtk_widget_set_tooltip_text(help_bt,
+ "Help on how the Time Shift feature works.");
+
+ /* Link everything together */
+
+ g_object_set_data(G_OBJECT(time_shift_frame_w), E_TIMESHIFT_SELECT,
+ timeshift_rb);
+ g_object_set_data(G_OBJECT(time_shift_frame_w), E_TIMESHIFT_OFFSET_KEY,
+ timeshift_offset_text_box);
+ g_object_set_data(G_OBJECT(time_shift_frame_w), E_SETTIME_SELECT, settime_rb);
+ g_object_set_data(G_OBJECT(time_shift_frame_w), E_SETTIME_TIME_KEY,
+ settime_time_text_box);
+ g_object_set_data(G_OBJECT(time_shift_frame_w), E_SETTIME_PACKETNUMBER_KEY,
+ settime_packetnumber_text_box);
+ g_object_set_data(G_OBJECT(time_shift_frame_w), E_ADJTIME_SELECT, adjtime_rb);
+ g_object_set_data(G_OBJECT(time_shift_frame_w), E_ADJTIME_TIME1_KEY,
+ adjtime_time1_text_box);
+ g_object_set_data(G_OBJECT(time_shift_frame_w), E_ADJTIME_PACKETNUMBER1_KEY,
+ adjtime_packetnumber1_text_box);
+ g_object_set_data(G_OBJECT(time_shift_frame_w), E_ADJTIME_TIME2_KEY,
+ adjtime_time2_text_box);
+ g_object_set_data(G_OBJECT(time_shift_frame_w), E_ADJTIME_PACKETNUMBER2_KEY,
+ adjtime_packetnumber2_text_box);
+ g_object_set_data(G_OBJECT(time_shift_frame_w), E_UNDO_SELECT, undo_rb);
+
+ dlg_set_activate(timeshift_offset_text_box, apply_bt);
+
+ /* Give the initial focus to the "offset" entry box. */
+ gtk_widget_grab_focus(timeshift_offset_text_box);
+
+ g_signal_connect(time_shift_frame_w, "delete_event",
+ G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(time_shift_frame_w, "destroy",
+ G_CALLBACK(time_shift_frame_destroy_cb), NULL);
+
+ gtk_widget_show(time_shift_frame_w);
+ window_present(time_shift_frame_w);
+}
+
+static void
+error_message(const gchar *msg)
+{
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", msg);
+}
+
+static int action_timeshift(GtkWindow *parent_w);
+static void action_settime(GtkWindow *parent_w);
+static void action_adjtime(GtkWindow *parent_w);
+static void action_undo(GtkWindow *parent_w);
+
+static void
+time_shift_apply_cb(GtkWidget *ok_bt _U_, GtkWindow *parent_w)
+{
+ GtkWidget *flag_rb;
+
+ if (cfile.state == FILE_CLOSED) {
+ /* Nothing to do here */
+ return;
+ }
+ if (cfile.state == FILE_READ_IN_PROGRESS) {
+ error_message("The Time Shift functions are not available on live captures.");
+ return;
+ }
+
+
+ flag_rb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w),
+ E_TIMESHIFT_SELECT);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(flag_rb)) == TRUE) {
+ action_timeshift(parent_w);
+ return;
+ }
+
+ flag_rb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w),
+ E_SETTIME_SELECT);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(flag_rb)) == TRUE) {
+ action_settime(parent_w);
+ return;
+ }
+
+ flag_rb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w),
+ E_ADJTIME_SELECT);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(flag_rb)) == TRUE) {
+ action_adjtime(parent_w);
+ return;
+ }
+
+ flag_rb = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w), E_UNDO_SELECT);
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(flag_rb)) == TRUE) {
+ action_undo(parent_w);
+ return;
+ }
+}
+
+#define CHECK_YEARS(Y) \
+ if (Y < 1970) { \
+ error_message("years must be larger than 1970"); \
+ return(1); \
+ }
+#define CHECK_MONTHS(M) \
+ if (M < 1 || M > 12) { \
+ error_message("months must be between [1..12]"); \
+ return(1); \
+ }
+#define CHECK_DAYS(D) \
+ if (D < 1 || D > 31) { \
+ error_message("days must be between [1..31]"); \
+ return(1); \
+ }
+#define CHECK_HOURS(h) \
+ if (h < 0 || h > 23) { \
+ error_message("hours must be between [0..23]"); \
+ return(1); \
+ }
+#define CHECK_HOUR(h) \
+ if (h < 0) { \
+ error_message("negative hours, you have have specified more than " \
+ "one minus character?"); \
+ return(1); \
+ } \
+ offset_float += h * 3600
+#define CHECK_MINUTE(m) \
+ if (m < 0 || m > 59) { \
+ error_message("minutes must be between [0..59]"); \
+ return(1); \
+ } \
+ offset_float += m * 60
+#define CHECK_SECOND(s) \
+ if (s < 0 || s > 59) { \
+ error_message("seconds must be between [0..59]"); \
+ return(1); \
+ } \
+ offset_float += s
+#define CHECK_SEC_DEC(f) \
+ if (f < 0) { \
+ error_message("fractional seconds must be > 0"); \
+ return(1); \
+ } \
+ offset_float += f
+
+static int
+action_timeshift(GtkWindow *parent_w)
+{
+ GtkWidget *offset_te;
+ const gchar *offset_text;
+ gchar *poffset_text;
+ nstime_t offset;
+ long double offset_float = 0;
+ guint32 i;
+ frame_data *fd;
+ int neg;
+ int h, m;
+ long double f;
+
+ /*
+ * The following offset types are allowed:
+ * -?((hh:)mm:)ss(.decimals)?
+ *
+ * Since Wireshark doesn't support regular expressions (please prove me
+ * wrong :-) we will have to figure it out ourselves in the
+ * following order:
+ *
+ * 1. hh:mm:ss.decimals
+ * 2. mm:ss.decimals
+ * 3. ss.decimals
+ *
+ */
+
+ offset_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w),
+ E_TIMESHIFT_OFFSET_KEY);
+ offset_text = gtk_entry_get_text(GTK_ENTRY(offset_te));
+ poffset_text = (gchar *)offset_text;
+
+ /* strip whitespace */
+ while (isspace(poffset_text[0]))
+ ++poffset_text;
+
+ /* check for minus sign */
+ neg = FALSE;
+ if (poffset_text[0] == '-') {
+ neg = TRUE;
+ poffset_text++;
+ }
+
+ /* check for empty string */
+ if (poffset_text[0] == '\0')
+ return(1);
+
+ h = m = 0;
+ f = 0.0;
+ if (sscanf(poffset_text, "%d:%d:%Lf", &h, &m, &f) == 3) {
+ /* printf("%%d:%%d:%%d.%%d\n"); */
+ CHECK_HOUR(h);
+ CHECK_MINUTE(m);
+ CHECK_SEC_DEC(f);
+ } else if (sscanf(poffset_text, "%d:%Lf", &m, &f) == 2) {
+ /* printf("%%d:%%d.%%d\n"); */
+ CHECK_MINUTE(m);
+ CHECK_SEC_DEC(f);
+ } else if (sscanf(poffset_text, "%Lf", &f) == 1) {
+ /* printf("%%d.%%d\n"); */
+ CHECK_SEC_DEC(f);
+ } else {
+ error_message("Could not parse the time: Expected ((hh:)mm:)ss.(dec).");
+ return(1);
+ }
+
+ if (offset_float == 0)
+ return(1);
+
+ nstime_set_zero(&offset);
+ offset.secs = (time_t)floorl(offset_float);
+ offset_float -= offset.secs;
+ offset.nsecs = (int)(offset_float * 1000000000);
+
+ if ((fd = frame_data_sequence_find(cfile.frames, 1)) == NULL)
+ return(1); /* Shouldn't happen */
+ modify_time_init(fd);
+
+ for (i = 1; i <= cfile.count; i++) {
+ if ((fd = frame_data_sequence_find(cfile.frames, i)) == NULL)
+ continue; /* Shouldn't happen */
+ modify_time_perform(fd, neg, &offset, SHIFT_KEEPOFFSET);
+ }
+ new_packet_list_queue_draw();
+
+ return(0);
+}
+
+static int
+timestring2nstime(const gchar *ts, nstime_t *packettime, nstime_t *nstime)
+{
+ gchar *pts;
+ int h, m, Y, M, D;
+ long double f;
+ struct tm tm, *tmptm;
+ time_t tt;
+ long double offset_float = 0;
+
+ /*
+ * The following time format is allowed:
+ * [YYYY-MM-DD] hh:mm:ss(.decimals)?
+ *
+ * Since Wireshark doesn't support regular expressions (please prove me
+ * wrong :-) we will have to figure it out ourselves in the
+ * following order:
+ *
+ * 1. YYYY-MM-DD hh:mm:ss.decimals
+ * 2. hh:mm:ss.decimals
+ *
+ */
+
+ pts = (gchar *)ts;
+
+ /* strip whitespace */
+ while (isspace(pts[0]))
+ ++pts;
+
+ /* check for empty string */
+ if (pts[0] == '\0')
+ return(1);
+
+ if (sscanf(pts, "%d-%d-%d %d:%d:%Lf", &Y, &M, &D, &h, &m, &f) == 6) {
+ /* printf("%%d-%%d-%%d %%d:%%d:%%f\n"); */
+ CHECK_YEARS(Y);
+ CHECK_MONTHS(M);
+ CHECK_DAYS(D);
+ CHECK_HOURS(h);
+ CHECK_MINUTE(m);
+ CHECK_SEC_DEC(f);
+ } else if (sscanf(pts, "%d:%d:%Lf", &h, &m, &f) == 3) {
+ /* printf("%%d:%%d:%%f\n"); */
+ Y = M = D = 0;
+ CHECK_HOUR(h);
+ CHECK_MINUTE(m);
+ CHECK_SEC_DEC(f);
+ } else {
+ error_message("Could not parse the time: Expected (YYYY-MM-DD) "
+ "hh:mm:ss(.dec)");
+ return(1);
+ }
+
+ /* Convert the time entered in an epoch offset */
+ tmptm = localtime(&(packettime->secs));
+ if (tmptm) {
+ tm = *tmptm;
+ } else {
+ memset (&tm, 0, sizeof (tm));
+ }
+ if (Y != 0) {
+ tm.tm_year = Y - 1900;
+ tm.tm_mon = M - 1;
+ tm.tm_mday = D;
+ }
+ tm.tm_hour = h;
+ tm.tm_min = m;
+ tm.tm_sec = (int)floorl(f);
+ tt = mktime(&tm);
+ if (tt == -1) {
+ error_message("mktime went wrong. Was the time invalid?");
+ return(1);
+ }
+
+ nstime->secs = tt;
+ f -= tm.tm_sec;
+ nstime->nsecs = (int)(f * 1000000000);
+
+ return(0);
+}
+
+static void
+action_settime(GtkWindow *parent_w)
+{
+ GtkWidget *packetnumber_te;
+ const gchar *packetnumber_text;
+ long packetnumber;
+ GtkWidget *time_te;
+ const gchar *time_text;
+ nstime_t settime, difftime, packettime;
+ frame_data *fd, *packetfd;
+ guint32 i;
+
+ packetnumber_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w),
+ E_SETTIME_PACKETNUMBER_KEY);
+ packetnumber_text = gtk_entry_get_text(GTK_ENTRY(packetnumber_te));
+ packetnumber = strtol((char *)packetnumber_text, NULL, 10);
+
+ time_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w),
+ E_SETTIME_TIME_KEY);
+ time_text = gtk_entry_get_text(GTK_ENTRY(time_te));
+
+ /*
+ * Get a copy of the real time (abs_ts - shift_offset) do we can find out the
+ * difference between the specified time and the original packet
+ */
+ if ((packetfd = frame_data_sequence_find(cfile.frames, packetnumber)) == NULL)
+ return;
+ nstime_delta(&packettime, &(packetfd->abs_ts), &(packetfd->shift_offset));
+
+ if (timestring2nstime(time_text, &packettime, &settime) != 0)
+ return;
+
+ /* Calculate difference between packet time and requested time */
+ nstime_delta(&difftime, &settime, &packettime);
+
+ /* Up to here nothing is changed */
+
+ if ((fd = frame_data_sequence_find(cfile.frames, 1)) == NULL)
+ return; /* Shouldn't happen */
+ modify_time_init(fd);
+
+ /* Set everything back to the original time */
+ for (i = 1; i <= cfile.count; i++) {
+ if ((fd = frame_data_sequence_find(cfile.frames, i)) == NULL)
+ continue; /* Shouldn't happen */
+ modify_time_perform(fd, SHIFT_POS, &difftime, SHIFT_SETTOZERO);
+ }
+
+ new_packet_list_queue_draw();
+}
+
+/*
+ * If the line between (OT1, NT1) and (OT2, NT2) is a straight line
+ * and (OT3, NT3) is on that line,
+ * then (NT2 - NT1) / (OT2 - OT2) = (NT3 - NT1) / (OT3 - OT1) and
+ * then (OT3 - OT1) * (NT2 - NT1) / (OT2 - OT2) = (NT3 - NT1) and
+ * then NT1 + (OT3 - OT1) * (NT2 - NT1) / (OT2 - OT2) = NT3 and
+ * then NT3 = NT1 + (OT3 - OT1) * (NT2 - NT1) / (OT2 - OT2) and
+ * thus NT3 = NT1 + (OT3 - OT1) * (NT2 - NT1) / (OT2 - OT1)
+ * or NT3 = NT1 + (OT3 - OT1) * ( deltaNT12 / deltaOT12)
+ *
+ * All the things you come up when waiting for the train to come...
+ */
+static void
+calcNT3(nstime_t *OT1, nstime_t *OT3, nstime_t *NT1, nstime_t *NT3,
+ nstime_t *deltaOT, nstime_t *deltaNT)
+{
+ long double fnt, fot, f, secs, nsecs;
+
+ fnt = (long double)deltaNT->secs + (deltaNT->nsecs / 1000000000.0L);
+ fot = (long double)deltaOT->secs + (deltaOT->nsecs / 1000000000.0L);
+ f = fnt / fot;
+
+ nstime_copy(NT3, OT3);
+ nstime_subtract(NT3, OT1);
+
+ secs = f * (long double)NT3->secs;
+ nsecs = f * (long double)NT3->nsecs;
+ nsecs += (secs - floorl(secs)) * 1000000000.0L;
+ while (nsecs > 1000000000L) {
+ secs += 1;
+ nsecs -= 1000000000L;
+ }
+ while (nsecs < 0) {
+ secs -= 1;
+ nsecs += 1000000000L;
+ }
+ NT3->secs = (time_t)secs;
+ NT3->nsecs = (int)nsecs;
+ nstime_add(NT3, NT1);
+}
+
+static void
+action_adjtime(GtkWindow *parent_w _U_)
+{
+ GtkWidget *packetnumber_te;
+ const gchar *packetnumber_text;
+ long packetnumber1, packetnumber2;
+ GtkWidget *time_te;
+ const gchar *time1_text, *time2_text;
+ nstime_t nt1, nt2, ot1, ot2, nt3;
+ nstime_t dnt, dot, d3t;
+ frame_data *fd, *packet1fd, *packet2fd;
+ guint32 i;
+
+ packetnumber_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w),
+ E_ADJTIME_PACKETNUMBER1_KEY);
+ packetnumber_text = gtk_entry_get_text(GTK_ENTRY(packetnumber_te));
+ packetnumber1 = strtol((char *)packetnumber_text, NULL, 10);
+ packetnumber_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w),
+ E_ADJTIME_PACKETNUMBER2_KEY);
+ packetnumber_text = gtk_entry_get_text(GTK_ENTRY(packetnumber_te));
+ packetnumber2 = strtol((char *)packetnumber_text, NULL, 10);
+
+ /*
+ * The following time format is allowed:
+ * [YYYY-MM-DD] hh:mm:ss(.decimals)?
+ *
+ * Since Wireshark doesn't support regular expressions (please prove me
+ * wrong :-) we will have to figure it out ourselves in the
+ * following order:
+ *
+ * 1. YYYY-MM-DD hh:mm:ss.decimals
+ * 2. hh:mm:ss.decimals
+ *
+ */
+
+ time_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w),
+ E_ADJTIME_TIME1_KEY);
+ time1_text = gtk_entry_get_text(GTK_ENTRY(time_te));
+ time_te = (GtkWidget *)g_object_get_data(G_OBJECT(parent_w),
+ E_ADJTIME_TIME2_KEY);
+ time2_text = gtk_entry_get_text(GTK_ENTRY(time_te));
+
+ /*
+ * Get a copy of the real time (abs_ts - shift_offset) do we can find out the
+ * difference between the specified time and the original packet
+ */
+ if ((packet1fd = frame_data_sequence_find(cfile.frames, packetnumber1)) == NULL)
+ return;
+ nstime_copy(&ot1, &(packet1fd->abs_ts));
+ nstime_subtract(&ot1, &(packet1fd->shift_offset));
+
+ if (timestring2nstime(time1_text, &ot1, &nt1) != 0)
+ return;
+
+ /*
+ * Get a copy of the real time (abs_ts - shift_offset) do we can find out the
+ * difference between the specified time and the original packet
+ */
+ if ((packet2fd = frame_data_sequence_find(cfile.frames, packetnumber2)) == NULL)
+ return;
+ nstime_copy(&ot2, &(packet2fd->abs_ts));
+ nstime_subtract(&ot2, &(packet2fd->shift_offset));
+
+ if (timestring2nstime(time2_text, &ot2, &nt2) != 0)
+ return;
+
+ nstime_copy(&dot, &ot2);
+ nstime_subtract(&dot, &ot1);
+
+ nstime_copy(&dnt, &nt2);
+ nstime_subtract(&dnt, &nt1);
+
+ /* Up to here nothing is changed */
+ if ((fd = frame_data_sequence_find(cfile.frames, 1)) == NULL)
+ return; /* Shouldn't happen */
+ modify_time_init(fd);
+
+ for (i = 1; i <= cfile.count; i++) {
+ if ((fd = frame_data_sequence_find(cfile.frames, i)) == NULL)
+ continue; /* Shouldn't happen */
+
+ /* Set everything back to the original time */
+ nstime_subtract(&(fd->abs_ts), &(fd->shift_offset));
+ nstime_set_zero(&(fd->shift_offset));
+
+ /* Add the difference to each packet */
+ calcNT3(&ot1, &(fd->abs_ts), &nt1, &nt3, &dot, &dnt);
+
+ nstime_copy(&d3t, &nt3);
+ nstime_subtract(&d3t, &(fd->abs_ts));
+
+ modify_time_perform(fd, SHIFT_POS, &d3t, SHIFT_SETTOZERO);
+ }
+
+ new_packet_list_queue_draw();
+}
+
+static void
+action_undo(GtkWindow *parent_w _U_)
+{
+ guint32 i;
+ frame_data *fd;
+ nstime_t nulltime;
+
+ nulltime.secs = nulltime.nsecs = 0;
+
+ if ((fd = frame_data_sequence_find(cfile.frames, 1)) == NULL)
+ return; /* Shouldn't happen */
+ modify_time_init(fd);
+
+ for (i = 1; i <= cfile.count; i++) {
+ if ((fd = frame_data_sequence_find(cfile.frames, i)) == NULL)
+ continue; /* Shouldn't happen */
+ modify_time_perform(fd, SHIFT_NEG, &nulltime, SHIFT_SETTOZERO);
+ }
+ new_packet_list_queue_draw();
+}
+
+static void
+time_shift_close_cb(GtkWidget *close_bt _U_, gpointer parent_w _U_)
+{
+ gtk_grab_remove(GTK_WIDGET(parent_w));
+ window_destroy(GTK_WIDGET(parent_w));
+}
+
+static void
+time_shift_frame_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
+{
+ /* Note that we no longer have a "Time Shift" dialog box. */
+ time_shift_frame_w = NULL;
+}
+
+static void
+modify_time_init(frame_data *fd)
+{
+ modify_time_perform(fd, SHIFT_NEG, NULL, SHIFT_KEEPOFFSET);
+}
+
+static void
+modify_time_perform(frame_data *fd, int neg, nstime_t *offset, int settozero)
+{
+ static frame_data *first_packet = NULL;
+ static frame_data *lastdisplayed_packet = NULL;
+ static frame_data *prevcaptured_packet = NULL;
+ static nstime_t nulltime;
+
+ /* Only for initializing */
+ if (offset == NULL) {
+ first_packet = fd;
+ lastdisplayed_packet = NULL;
+ prevcaptured_packet = NULL;
+ nulltime.secs = nulltime.nsecs = 0;
+ return;
+ }
+ if (first_packet == NULL) {
+ fprintf(stderr, "modify_time_perform: not initialized?\n");
+ return;
+ }
+
+ /* The actual shift */
+
+ if (settozero == SHIFT_SETTOZERO) {
+ nstime_subtract(&(fd->abs_ts), &(fd->shift_offset));
+ nstime_copy(&(fd->shift_offset), &nulltime);
+ }
+
+ if (neg == SHIFT_POS) {
+ nstime_add(&(fd->abs_ts), offset);
+ nstime_add(&(fd->shift_offset), offset);
+ } else if (neg == SHIFT_NEG) {
+ nstime_subtract(&(fd->abs_ts), offset);
+ nstime_subtract(&(fd->shift_offset), offset);
+ } else {
+ fprintf(stderr, "modify_time_perform: neg = %d?\n", neg);
+ }
+
+ /*
+ * rel_ts - Relative timestamp to first packet
+ * del_dis_ts - Delta timestamp to previous displayed frame
+ * del_cap_ts - Delta timestamp to previous captured frame
+ */
+ if (first_packet != NULL) {
+ nstime_copy(&(fd->rel_ts), &(fd->abs_ts));
+ nstime_subtract(&(fd->rel_ts), &(first_packet->abs_ts));
+ } else
+ nstime_copy(&(fd->rel_ts), &nulltime);
+
+ if (prevcaptured_packet != NULL) {
+ nstime_copy(&(fd->del_cap_ts), &(fd->abs_ts));
+ nstime_subtract(&(fd->del_cap_ts), &(prevcaptured_packet->abs_ts));
+ } else
+ nstime_copy(&(fd->del_cap_ts), &nulltime);
+
+ if (lastdisplayed_packet != NULL) {
+ nstime_copy(&(fd->del_dis_ts), &(fd->abs_ts));
+ nstime_subtract(&(fd->del_dis_ts), &(lastdisplayed_packet->abs_ts));
+ } else
+ nstime_copy(&(fd->del_dis_ts), &nulltime);
+
+ prevcaptured_packet = fd;
+ if (fd->flags.passed_dfilter)
+ lastdisplayed_packet = fd;
+}
diff --git a/ui/gtk/time_shift_dlg.h b/ui/gtk/time_shift_dlg.h
new file mode 100644
index 0000000000..3ac4442507
--- /dev/null
+++ b/ui/gtk/time_shift_dlg.h
@@ -0,0 +1,38 @@
+/* time_shift_dlg.h
+ * Submitted by Edwin Groothuis <wireshark@mavetju.org>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __TIME_SHIFT_DLG_H__
+#define __TIME_SHIFT_DLG_H__
+
+#include "globals.h"
+
+/** User requested to shift the time of the trace
+ *
+ * @param widget parent widget (unused)
+ * @param data unused
+ * @param action the function to use
+ */
+extern void time_shift_cb(GtkWidget *widget, gpointer data);
+
+#endif /* __TIME_SHIFT_DLG_H__ */
diff --git a/ui/gtk/uat_gui.c b/ui/gtk/uat_gui.c
new file mode 100644
index 0000000000..bea21d33d4
--- /dev/null
+++ b/ui/gtk/uat_gui.c
@@ -0,0 +1,1078 @@
+/*
+ * uat_gui.c
+ *
+ * $Id$
+ *
+ * User Accessible Tables GUI
+ * Mantain an array of user accessible data strucures
+ *
+ * (c) 2007, Luis E. Garcia Ontanon <luis@ontanon.org>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2001 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * TO DO:
+ * + improvements
+ * - field value check (red/green editbox)
+ * - tooltips (add field descriptions)
+ * - Make cells editable
+ * - Allow reordering via drag and drop
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#if GTK_CHECK_VERSION(3,0,0)
+# include <gdk/gdkkeysyms-compat.h>
+#endif
+
+#include <epan/dfilter/dfilter-macro.h>
+#include <epan/emem.h>
+#include <epan/report_err.h>
+#include <epan/proto.h>
+#include <epan/packet.h>
+#include <epan/uat-int.h>
+#include <epan/value_string.h>
+#include <epan/filesystem.h>
+
+#include "../stat_menu.h"
+
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/stock_icons.h"
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/uat_gui.h"
+#include "ui/gtk/old-gtk-compat.h"
+
+# define BUTTON_SIZE_X -1
+# define BUTTON_SIZE_Y -1
+
+struct _uat_rep_t {
+ GtkWidget* window;
+ GtkWidget* vbox;
+ GtkWidget* scrolledwindow;
+ GtkTreeView* list;
+ GtkListStore *list_store;
+ GtkWidget* bbox;
+ GtkWidget* bt_new;
+ GtkWidget* bt_edit;
+ GtkWidget* bt_copy;
+ GtkWidget* bt_delete;
+ GtkWidget* bt_refresh;
+ GtkWidget* bt_clear;
+ GtkWidget* bt_up;
+ GtkWidget* bt_down;
+ GtkWidget* bt_apply;
+ GtkWidget* bt_cancel;
+ GtkWidget* bt_ok;
+ GtkWidget* unsaved_window;
+
+ gint selected;
+};
+
+struct _str_pair {
+ const char* ptr;
+ unsigned len;
+};
+
+struct _uat_dlg_data {
+ GtkWidget* win;
+ GPtrArray* entries;
+ uat_t* uat;
+ void* rec;
+ gboolean is_new;
+ gint row;
+ GPtrArray* tobe_freed;
+};
+
+
+static gboolean unsaved_dialog(GtkWindow *w, GdkEvent* e, gpointer u);
+static gboolean uat_window_delete_event_cb(GtkWindow *w, GdkEvent* e, gpointer u);
+
+static void set_buttons(uat_t* uat, gint row) {
+
+ if (!uat->rep) return;
+
+ if (row > 0) {
+ gtk_widget_set_sensitive (uat->rep->bt_up, TRUE);
+ } else {
+ gtk_widget_set_sensitive (uat->rep->bt_up, FALSE);
+ }
+
+ if (row < (gint)(*uat->nrows_p - 1) && row >= 0) {
+ gtk_widget_set_sensitive (uat->rep->bt_down, TRUE);
+ } else {
+ gtk_widget_set_sensitive (uat->rep->bt_down, FALSE);
+ }
+
+ if (row < 0) {
+ gtk_widget_set_sensitive (uat->rep->bt_edit, FALSE);
+ gtk_widget_set_sensitive (uat->rep->bt_copy, FALSE);
+ gtk_widget_set_sensitive (uat->rep->bt_delete, FALSE);
+ }
+
+ if (uat->changed) {
+ g_signal_handlers_disconnect_by_func(uat->rep->window, uat_window_delete_event_cb, uat);
+ g_signal_connect(uat->rep->window, "delete_event", G_CALLBACK(unsaved_dialog), uat);
+ g_signal_connect(uat->rep->window, "destroy", G_CALLBACK(unsaved_dialog), uat);
+ } else {
+ g_signal_handlers_disconnect_by_func(uat->rep->window, unsaved_dialog, uat);
+ g_signal_connect(GTK_WINDOW(uat->rep->window), "delete_event", G_CALLBACK(uat_window_delete_event_cb), uat);
+ g_signal_connect(GTK_WINDOW(uat->rep->window), "destroy", G_CALLBACK(uat_window_delete_event_cb), uat);
+ }
+}
+
+static char* fld_tostr(void* rec, uat_field_t* f) {
+ guint len;
+ const char* ptr;
+ char* out;
+
+ f->cb.tostr(rec,&ptr,&len,f->cbdata.tostr,f->fld_data);
+
+ switch(f->mode) {
+ case PT_TXTMOD_STRING:
+ case PT_TXTMOD_ENUM:
+ case PT_TXTMOD_FILENAME:
+ case PT_TXTMOD_DIRECTORYNAME:
+ out = ep_strndup(ptr,len);
+ break;
+ case PT_TXTMOD_HEXBYTES: {
+ GString* s = g_string_sized_new( len*2 + 1 );
+ guint i;
+
+ for (i=0; i<len;i++) g_string_append_printf(s,"%.2X",((guint8*)ptr)[i]);
+
+ out = ep_strdup(s->str);
+
+ g_string_free(s,TRUE);
+ break;
+ }
+ default:
+ g_assert_not_reached();
+ out = NULL;
+ break;
+ }
+
+ return out;
+}
+
+
+
+static void append_row(uat_t* uat, guint idx) {
+ GPtrArray* a = g_ptr_array_new();
+ void* rec = UAT_INDEX_PTR(uat,idx);
+ uat_field_t* f = uat->fields;
+ guint colnum;
+ GtkTreeIter iter;
+
+ if (! uat->rep) return;
+
+ gtk_list_store_insert_before(uat->rep->list_store, &iter, NULL);
+ for ( colnum = 0; colnum < uat->ncols; colnum++ ) {
+ g_ptr_array_add(a,fld_tostr(rec,&(f[colnum])));
+ gtk_list_store_set(uat->rep->list_store, &iter, colnum, fld_tostr(rec,&(f[colnum])), -1);
+ }
+
+ g_ptr_array_free(a,TRUE);
+}
+
+static void reset_row(uat_t* uat, guint idx) {
+ void* rec = UAT_INDEX_PTR(uat,idx);
+ uat_field_t* f = uat->fields;
+ guint colnum;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ if (! uat->rep) return;
+
+ path = gtk_tree_path_new_from_indices(idx, -1);
+ if (!path || !gtk_tree_model_get_iter(GTK_TREE_MODEL(uat->rep->list_store), &iter, path)) {
+ return;
+ }
+
+ for ( colnum = 0; colnum < uat->ncols; colnum++ ) {
+ gtk_list_store_set(uat->rep->list_store, &iter, colnum, fld_tostr(rec,&(f[colnum])), -1);
+ }
+}
+
+static guint8* unhexbytes(const char* si, guint len, guint* len_p, const char** err) {
+ guint8* buf;
+ guint8* p;
+ const guint8* s = (void*)si;
+ unsigned i;
+
+ if (len % 2) {
+ *err = "Uneven number of chars hex string";
+ return NULL;
+ }
+
+ buf = ep_alloc(len/2+1);
+ p = buf;
+
+ for (i = 0; i<len ; i += 2) {
+ guint8 lo = s[i+1];
+ guint8 hi = s[i];
+
+ if (hi >= '0' && hi <= '9') {
+ hi -= '0';
+ } else if (hi >= 'a' && hi <= 'f') {
+ hi -= 'a';
+ hi += 0xa;
+ } else if (hi >= 'A' && hi <= 'F') {
+ hi -= 'A';
+ hi += 0xa;
+ } else {
+ goto on_error;
+ }
+
+ if (lo >= '0' && lo <= '9') {
+ lo -= '0';
+ } else if (lo >= 'a' && lo <= 'f') {
+ lo -= 'a';
+ lo += 0xa;
+ } else if (lo >= 'A' && lo <= 'F') {
+ lo -= 'A';
+ lo += 0xa;
+ } else {
+ goto on_error;
+ }
+
+ *(p++) = (hi*0x10) + lo;
+ }
+
+ len /= 2;
+
+ if (len_p) *len_p = len;
+
+ buf[len] = '\0';
+
+ *err = NULL;
+ return buf;
+
+on_error:
+ *err = "Error parsing hex string";
+ return NULL;
+}
+
+
+static gboolean uat_dlg_cb(GtkWidget *win _U_, gpointer user_data) {
+ struct _uat_dlg_data* dd = user_data;
+ guint ncols = dd->uat->ncols;
+ uat_field_t* f = dd->uat->fields;
+ const char* err = NULL;
+ guint colnum;
+
+ for ( colnum = 0; colnum < ncols; colnum++ ) {
+ void* e = g_ptr_array_index(dd->entries,colnum);
+ const char *text = NULL;
+ char *text_free = NULL;
+ unsigned len = 0;
+
+ switch(f[colnum].mode) {
+ case PT_TXTMOD_FILENAME:
+ case PT_TXTMOD_DIRECTORYNAME:
+ text = text_free = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(e));
+ if (text) {
+ len = (unsigned) strlen(text);
+ } else {
+ text = "";
+ len = 0;
+ }
+ break;
+
+ case PT_TXTMOD_STRING:
+ text = gtk_entry_get_text(GTK_ENTRY(e));
+ len = (unsigned) strlen(text);
+ break;
+ case PT_TXTMOD_HEXBYTES: {
+ text = gtk_entry_get_text(GTK_ENTRY(e));
+
+ text = (void*) unhexbytes(text, (guint) strlen(text), &len, &err);
+
+ if (err) {
+ err = ep_strdup_printf("error in field '%s': %s",f[colnum].title,err);
+ goto on_failure;
+ }
+
+ break;
+ }
+ case PT_TXTMOD_ENUM: {
+ gint idx = *(int*)e;
+ text = (idx >= 0) ? ((value_string *)(f[colnum].fld_data))[idx].strptr : "";
+ len = (unsigned) strlen(text);
+ break;
+ }
+ default:
+ g_assert_not_reached();
+ return FALSE;
+ }
+
+ if (f[colnum].cb.chk) {
+ if (! f[colnum].cb.chk(dd->rec, text, len, f[colnum].cbdata.chk, f[colnum].fld_data, &err)) {
+ err = ep_strdup_printf("error in column '%s': %s",f[colnum].title,err);
+ goto on_failure;
+ }
+ }
+
+ f[colnum].cb.set(dd->rec,text,len, f[colnum].cbdata.set, f[colnum].fld_data);
+
+ g_free(text_free);
+ }
+
+ if (dd->uat->update_cb) {
+ dd->uat->update_cb(dd->rec,&err);
+
+ if (err) {
+ err = ep_strdup_printf("error updating record: %s",err);
+ goto on_failure;
+ }
+ }
+
+ if (dd->is_new) {
+ void* rec_tmp = dd->rec;
+ dd->rec = uat_add_record(dd->uat, dd->rec);
+
+ if (dd->uat->free_cb) {
+ dd->uat->free_cb(rec_tmp);
+ }
+
+ g_free(rec_tmp);
+ }
+
+ dd->uat->changed = TRUE;
+
+ set_buttons(dd->uat, dd->uat->rep ? dd->uat->rep->selected : -1);
+
+ if (dd->is_new) {
+ append_row(dd->uat, (*dd->uat->nrows_p) - 1 );
+ } else {
+ reset_row(dd->uat,dd->row);
+ }
+
+ g_ptr_array_free(dd->entries,TRUE);
+ window_destroy(GTK_WIDGET(dd->win));
+
+ if (dd->uat->rep)
+ window_present(GTK_WIDGET(dd->uat->rep->window));
+
+ while (dd->tobe_freed->len) g_free( g_ptr_array_remove_index_fast(dd->tobe_freed, dd->tobe_freed->len - 1 ) );
+
+ g_free(dd);
+
+ return TRUE;
+on_failure:
+
+ report_failure("%s",err);
+ return FALSE;
+}
+
+static gboolean uat_cancel_dlg_cb(GtkWidget *win _U_, gpointer user_data) {
+ struct _uat_dlg_data* dd = user_data;
+
+ if (dd->uat->rep)
+ window_present(GTK_WIDGET(dd->uat->rep->window));
+
+ if (dd->is_new) g_free(dd->rec);
+ g_ptr_array_free(dd->entries,TRUE);
+ window_destroy(GTK_WIDGET(dd->win));
+
+ while (dd->tobe_freed->len) g_free( g_ptr_array_remove_index_fast(dd->tobe_freed, dd->tobe_freed->len - 1 ) );
+
+ g_free(dd);
+
+ return TRUE;
+}
+
+static void fld_combo_box_changed_cb(GtkComboBox *combo_box, gpointer user_data) {
+ int* valptr = user_data;
+ *valptr = gtk_combo_box_get_active(combo_box);
+}
+
+static void uat_edit_dialog(uat_t* uat, gint row, gboolean copy) {
+ GtkWidget *win, *main_tb, *main_vb, *bbox, *bt_cancel, *bt_ok;
+ struct _uat_dlg_data* dd = g_malloc(sizeof(struct _uat_dlg_data));
+ uat_field_t* f = uat->fields;
+ guint colnum;
+
+ dd->entries = g_ptr_array_new();
+ dd->win = dlg_conf_window_new(ep_strdup_printf("%s: %s", uat->name, (row == -1 ? "New" : "Edit")));
+ dd->uat = uat;
+ if (copy && row >= 0) {
+ dd->rec = g_malloc0(uat->record_size);
+ if (uat->copy_cb) {
+ uat->copy_cb (dd->rec, UAT_INDEX_PTR(uat,row), uat->record_size);
+ }
+ dd->is_new = TRUE;
+ } else if (row >= 0) {
+ dd->rec = UAT_INDEX_PTR(uat,row);
+ dd->is_new = FALSE;
+ } else {
+ dd->rec = g_malloc0(uat->record_size);
+ dd->is_new = TRUE;
+ }
+ dd->row = row;
+ dd->tobe_freed = g_ptr_array_new();
+
+ win = dd->win;
+
+ gtk_window_set_resizable(GTK_WINDOW(win),FALSE);
+ gtk_window_resize(GTK_WINDOW(win),400, 30*(uat->ncols+2));
+
+ main_vb = gtk_vbox_new(FALSE,5);
+ gtk_container_add(GTK_CONTAINER(win), main_vb);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 6);
+
+ main_tb = gtk_table_new(uat->ncols+1, 2, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 5);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 10);
+
+ bbox = dlg_button_row_new(GTK_STOCK_CANCEL,GTK_STOCK_OK, NULL);
+ gtk_box_pack_end(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+
+ bt_ok = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
+ g_signal_connect(bt_ok, "clicked", G_CALLBACK(uat_dlg_cb), dd);
+
+ bt_cancel = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ g_signal_connect(bt_cancel, "clicked", G_CALLBACK(uat_cancel_dlg_cb), dd);
+ window_set_cancel_button(win, bt_cancel, NULL);
+
+ for ( colnum = 0; colnum < uat->ncols; colnum++ ) {
+ GtkWidget *entry, *label, *event_box;
+ char* text = fld_tostr(dd->rec,&(f[colnum]));
+
+ event_box = gtk_event_box_new();
+
+ label = gtk_label_new(ep_strdup_printf("%s:", f[colnum].title));
+ if (f[colnum].desc != NULL)
+ gtk_widget_set_tooltip_text(event_box, f[colnum].desc);
+
+ gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), event_box, 0, 1, colnum+1, colnum + 2);
+ gtk_container_add(GTK_CONTAINER(event_box), label);
+
+ switch(f[colnum].mode) {
+ case PT_TXTMOD_FILENAME:
+ case PT_TXTMOD_DIRECTORYNAME:
+ entry = gtk_file_chooser_button_new(f[colnum].desc,
+ (f[colnum].mode == PT_TXTMOD_FILENAME) ? GTK_FILE_CHOOSER_ACTION_OPEN : GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
+ if (! dd->is_new || copy) {
+ gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(entry), text);
+ }
+ g_ptr_array_add(dd->entries,entry);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), entry, 1, 2, colnum+1, colnum + 2);
+ break;
+
+ case PT_TXTMOD_STRING:
+ case PT_TXTMOD_HEXBYTES:
+ entry = gtk_entry_new();
+ if (! dd->is_new || copy) {
+ gtk_entry_set_text(GTK_ENTRY(entry),text);
+ }
+ g_ptr_array_add(dd->entries,entry);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), entry, 1, 2, colnum+1, colnum + 2);
+ dlg_set_activate(entry, bt_ok);
+ break;
+
+ case PT_TXTMOD_ENUM: {
+ GtkWidget *combo_box;
+ int idx;
+ const value_string* enum_vals = f[colnum].fld_data;
+ int* valptr = g_malloc(sizeof(int)); /* A place to store the index of the */
+ /* "active" fld_data array entry */
+ /* -1 means "nothing selected (active)" */
+ combo_box = gtk_combo_box_text_new();
+ *valptr = -1;
+ for (idx = 0; enum_vals[idx].strptr != NULL; idx++) {
+ const char* str = enum_vals[idx].strptr;
+ gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT(combo_box), str);
+
+ if ( g_str_equal(str, text) ) {
+ *valptr = idx;
+ }
+ }
+
+ g_ptr_array_add(dd->entries,valptr);
+ g_ptr_array_add(dd->tobe_freed,valptr);
+
+ if (*valptr != -1)
+ gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), *valptr);
+
+ g_signal_connect(combo_box, "changed", G_CALLBACK(fld_combo_box_changed_cb), valptr);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), combo_box, 1, 2, colnum+1, colnum + 2);
+
+ break;
+ }
+ default:
+ g_assert_not_reached();
+ return;
+ }
+ }
+
+ gtk_widget_grab_default(bt_ok);
+ gtk_widget_show_all(win);
+}
+
+struct _uat_del {
+ GtkWidget *win;
+ uat_t* uat;
+ gint idx;
+};
+
+static void uat_del_cb(GtkButton *button _U_, gpointer u) {
+ struct _uat_del* ud = u;
+ GtkTreeIter iter;
+ GtkTreePath *path;
+
+ uat_remove_record_idx(ud->uat, ud->idx);
+
+ if (ud->uat->rep) {
+ path = gtk_tree_path_new_from_indices(ud->idx, -1);
+ if (path && gtk_tree_model_get_iter(GTK_TREE_MODEL(ud->uat->rep->list_store), &iter, path)) {
+ gtk_list_store_remove(ud->uat->rep->list_store, &iter);
+ }
+ }
+
+ ud->uat->changed = TRUE;
+ set_buttons(ud->uat,-1);
+
+ window_destroy(GTK_WIDGET(ud->win));
+
+ if (ud->uat->rep)
+ window_present(GTK_WIDGET(ud->uat->rep->window));
+
+ g_free(ud);
+}
+
+static void uat_cancel_del_cb(GtkButton *button _U_, gpointer u) {
+ struct _uat_del* ud = u;
+ window_destroy(GTK_WIDGET(ud->win));
+
+ if (ud->uat->rep)
+ window_present(GTK_WIDGET(ud->uat->rep->window));
+ g_free(ud);
+}
+
+static void uat_del_dlg(uat_t* uat, int idx) {
+ GtkWidget *win, *main_tb, *main_vb, *bbox, *bt_cancel, *bt_ok;
+ uat_field_t* f = uat->fields;
+ guint colnum;
+ void* rec = UAT_INDEX_PTR(uat,idx);
+ struct _uat_del* ud = g_malloc(sizeof(struct _uat_del));
+
+ ud->uat = uat;
+ ud->idx = idx;
+ ud->win = win = dlg_conf_window_new(ep_strdup_printf("%s: Confirm Delete", uat->name));
+
+ gtk_window_set_resizable(GTK_WINDOW(win),FALSE);
+ gtk_window_resize(GTK_WINDOW(win),400,25*(uat->ncols+2));
+
+ main_vb = gtk_vbox_new(FALSE,5);
+ gtk_container_add(GTK_CONTAINER(win), main_vb);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 6);
+
+ main_tb = gtk_table_new(uat->ncols+1, 2, FALSE);
+ gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
+ gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
+ gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
+
+ for ( colnum = 0; colnum < uat->ncols; colnum++ ) {
+ GtkWidget *label;
+ char* text = fld_tostr(rec,&(f[colnum]));
+
+ label = gtk_label_new(ep_strdup_printf("%s:", f[colnum].title));
+ gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, colnum+1, colnum + 2);
+
+ label = gtk_label_new(text);
+ gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
+ gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 1, 2, colnum+1, colnum + 2);
+ }
+
+ bbox = dlg_button_row_new(GTK_STOCK_CANCEL,GTK_STOCK_DELETE, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+
+ bt_ok = g_object_get_data(G_OBJECT(bbox),GTK_STOCK_DELETE);
+ g_signal_connect(bt_ok, "clicked", G_CALLBACK(uat_del_cb), ud);
+
+ bt_cancel = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
+ g_signal_connect(bt_cancel, "clicked", G_CALLBACK(uat_cancel_del_cb), ud);
+ window_set_cancel_button( win, bt_cancel, NULL);
+
+ gtk_widget_show_all(win);
+}
+
+static void uat_new_cb(GtkButton *button _U_, gpointer u) {
+ uat_t* uat = u;
+
+ if (! uat->rep) return;
+
+ uat_edit_dialog(uat, -1, FALSE);
+}
+
+static void uat_edit_cb(GtkWidget *button _U_, gpointer u) {
+ uat_t* uat = u;
+
+ if (! uat->rep) return;
+
+ uat_edit_dialog(uat, uat->rep->selected, FALSE);
+}
+
+static void uat_copy_cb(GtkWidget *button _U_, gpointer u) {
+ uat_t* uat = u;
+
+ if (! uat->rep) return;
+
+ uat_edit_dialog(uat, uat->rep->selected, TRUE);
+}
+
+static void uat_double_click_cb(GtkWidget *tv, GtkTreePath *path _U_, GtkTreeViewColumn *column _U_, gpointer u) {
+ uat_edit_cb(tv, u);
+}
+
+static void uat_delete_cb(GtkButton *button _U_, gpointer u) {
+ uat_t* uat = u;
+
+ if (! uat->rep) return;
+
+ uat_del_dlg(uat,uat->rep->selected);
+}
+
+static gboolean uat_window_delete_event_cb(GtkWindow *w _U_, GdkEvent* e _U_, gpointer u) {
+ uat_t* uat = u;
+
+ if (uat->rep) {
+ void* rep = uat->rep;
+
+ g_signal_handlers_disconnect_by_func(uat->rep->window, uat_window_delete_event_cb, uat);
+ g_signal_handlers_disconnect_by_func(uat->rep->window, unsaved_dialog, uat);
+
+ gtk_widget_destroy(uat->rep->window);
+
+ uat->rep = NULL;
+ g_free(rep);
+ }
+ return TRUE;
+}
+
+static void uat_up_cb(GtkButton *button _U_, gpointer u) {
+ uat_t* uat = u;
+ gint row = uat->rep->selected;
+
+ g_assert(row > 0);
+
+ uat_swap(uat,row,row-1);
+ tree_view_list_store_move_selection(uat->rep->list, TRUE);
+
+ uat->changed = TRUE;
+
+ row -= 1;
+ uat->rep->selected = row;
+ set_buttons(uat,row);
+}
+
+static void uat_down_cb(GtkButton *button _U_, gpointer u) {
+ uat_t* uat = u;
+ gint row = uat->rep->selected;
+
+ g_assert(row >= 0 && (guint) row < *uat->nrows_p - 1);
+
+ uat_swap(uat,row,row+1);
+ tree_view_list_store_move_selection(uat->rep->list, FALSE);
+
+ uat->changed = TRUE;
+
+ row += 1;
+ uat->rep->selected = row;
+ set_buttons(uat,row);
+}
+
+static void uat_cancel_cb(GtkWidget *button _U_, gpointer u) {
+ uat_t* uat = u;
+ gchar* err = NULL;
+
+ if (uat->changed) {
+ uat_clear(uat);
+ uat_load(uat,&err);
+
+ if (err) {
+ report_failure("Error while loading %s: %s",uat->name,err);
+ }
+
+ redissect_packets ();
+ }
+
+ g_signal_handlers_disconnect_by_func(uat->rep->window, uat_window_delete_event_cb, uat);
+ g_signal_handlers_disconnect_by_func(uat->rep->window, unsaved_dialog, uat);
+ gtk_widget_destroy(uat->rep->window);
+ g_free(uat->rep);
+ uat->rep = NULL;
+}
+
+static void uat_apply_cb(GtkButton *button _U_, gpointer u) {
+ uat_t* uat = u;
+
+ if (uat->changed) {
+ if (uat->post_update_cb) uat->post_update_cb();
+ redissect_packets ();
+ }
+}
+
+static void uat_ok_cb(GtkButton *button _U_, gpointer u) {
+ uat_t* uat = u;
+ gchar* err = NULL;
+
+ if (uat->changed) {
+ uat_save(uat,&err);
+
+ if (err) {
+ report_failure("Error while saving %s: %s",uat->name,err);
+ }
+
+ if (uat->post_update_cb) uat->post_update_cb();
+ redissect_packets ();
+ }
+
+ g_signal_handlers_disconnect_by_func(uat->rep->window, uat_window_delete_event_cb, uat);
+ g_signal_handlers_disconnect_by_func(uat->rep->window, unsaved_dialog, uat);
+ gtk_widget_destroy(uat->rep->window);
+ g_free(uat->rep);
+ uat->rep = NULL;
+}
+
+static void uat_clear_cb(GtkButton *button _U_, gpointer u) {
+ uat_t *uat = u;
+
+ gtk_list_store_clear(uat->rep->list_store);
+ uat_clear(uat);
+ uat->changed = TRUE;
+}
+
+static void uat_refresh_cb(GtkButton *button _U_, gpointer u) {
+ uat_t *uat = u;
+ gchar *err = NULL;
+ guint i;
+
+ uat_clear_cb(button, u);
+
+ uat->from_global = TRUE;
+ uat_load(uat,&err);
+ uat->from_global = FALSE;
+ uat->changed = TRUE;
+
+ if (err) {
+ report_failure("Error while loading %s: %s",uat->name,err);
+ }
+
+ for (i = 0 ; i < *(uat->nrows_p); i++) {
+ append_row(uat, i);
+ }
+}
+
+
+static void remember_selected_row(GtkWidget *w _U_, gpointer u) {
+ uat_t* uat = u;
+ gint row;
+
+ row = tree_view_list_store_get_selected_row(uat->rep->list);
+ uat->rep->selected = row;
+
+ gtk_widget_set_sensitive (uat->rep->bt_edit, TRUE);
+ gtk_widget_set_sensitive (uat->rep->bt_copy, uat->copy_cb ? TRUE : FALSE);
+ gtk_widget_set_sensitive(uat->rep->bt_delete, TRUE);
+
+ set_buttons(uat,row);
+}
+
+static void uat_yessave_cb(GtkWindow *w _U_, void* u) {
+ uat_t* uat = u;
+ gchar* err = NULL;
+
+ window_delete_event_cb(uat->rep->unsaved_window,NULL,NULL);
+
+ uat_save(uat,&err);
+
+ if (err) {
+ report_failure("Error while saving %s: %s",uat->name,err);
+ }
+
+ g_signal_handlers_disconnect_by_func(uat->rep->window, uat_window_delete_event_cb, uat);
+ g_signal_handlers_disconnect_by_func(uat->rep->window, unsaved_dialog, uat);
+ window_destroy(uat->rep->window);
+
+ g_free(uat->rep);
+ uat->rep = NULL;
+}
+
+
+static void uat_nosave_cb(GtkWindow *w _U_, void* u) {
+ uat_t* uat = u;
+ window_delete_event_cb(uat->rep->unsaved_window,NULL,NULL);
+ g_signal_handlers_disconnect_by_func(uat->rep->window, uat_window_delete_event_cb, uat);
+ g_signal_handlers_disconnect_by_func(uat->rep->window, unsaved_dialog, uat);
+ window_destroy(uat->rep->window);
+
+ g_free(uat->rep);
+ uat->rep = NULL;
+}
+
+static gboolean unsaved_dialog(GtkWindow *w _U_, GdkEvent* e _U_, gpointer u) {
+ GtkWidget *win, *vbox, *label, *bbox;
+ GtkWidget *yes_bt, *no_bt;
+ gchar* message;
+ uat_t* uat = u;
+
+ if (uat->rep->unsaved_window) {
+ window_present(uat->rep->unsaved_window);
+ return TRUE;
+ }
+
+ uat->rep->unsaved_window = win = dlg_conf_window_new("Discard Changes?");
+ gtk_window_set_default_size(GTK_WINDOW(win), 360, 140);
+
+ gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER_ON_PARENT);
+ vbox = gtk_vbox_new(FALSE, 12);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
+ gtk_container_add(GTK_CONTAINER(win), vbox);
+
+ message = ep_strdup_printf("Changes to '%s' are not being saved!\n"
+ "Do you want to save '%s'?", uat->name, uat->name);
+
+ label = gtk_label_new(message);
+
+ bbox = dlg_button_row_new(GTK_STOCK_YES,GTK_STOCK_NO, NULL);
+
+ yes_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_YES);
+ no_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_NO);
+
+ g_signal_connect(no_bt, "clicked", G_CALLBACK(uat_nosave_cb), uat);
+ g_signal_connect(yes_bt, "clicked", G_CALLBACK(uat_yessave_cb), uat);
+
+ gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
+ gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ gtk_widget_show_all(win);
+ window_present(win);
+
+ return TRUE;
+}
+
+static void uat_help_cb(GtkWidget* w _U_, gpointer u) {
+ help_topic_html(ep_strdup_printf("%s.html",((uat_t*)u)->help));
+}
+
+static GtkWidget* uat_window(void* u) {
+ uat_t* uat = u;
+ uat_field_t* f = uat->fields;
+ uat_rep_t* rep;
+ guint i;
+ guint colnum;
+ GType *col_types;
+ GtkWidget *hbox, *vbox, *move_hbox, *edit_hbox, *refresh_hbox;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSelection *selection;
+ gchar *global_fname;
+ gboolean global_file_exists;
+
+ if (uat->rep) {
+ window_present(uat->rep->window);
+ return uat->rep->window;
+ } else {
+ uat->rep = rep = g_malloc0(sizeof(uat_rep_t));
+ }
+
+ global_fname = get_datafile_path(uat->filename);
+ global_file_exists = file_exists(global_fname);
+ g_free (global_fname);
+
+ rep->window = dlg_conf_window_new(uat->name);
+
+ gtk_window_set_resizable(GTK_WINDOW(rep->window),TRUE);
+ gtk_window_resize(GTK_WINDOW(rep->window), 720, 512);
+ gtk_window_set_position(GTK_WINDOW(rep->window), GTK_WIN_POS_CENTER_ON_PARENT);
+
+ gtk_container_set_border_width(GTK_CONTAINER(rep->window), 6);
+
+ rep->vbox = gtk_vbox_new(FALSE, 12);
+ gtk_container_set_border_width(GTK_CONTAINER(rep->vbox), 6);
+ gtk_container_add(GTK_CONTAINER(rep->window), rep->vbox);
+
+ hbox = gtk_hbox_new(FALSE,12);
+ gtk_container_set_border_width(GTK_CONTAINER(hbox), 6);
+ gtk_container_add(GTK_CONTAINER(rep->vbox), hbox);
+
+ vbox = gtk_vbox_new(FALSE, 12);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 6);
+ gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
+
+ rep->scrolledwindow = scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(rep->scrolledwindow), GTK_SHADOW_IN);
+
+ col_types = g_malloc(sizeof(GType) * uat->ncols);
+ for ( colnum = 0; colnum < uat->ncols; colnum++ ) {
+ col_types[colnum] = G_TYPE_STRING;
+ }
+ rep->list_store = gtk_list_store_newv(uat->ncols, col_types);
+ g_free(col_types);
+
+ rep->list = GTK_TREE_VIEW(tree_view_new(GTK_TREE_MODEL(rep->list_store))); /* uat->ncols */
+ gtk_container_add(GTK_CONTAINER(rep->scrolledwindow), GTK_WIDGET(rep->list));
+ gtk_box_pack_start(GTK_BOX(hbox), rep->scrolledwindow, TRUE, TRUE, 0);
+
+ selection = gtk_tree_view_get_selection(rep->list);
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+
+ for ( colnum = 0; colnum < uat->ncols; colnum++ ) {
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(f[colnum].title,
+ renderer, "text", colnum, NULL);
+ gtk_tree_view_column_set_resizable (column,TRUE);
+ gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_append_column (rep->list, column);
+ if (f[colnum].desc != NULL)
+ gtk_widget_set_tooltip_text(gtk_tree_view_column_get_button(column), f[colnum].desc);
+ }
+
+ for ( i = 0 ; i < *(uat->nrows_p); i++ ) {
+ append_row(uat, i);
+ }
+
+ if(uat->help) {
+ GtkWidget* help_btn;
+ rep->bbox = dlg_button_row_new(GTK_STOCK_HELP, GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_CANCEL, NULL);
+ help_btn = g_object_get_data(G_OBJECT(rep->bbox),GTK_STOCK_HELP);
+ g_signal_connect(help_btn, "clicked", G_CALLBACK(uat_help_cb), uat);
+ } else {
+
+ rep->bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_CANCEL, NULL);
+ }
+
+ move_hbox = gtk_vbutton_box_new();
+ gtk_box_pack_start(GTK_BOX(vbox), move_hbox, TRUE, FALSE, 0);
+
+ edit_hbox = gtk_vbutton_box_new();
+ gtk_box_pack_start(GTK_BOX(vbox), edit_hbox, TRUE, FALSE, 0);
+
+ refresh_hbox = gtk_vbutton_box_new();
+ gtk_box_pack_end(GTK_BOX(vbox), refresh_hbox, TRUE, FALSE, 0);
+
+
+ rep->bt_up = gtk_button_new_from_stock(GTK_STOCK_GO_UP);
+ gtk_widget_set_tooltip_text(rep->bt_up, "Move selected entry up");
+
+ rep->bt_down = gtk_button_new_from_stock(GTK_STOCK_GO_DOWN);
+ gtk_widget_set_tooltip_text(rep->bt_down, "Move selected entry down");
+
+ gtk_box_pack_start(GTK_BOX(move_hbox), rep->bt_up, TRUE, FALSE, 5);
+ gtk_box_pack_start(GTK_BOX(move_hbox), rep->bt_down, TRUE, FALSE, 5);
+
+
+ rep->bt_new = gtk_button_new_from_stock(GTK_STOCK_NEW);
+ gtk_widget_set_tooltip_text(rep->bt_new, "Create a new entry");
+
+ rep->bt_edit = gtk_button_new_from_stock(WIRESHARK_STOCK_EDIT);
+ gtk_widget_set_tooltip_text(rep->bt_edit, "Edit selected entry");
+
+ rep->bt_copy = gtk_button_new_from_stock(GTK_STOCK_COPY);
+ gtk_widget_set_tooltip_text(rep->bt_copy, "Copy selected entry");
+
+ rep->bt_delete = gtk_button_new_from_stock(GTK_STOCK_DELETE);
+ gtk_widget_set_tooltip_text(rep->bt_delete, "Delete selected entry");
+
+ gtk_box_pack_end(GTK_BOX(edit_hbox), rep->bt_new, TRUE, FALSE, 5);
+ gtk_box_pack_end(GTK_BOX(edit_hbox), rep->bt_edit, TRUE, FALSE, 5);
+ gtk_box_pack_end(GTK_BOX(edit_hbox), rep->bt_copy, TRUE, FALSE, 5);
+ gtk_box_pack_end(GTK_BOX(edit_hbox), rep->bt_delete, TRUE, FALSE, 5);
+
+ rep->bt_refresh = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
+ gtk_widget_set_tooltip_text(rep->bt_refresh, "Refresh from system defaults");
+
+ rep->bt_clear = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
+ gtk_widget_set_tooltip_text(rep->bt_clear, "Delete all entries");
+
+ gtk_box_pack_end(GTK_BOX(refresh_hbox), rep->bt_refresh, TRUE, FALSE, 5);
+ gtk_box_pack_end(GTK_BOX(refresh_hbox), rep->bt_clear, TRUE, FALSE, 5);
+
+
+ rep->bt_apply = g_object_get_data(G_OBJECT(rep->bbox),GTK_STOCK_APPLY);
+ rep->bt_cancel = g_object_get_data(G_OBJECT(rep->bbox),GTK_STOCK_CANCEL);
+ rep->bt_ok = g_object_get_data(G_OBJECT(rep->bbox),GTK_STOCK_OK);
+
+ gtk_box_pack_end(GTK_BOX(rep->vbox), rep->bbox, FALSE, FALSE, 0);
+
+ gtk_widget_set_sensitive (rep->bt_up, FALSE);
+ gtk_widget_set_sensitive (rep->bt_down, FALSE);
+ gtk_widget_set_sensitive (rep->bt_edit, FALSE);
+ gtk_widget_set_sensitive (rep->bt_copy, FALSE);
+ gtk_widget_set_sensitive (rep->bt_delete, FALSE);
+ gtk_widget_set_sensitive (rep->bt_refresh, global_file_exists);
+
+ g_signal_connect(rep->list, "row-activated", G_CALLBACK(uat_double_click_cb), uat);
+ g_signal_connect(selection, "changed", G_CALLBACK(remember_selected_row), uat);
+
+
+ g_signal_connect(rep->bt_new, "clicked", G_CALLBACK(uat_new_cb), uat);
+ g_signal_connect(rep->bt_edit, "clicked", G_CALLBACK(uat_edit_cb), uat);
+ g_signal_connect(rep->bt_copy, "clicked", G_CALLBACK(uat_copy_cb), uat);
+ g_signal_connect(rep->bt_delete, "clicked", G_CALLBACK(uat_delete_cb), uat);
+
+ g_signal_connect(rep->bt_refresh, "clicked", G_CALLBACK(uat_refresh_cb), uat);
+ g_signal_connect(rep->bt_clear, "clicked", G_CALLBACK(uat_clear_cb), uat);
+
+ g_signal_connect(rep->bt_up, "clicked", G_CALLBACK(uat_up_cb), uat);
+ g_signal_connect(rep->bt_down, "clicked", G_CALLBACK(uat_down_cb), uat);
+
+ g_signal_connect(rep->bt_apply, "clicked", G_CALLBACK(uat_apply_cb), uat);
+ g_signal_connect(rep->bt_cancel, "clicked", G_CALLBACK(uat_cancel_cb), uat);
+ g_signal_connect(rep->bt_ok, "clicked", G_CALLBACK(uat_ok_cb), uat);
+
+ window_set_cancel_button(rep->window, rep->bt_cancel, NULL); /* set esc to activate cancel button */
+
+ if (uat->changed) {
+ g_signal_connect(GTK_WINDOW(rep->window), "delete_event", G_CALLBACK(unsaved_dialog), uat);
+ g_signal_connect(GTK_WINDOW(rep->window), "destroy", G_CALLBACK(unsaved_dialog), uat);
+ } else {
+ g_signal_connect(GTK_WINDOW(rep->window), "delete_event", G_CALLBACK(uat_window_delete_event_cb), uat);
+ g_signal_connect(GTK_WINDOW(rep->window), "destroy", G_CALLBACK(uat_window_delete_event_cb), uat);
+ }
+
+ gtk_widget_grab_focus(GTK_WIDGET(rep->list));
+
+ gtk_widget_show_all(rep->window);
+ window_present(rep->window);
+
+ return rep->window;
+}
+
+void uat_window_cb(GtkWidget* u _U_, void* uat) {
+ uat_window(uat);
+}
+
diff --git a/ui/gtk/uat_gui.h b/ui/gtk/uat_gui.h
new file mode 100644
index 0000000000..d9324462fe
--- /dev/null
+++ b/ui/gtk/uat_gui.h
@@ -0,0 +1,35 @@
+/*
+ * uat_gui.h
+ *
+ * $Id$
+ *
+ * User Accessible Tables GUI
+ * Mantain an array of user accessible data strucures
+ *
+ * (c) 2007, Luis E. Garcia Ontanon <luis@ontanon.org>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 2001 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __UAT_GUI_H__
+#define __UAT_GUI_H__
+
+void uat_window_cb(GtkWidget* unused, void* uat);
+
+#endif /* __UAT_GUI_H__ */
diff --git a/ui/gtk/ui/main-menubar-ui.xml b/ui/gtk/ui/main-menubar-ui.xml
new file mode 100644
index 0000000000..beb8c8305d
--- /dev/null
+++ b/ui/gtk/ui/main-menubar-ui.xml
@@ -0,0 +1,386 @@
+<!--
+$Id$
+xml description of Wireshark's main menubar, loaded from main_menubar.c in the gtk directory.
+-->
+<ui>
+ <menubar name ='Menubar'>
+ <menu name= 'FileMenu' action='/File'>
+ <menuitem name='Open' action='/File/Open'/>
+ <menu name='OpenRecent' action='/File/OpenRecent'>
+ <placeholder name='RecentFiles'/>
+ </menu>
+ <menuitem name='Merge' action='/File/Merge'/>
+ <menuitem name='Import' action='/File/Import'/>
+ <menuitem name='Close' action='/File/Close'/>
+ <separator/>
+ <menuitem name='Save' action='/File/Save'/>
+ <menuitem name='SaveAs' action='/File/SaveAs'/>
+ <separator/>
+ <menu name= 'Set' action='/File/Set'>
+ <menuitem name='ListFiles' action='/File/Set/ListFiles'/>
+ <menuitem name='NextFile' action='/File/Set/NextFile'/>
+ <menuitem name='PreviousFile' action='/File/Set/PreviousFile'/>
+ </menu>
+ <separator/>
+ <menu name= 'Export' action='/File/Export'>
+ <menu name= 'File' action='/File/Export/File'>
+ <menuitem name='AsTxt' action='/File/Export/File/Text'/>
+ <menuitem name='AsPostScript' action='/File/Export/File/PostScript'/>
+ <menuitem name='AsCSV' action='/File/Export/File/CSV'/>
+ <menuitem name='AsCArrays' action='/File/Export/File/CArrays'/>
+ <separator/>
+ <menuitem name='AsPSML' action='/File/Export/File/PSML'/>
+ <menuitem name='AsPDML' action='/File/Export/File/PDML'/>
+ <separator/>
+ </menu>
+ <menuitem name='SelectedPacketBytes' action='/File/Export/SelectedPacketBytes'/>
+ <menu name= 'Objects' action='/File/Export/Objects'>
+ <menuitem name='HTTP' action='/File/Export/Objects/HTTP'/>
+ <menuitem name='DICOM' action='/File/Export/Objects/DICOM'/>
+ <menuitem name='SMB' action='/File/Export/Objects/SMB'/>
+ </menu>
+ </menu>
+ <separator/>
+ <menuitem name='Print' action='/File/Print'/>
+ <separator/>
+ <menuitem name='Quit' action='/File/Quit'/>
+ </menu>
+ <menu name= 'EditMenu' action='/Edit'>
+ <menu name= 'Copy' action='/Edit/Copy'>
+ <menuitem name='Description' action='/Edit/Copy/Description'/>
+ <menuitem name='Fieldname' action='/Edit/Copy/Fieldname'/>
+ <menuitem name='Value' action='/Edit/Copy/Value'/>
+ <separator/>
+ <menuitem name='AsFilter' action='/Edit/Copy/AsFilter'/>
+ </menu>
+ <menuitem name='FindPacket' action='/Edit/FindPacket'/>
+ <menuitem name='FindNext' action='/Edit/FindNext'/>
+ <menuitem name='FindPrevious' action='/Edit/FindPrevious'/>
+ <separator/>
+ <menuitem name='MarkPacket' action='/Edit/MarkPacket'/>
+ <menuitem name='MarkAllDisplayedPackets' action='/Edit/MarkAllDisplayedPackets'/>
+ <menuitem name='UnmarkAllDisplayedPackets' action='/Edit/UnmarkAllDisplayedPackets'/>
+ <menuitem name='FindNextMark' action='/Edit/FindNextMark'/>
+ <menuitem name='FindPreviousMark' action='/Edit/FindPreviousMark'/>
+ <separator/>
+ <menuitem name='IgnorePacket' action='/Edit/IgnorePacket'/>
+ <menuitem name='IgnoreAllDisplayedPackets' action='/Edit/IgnoreAllDisplayedPackets'/>
+ <menuitem name='Un-IgnoreAllPackets' action='/Edit/Un-IgnoreAllPackets'/>
+ <separator/>
+ <menuitem name='SetTimeReference' action='/Edit/SetTimeReference'/>
+ <menuitem name='Un-TimeReferenceAllPackets' action='/Edit/Un-TimeReferenceAllPackets'/>
+ <menuitem name='FindNextTimeReference' action='/Edit/FindNextTimeReference'/>
+ <menuitem name='FindPreviousTimeReference' action='/Edit/FindPreviousTimeReference'/>
+ <menuitem name='TimeShift' action='/Edit/TimeShift'/>
+ <separator/>
+ <menuitem name='EditPacket' action='/Edit/EditPacket'/>
+ <separator/>
+ <menuitem name='ConfigurationProfiles' action='/Edit/ConfigurationProfiles'/>
+ <menuitem name='Preferences' action='/Edit/Preferences'/>
+ </menu>
+ <menu name= 'ViewMenu' action='/View'>
+ <menuitem name='MainToolbar' action='/View/MainToolbar'/>
+ <menuitem name='FilterToolbar' action='/View/FilterToolbar'/>
+ <menuitem name='WirelessToolbar' action='/View/WirelessToolbar'/>
+ <menuitem name='Statusbar' action='/View/Statusbar'/>
+ <separator/>
+ <menuitem name='PacketList' action='/View/PacketList'/>
+ <menuitem name='PacketDetails' action='/View/PacketDetails'/>
+ <menuitem name='PacketBytes' action='/View/PacketBytes'/>
+ <separator/>
+ <menu name= 'TimeDisplayFormat' action='/View/TimeDisplayFormat'>
+ <menuitem name='DateandTimeofDay' action='/View/TimeDisplayFormat/DateandTimeofDay'/>
+ <menuitem name='TimeofDay' action='/View/TimeDisplayFormat/TimeofDay'/>
+ <menuitem name='SecondsSinceEpoch' action='/View/TimeDisplayFormat/SecondsSinceEpoch'/>
+ <menuitem name='SecondsSinceBeginningofCapture' action='/View/TimeDisplayFormat/SecondsSinceBeginningofCapture'/>
+ <menuitem name='SecondsSincePreviousCapturedPacket' action='/View/TimeDisplayFormat/SecondsSincePreviousCapturedPacket'/>
+ <menuitem name='SecondsSincePreviousDisplayedPacket' action='/View/TimeDisplayFormat/SecondsSincePreviousDisplayedPacket'/>
+ <menuitem name='UTCDateandTimeofDay' action='/View/TimeDisplayFormat/UTCDateandTimeofDay'/>
+ <menuitem name='UTCTimeofDay' action='/View/TimeDisplayFormat/UTCTimeofDay'/>
+ <separator/>
+ <menuitem name='FileFormatPrecision-Automatic' action='/View/TimeDisplayFormat/FileFormatPrecision-Automatic'/>
+ <menuitem name='FileFormatPrecision-Seconds' action='/View/TimeDisplayFormat/FileFormatPrecision-Seconds'/>
+ <menuitem name='FileFormatPrecision-Deciseconds' action='/View/TimeDisplayFormat/FileFormatPrecision-Deciseconds'/>
+ <menuitem name='FileFormatPrecision-Centiseconds' action='/View/TimeDisplayFormat/FileFormatPrecision-Centiseconds'/>
+ <menuitem name='FileFormatPrecision-Milliseconds' action='/View/TimeDisplayFormat/FileFormatPrecision-Milliseconds'/>
+ <menuitem name='FileFormatPrecision-Microseconds' action='/View/TimeDisplayFormat/FileFormatPrecision-Microseconds'/>
+ <menuitem name='FileFormatPrecision-Nanoseconds' action='/View/TimeDisplayFormat/FileFormatPrecision-Nanoseconds'/>
+ <separator/>
+ <menuitem name='DisplaySecondsWithHoursAndMinutes' action='/View/TimeDisplayFormat/DisplaySecondsWithHoursAndMinutes'/>
+ </menu>
+ <menu name= 'NameResolution' action='/View/NameResolution'>
+ <menuitem name='ResolveName' action='/View/NameResolution/ResolveName'/>
+ <separator/>
+ <menuitem name='EnableforMACLayer' action='/View/NameResolution/EnableforMACLayer'/>
+ <menuitem name='EnableforNetworkLayer' action='/View/NameResolution/EnableforNetworkLayer'/>
+ <menuitem name='EnableforTransportLayer' action='/View/NameResolution/EnableforTransportLayer'/>
+ </menu>
+ <menuitem name='ColorizePacketList' action='/View/ColorizePacketList'/>
+ <menuitem name='AutoScrollinLiveCapture' action='/View/AutoScrollinLiveCapture'/>
+ <separator/>
+ <menuitem name='ZoomIn' action='/View/ZoomIn'/>
+ <menuitem name='ZoomOut' action='/View/ZoomOut'/>
+ <menuitem name='NormalSize' action='/View/NormalSize'/>
+ <separator/>
+ <menuitem name='ResizeAllColumns' action='/View/ResizeAllColumns'/>
+ <menuitem name='DisplayedColumns' action='/View/DisplayedColumns'/>
+ <separator/>
+ <menuitem name='ExpandSubtrees' action='/View/ExpandSubtrees'/>
+ <menuitem name='ExpandAll' action='/View/ExpandAll'/>
+ <menuitem name='CollapseAll' action='/View/CollapseAll'/>
+ <separator/>
+ <menu name= 'ColorizeConversation' action='/View/ColorizeConversation'>
+ <menuitem name='Color1' action='/View/ColorizeConversation/Color 1'/>
+ <menuitem name='Color2' action='/View/ColorizeConversation/Color 2'/>
+ <menuitem name='Color3' action='/View/ColorizeConversation/Color 3'/>
+ <menuitem name='Color4' action='/View/ColorizeConversation/Color 4'/>
+ <menuitem name='Color5' action='/View/ColorizeConversation/Color 5'/>
+ <menuitem name='Color6' action='/View/ColorizeConversation/Color 6'/>
+ <menuitem name='Color7' action='/View/ColorizeConversation/Color 7'/>
+ <menuitem name='Color8' action='/View/ColorizeConversation/Color 8'/>
+ <menuitem name='Color9' action='/View/ColorizeConversation/Color 9'/>
+ <menuitem name='Color10' action='/View/ColorizeConversation/Color 10'/>
+ <menuitem name='NewColoringRule' action='/View/ColorizeConversation/NewColoringRule'/>
+ </menu>
+ <separator/>
+ <menuitem name='ResetColoring1-10' action='/View/ResetColoring1-10'/>
+ <menuitem name='ColoringRules' action='/View/ColoringRules'/>
+ <separator/>
+ <menuitem name='ShowPacketinNewWindow' action='/View/ShowPacketinNewWindow'/>
+ <menuitem name='Reload' action='/View/Reload'/>
+ </menu>
+ <menu name= 'GoMenu' action='/Go'>
+ <menuitem name='Back' action='/Go/Back'/>
+ <menuitem name='Forward' action='/Go/Forward'/>
+ <menuitem name='Goto' action='/Go/Goto'/>
+ <menuitem name='GotoCorrespondingPacket' action='/Go/GotoCorrespondingPacket'/>
+ <separator/>
+ <menuitem name='PreviousPacket' action='/Go/PreviousPacket'/>
+ <menuitem name='NextPacket' action='/Go/NextPacket'/>
+ <menuitem name='FirstPacket' action='/Go/FirstPacket'/>
+ <menuitem name='LastPacket' action='/Go/LastPacket'/>
+ <menuitem name='PreviousPacketInConversation' action='/Go/PreviousPacketInConversation'/>
+ <menuitem name='NextPacketInConversation' action='/Go/NextPacketInConversation'/>
+ </menu>
+ <menu name= 'CaptureMenu' action='/Capture'>
+ <menuitem name='Interfaces' action='/Capture/Interfaces'/>
+ <menuitem name='Options' action='/Capture/Options'/>
+ <menuitem name='Start' action='/Capture/Start'/>
+ <menuitem name='Stop' action='/Capture/Stop'/>
+ <menuitem name='Restart' action='/Capture/Restart'/>
+ <menuitem name='CaptureFilters' action='/Capture/CaptureFilters'/>
+ </menu>
+ <menu name= 'AnalyzeMenu' action='/Analyze'>
+ <menuitem name='DisplayFilters' action='/Analyze/DisplayFilters'/>
+ <menuitem name='DisplayFilterMacros' action='/Analyze/DisplayFilterMacros'/>
+ <separator/>
+ <menuitem name='ApplyasColumn' action='/Analyze/ApplyasColumn'/>
+ <menu name= 'ApplyAsFilter' action='/Analyze/ApplyasFilter'>
+ <menuitem name='Selected' action='/Analyze/ApplyasFilter/Selected'/>
+ <menuitem name='NotSelected' action='/Analyze/ApplyasFilter/NotSelected'/>
+ <menuitem name='AndSelected' action='/Analyze/ApplyasFilter/AndSelected'/>
+ <menuitem name='OrSelected' action='/Analyze/ApplyasFilter/OrSelected'/>
+ <menuitem name='AndNotSelected' action='/Analyze/ApplyasFilter/AndNotSelected'/>
+ <menuitem name='OrNotSelected' action='/Analyze/ApplyasFilter/OrNotSelected'/>
+ </menu>
+ <menu name= 'PrepareaFilter' action='/Analyze/PrepareaFilter'>
+ <menuitem name='Selected' action='/Analyze/PrepareaFilter/Selected'/>
+ <menuitem name='NotSelected' action='/Analyze/PrepareaFilter/NotSelected'/>
+ <menuitem name='AndSelected' action='/Analyze/PrepareaFilter/AndSelected'/>
+ <menuitem name='OrSelected' action='/Analyze/PrepareaFilter/OrSelected'/>
+ <menuitem name='AndNotSelected' action='/Analyze/PrepareaFilter/AndNotSelected'/>
+ <menuitem name='OrNotSelected' action='/Analyze/PrepareaFilter/OrNotSelected'/>
+ </menu>
+ <separator/>
+ <menuitem name='EnabledProtocols' action='/Analyze/EnabledProtocols'/>
+ <menuitem name='DecodeAs' action='/Analyze/DecodeAs'/>
+ <menuitem name='UserSpecifiedDecodes' action='/Analyze/UserSpecifiedDecodes'/>
+ <separator/>
+ <menuitem name='FollowTCPStream' action='/Analyze/FollowTCPStream'/>
+ <menuitem name='FollowUDPStream' action='/Analyze/FollowUDPStream'/>
+ <menuitem name='FollowSSLStream' action='/Analyze/FollowSSLStream'/>
+ <menuitem name='ExpertInfoComposite' action='/Analyze/ExpertInfoComposite'/>
+ <menu name= 'ConversationFilterMenu' action='/Analyze/ConversationFilter'>
+ <placeholder name='Filters'/>
+ </menu>
+ </menu>
+ <menu name= 'StatisticsMenu' action='/Statistics'>
+ <menuitem name='Summary' action='/Statistics/Summary'/>
+ <menuitem name='ProtocolHierarchy' action='/Statistics/ProtocolHierarchy'/>
+ <menuitem name='Conversations' action='/Statistics/Conversations'/>
+ <menuitem name='Endpoints' action='/Statistics/Endpoints'/>
+ <menuitem name='PacketLengths' action='/Statistics/plen'/>
+ <menuitem name='IOGraphs' action='/Statistics/IOGraphs'/>
+ <separator/>
+ <menu name= 'ConversationListMenu' action='/Stataistics/ConversationList'>
+ <menuitem name='Ethernet' action='/Stataistics/ConversationList/Ethernet'/>
+ <menuitem name='FibreChannel' action='/Stataistics/ConversationList/FibreChannel'/>
+ <menuitem name='FDDI' action='/Stataistics/ConversationList/FDDI'/>
+ <menuitem name='IP' action='/Stataistics/ConversationList/IP'/>
+ <menuitem name='IPv6' action='/Stataistics/ConversationList/IPv6'/>
+ <menuitem name='JXTA' action='/Stataistics/ConversationList/JXTA'/>
+ <menuitem name='NCP' action='/Stataistics/ConversationList/NCP'/>
+ <menuitem name='RSVP' action='/Stataistics/ConversationList/RSVP'/>
+ <menuitem name='SCTP' action='/Stataistics/ConversationList/SCTP'/>
+ <menuitem name='TCPIP' action='/Stataistics/ConversationList/TCPIP'/>
+ <menuitem name='TR' action='/Stataistics/ConversationList/TR'/>
+ <menuitem name='UDPIP' action='/Stataistics/ConversationList/UDPIP'/>
+ <menuitem name='USB' action='/Stataistics/ConversationList/USB'/>
+ <menuitem name='WLAN' action='/Stataistics/ConversationList/WLAN'/>
+ </menu>
+ <menu name= 'EndpointListMenu' action='/Statistics/EndpointList'>
+ <menuitem name='Ethernet' action='/Statistics/EndpointList/Ethernet'/>
+ <menuitem name='FibreChannel' action='/Statistics/EndpointList/FibreChannel'/>
+ <menuitem name='FDDI' action='/Statistics/EndpointList/FDDI'/>
+ <menuitem name='IP' action='/Statistics/EndpointList/IP'/>
+ <menuitem name='IPv6' action='/Statistics/EndpointList/IPv6'/>
+ <menuitem name='JXTA' action='/Statistics/EndpointList/JXTA'/>
+ <menuitem name='RSVP' action='/Statistics/EndpointList/RSVP'/>
+ <menuitem name='SCTP' action='/Statistics/EndpointList/SCTP'/>
+ <menuitem name='TCPIP' action='/Statistics/EndpointList/TCPIP'/>
+ <menuitem name='TR' action='/Statistics/EndpointList/TR'/>
+ <menuitem name='UDPIP' action='/Statistics/EndpointList/UDPIP'/>
+ <menuitem name='USB' action='/Statistics/EndpointList/USB'/>
+ <menuitem name='WLAN' action='/Statistics/EndpointList/WLAN'/>
+ </menu>
+ <menu name='ServiceResponseTimeMenu' action='/Statistics/ServiceResponseTime'>
+ <menuitem name='AFP' action='/Statistics/ServiceResponseTime/AFP'/>
+ <menuitem name='ONC-RPC' action='/Statistics/ServiceResponseTime/ONC-RPC'/>
+ <menuitem name='Camel' action='/Statistics/ServiceResponseTime/Camel'/>
+ <menuitem name='DCE-RPC' action='/Statistics/ServiceResponseTime/DCE-RPC'/>
+ <menuitem name='Diameter' action='/Statistics/ServiceResponseTime/Diameter'/>
+ <menuitem name='FibreChannel' action='/Statistics/ServiceResponseTime/FibreChannel'/>
+ <menuitem name='GTP' action='/Statistics/ServiceResponseTime/GTP'/>
+ <menuitem name='H225' action='/Statistics/ServiceResponseTime/H225'/>
+ <menuitem name='LDAP' action='/Statistics/ServiceResponseTime/LDAP'/>
+ <menuitem name='MEGACO' action='/Statistics/ServiceResponseTime/MEGACO'/>
+ <menuitem name='MGCP' action='/Statistics/ServiceResponseTime/MGCP'/>
+ <menuitem name='NCP' action='/Statistics/ServiceResponseTime/NCP'/>
+ <menuitem name='RADIUS' action='/Statistics/ServiceResponseTime/RADIUS'/>
+ <menuitem name='SCSI' action='/Statistics/ServiceResponseTime/SCSI'/>
+ <menuitem name='SMB2' action='/Statistics/ServiceResponseTime/SMB2'/>
+ </menu>
+ <separator/>
+ <menuitem name='ANCP' action='/StatisticsMenu/ancp'/>
+ <menu name= 'BACnetMenu' action='/StatisticsMenu/BACnet'>
+ <menuitem name='bacapp_instanceid' action='/StatisticsMenu/BACnet/bacapp_instanceid'/>
+ <menuitem name='bacapp_ip' action='/StatisticsMenu/BACnet/bacapp_ip'/>
+ <menuitem name='bacapp_objectid' action='/StatisticsMenu/BACnet/bacapp_objectid'/>
+ <menuitem name='bacapp_service' action='/StatisticsMenu/BACnet/bacapp_service'/>
+ </menu>
+ <menuitem name='BOOTP-DHCP' action='/StatisticsMenu/BOOTP-DHCP'/>
+ <menuitem name='Collectd' action='/StatisticsMenu/collectd'/>
+ <menuitem name='Compare' action='/StatisticsMenu/compare'/>
+ <menuitem name='FlowGraph' action='/StatisticsMenu/FlowGraph'/>
+ <menu name= 'HTTPMenu' action='/StatisticsMenu/HTTP'>
+ <menuitem name='http' action='/StatisticsMenu/HTTP/http'/>
+ <menuitem name='http_req' action='/StatisticsMenu/HTTP/http_req'/>
+ <menuitem name='http_srv' action='/StatisticsMenu/HTTP/http_srv'/>
+ </menu>
+ <menuitem name='IPAddresses' action='/StatisticsMenu/ip_hosts'/>
+ <menuitem name='IPDestinations' action='/StatisticsMenu/dests'/>
+ <menuitem name='IPptype' action='/StatisticsMenu/ptype'/>
+ <menuitem name='ONC-RPC-Programs' action='/StatisticsMenu/ONC-RPC-Programs'/>
+ <menu name= 'SametimeMenu' action='/StatisticsMenu/Sametime'>
+ <menuitem name='sametime' action='/StatisticsMenu/Sametime/sametime'/>
+ </menu>
+ <menu name= 'TCPStreamGraphMenu' action='/StatisticsMenu/TCPStreamGraphMenu'>
+ <menuitem name='Sequence-Graph-Stevens' action='/StatisticsMenu/TCPStreamGraphMenu/Time-Sequence-Graph-Stevens'/>
+ <menuitem name='Sequence-Graph-tcptrace' action='/StatisticsMenu/TCPStreamGraphMenu/Time-Sequence-Graph-tcptrace'/>
+ <menuitem name='Throughput-Graph' action='/StatisticsMenu/TCPStreamGraphMenu/Throughput-Graph'/>
+ <menuitem name='RTT-Graph' action='/StatisticsMenu/TCPStreamGraphMenu/RTT-Graph'/>
+ <menuitem name='Window-Scaling-Graph' action='/StatisticsMenu/TCPStreamGraphMenu/Window-Scaling-Graph'/>
+ </menu>
+ <menuitem name='UDPMulticastStreams' action='/StatisticsMenu/UDPMulticastStreams'/>
+ <menuitem name='WLANTraffic' action='/StatisticsMenu/WLANTraffic'/>
+ </menu>
+ <menu name= 'TelephonyMenu' action='/Telephony'>
+ <menu name= 'ANSI' action='/Telephony/ANSI'>
+ <menuitem name='BSMAP' action='/Telephony/ANSI/BSMAP'/>
+ <menuitem name='DTAP' action='/Telephony/ANSI/DTAP'/>
+ <menuitem name='MAP-OP' action='/Telephony/ANSI/MAP-OP'/>
+ </menu>
+ <menu name= 'GSM' action='/Telephony/GSM'>
+ <menuitem name='BSSMAP' action='/Telephony/GSM/BSSMAP'/>
+ <menu name='GSM-DTAP' action='/Telephony/GSM/DTAP'>
+ <menuitem name='CallControl' action='/Telephony/GSM/DTAP/CC'/>
+ <menuitem name='GPRS-MM' action='/Telephony/GSM/DTAP/GMM'/>
+ <menuitem name='GPRS-SM' action='/Telephony/GSM/DTAP/SM'/>
+ <menuitem name='MM' action='/Telephony/GSM/DTAP/MM'/>
+ <menuitem name='RR' action='/Telephony/GSM/DTAP/RR'/>
+ <menuitem name='SMS' action='/Telephony/GSM/DTAP/SMS'/>
+ <menuitem name='TP' action='/Telephony/GSM/DTAP/TP'/>
+ <menuitem name='SS' action='/Telephony/GSM/DTAP/SS'/>
+ </menu>
+ <menuitem name='SACCH' action='/Telephony/GSM/SACCH'/>
+ <menuitem name='MAP-OP' action='/Telephony/GSM/MAP-OP'/>
+ <menuitem name='MAP-Summary' action='/Telephony/GSM/MAPSummary'/>
+ </menu>
+ <menuitem name='H225' action='/Telephony/H225'/>
+ <menu name= 'IAX2menu' action='/Telephony/IAX2'>
+ <menuitem name='StreamAnalysis' action='/Telephony/IAX2/StreamAnalysis'/>
+ </menu>
+ <menuitem name='ISUP' action='/Telephony/isup_msg'/>
+ <menu name= 'LTEmenu' action='/Telephony/LTE'>
+ <menuitem name='LTE_MAC' action='/Telephony/LTE/MAC'/>
+ <menuitem name='LTE_RLC' action='/Telephony/LTE/RLC'/>
+ </menu>
+ <menu name= 'MTP3menu' action='/Telephony/MTP3'>
+ <menuitem name='MSUs' action='/Telephony/MTP3/MSUs'/>
+ <menuitem name='MSUSummary' action='/Telephony/MTP3/MSUSummary'/>
+ </menu>
+ <menu name= 'RTPmenu' action='/Telephony/RTP'>
+ <menuitem name='ShowAllStreams' action='/Telephony/RTP/ShowAllStreams'/>
+ <menuitem name='StreamAnalysis' action='/Telephony/RTP/StreamAnalysis'/>
+ </menu>
+ <menu name= 'RTSPmenu' action='/Telephony/RTSP'>
+ <menuitem name='rtsp' action='/Telephony/RTSP/rtsp'/>
+ </menu>
+ <menu name= 'SCTPmenu' action='/Telephony/SCTP'>
+ <menuitem name='AnalysethisAssociation' action='/Telephony/SCTP/AnalysethisAssociation'/>
+ <menuitem name='ShowAllAssociations' action='/Telephony/SCTP/ShowAllAssociations'/>
+ <menuitem name='ChunkCounter' action='/Telephony/SCTP/ChunkCounter'/>
+ </menu>
+ <menuitem name='SIP' action='/Telephony/SIP'/>
+ <menuitem name='SMPP' action='/Telephony/smpp_commands'/>
+ <menuitem name='UCP' action='/Telephony/ucp_messages'/>
+ <menuitem name='VoIPCalls' action='/Telephony/VoIPCalls'/>
+ <menuitem name='WSP' action='/Telephony/WSP'/>
+ </menu>
+ <menu name= 'ToolsMenu' action='/Tools'>
+ <menuitem name='FirewallACLRules' action='/Tools/FirewallACLRules'/>
+ <menu name='LUA' action='/Tools/LUA'>
+ <placeholder name='LUA-menu-items'/>
+ </menu>
+ </menu>
+ <menu name= 'InternalsMenu' action='/Internals'>
+ <menuitem name='Dissectortables' action='/Internals/Dissectortables'/>
+ <menuitem name='SupportedProtocols' action='/Internals/SupportedProtocols'/>
+ </menu>
+ <menu name= 'HelpMenu' action='/Help'>
+ <menuitem name='Contents' action='/Help/Contents'/>
+ <menu name= 'ManualPages' action='/Help/ManualPages'>
+ <menuitem name='Wireshark' action='/Help/ManualPages/Wireshark'/>
+ <menuitem name='WiresharkFilter' action='/Help/ManualPages/WiresharkFilter'/>
+ <separator/>
+ <menuitem name='TShark' action='/Help/ManualPages/TShark'/>
+ <menuitem name='RawShark' action='/Help/ManualPages/RawShark'/>
+ <menuitem name='Dumpcap' action='/Help/ManualPages/Dumpcap'/>
+ <menuitem name='Mergecap' action='/Help/ManualPages/Mergecap'/>
+ <menuitem name='Editcap' action='/Help/ManualPages/Editcap'/>
+ <menuitem name='Text2pcap' action='/Help/ManualPages/Text2pcap'/>
+ </menu>
+ <separator/>
+ <menuitem name='Website' action='/Help/Website'/>
+ <menuitem name='FAQs' action='/Help/FAQs'/>
+ <menuitem name='Downloads' action='/Help/Downloads'/>
+ <separator/>
+ <menuitem name='Wiki' action='/Help/Wiki'/>
+ <menuitem name='SampleCaptures' action='/Help/SampleCaptures'/>
+ <separator/>
+ <menuitem name='AboutWireshark' action='/Help/AboutWireshark'/>
+ </menu>
+ </menubar>
+</ui>
+
+
diff --git a/ui/gtk/utf8_entities.h b/ui/gtk/utf8_entities.h
new file mode 100644
index 0000000000..b693b14459
--- /dev/null
+++ b/ui/gtk/utf8_entities.h
@@ -0,0 +1,55 @@
+/* utf8_entities.h
+ * Byte sequences for various UTF-8 entities
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __UTF8_ENTITIES_H__
+#define __UTF8_ENTITIES_H__
+
+/*
+ * Sequences can be found at
+ * http://www.fileformat.info/info/unicode/
+ * http://www.utf8-chartable.de/
+ * and other places
+ */
+
+#define UTF8_HORIZONTAL_ELLIPSIS "\xe2\x80\xa6" /* 8230 / 0x2026 */
+#define UTF8_LEFTWARDS_ARROW "\xe2\x86\x90" /* 8592 / 0x2190 */
+#define UTF8_RIGHTWARDS_ARROW "\xe2\x86\x92" /* 8594 / 0x2192 */
+#define UTF8_LEFT_RIGHT_ARROW "\xe2\x86\x94" /* 8596 / 0x2194 */
+
+#define UTF8_PLACE_OF_INTEREST_SIGN "\xe2\x8c\x98" /* 8984 / 0x2318 */
+#endif /* __UTF8_ENTITIES_H__ */
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/gtk/voip_calls.c b/ui/gtk/voip_calls.c
new file mode 100644
index 0000000000..cec92c1d91
--- /dev/null
+++ b/ui/gtk/voip_calls.c
@@ -0,0 +1,4083 @@
+/* voip_calls.c
+ * VoIP calls summary addition for Wireshark
+ *
+ * $Id$
+ *
+ * Copyright 2004, Ericsson, Spain
+ * By Francisco Alcoba <francisco.alcoba@ericsson.com>
+ *
+ * based on h323_calls.c
+ * Copyright 2004, Iskratel, Ltd, Kranj
+ * By Miha Jemec <m.jemec@iskratel.si>
+ *
+ * H323, RTP, RTP Event, MGCP, AudioCodes (ISDN PRI and CAS), T38 and Graph Support
+ * By Alejandro Vaquero, alejandro.vaquero@verso.com
+ * Copyright 2005, Verso Technologies Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <string.h>
+
+#include <epan/epan.h>
+#include <epan/packet.h>
+#include <epan/tap.h>
+#include <epan/tap-voip.h>
+#include <epan/dissectors/packet-sip.h>
+#include <epan/dissectors/packet-mtp3.h>
+#include <epan/dissectors/packet-isup.h>
+#include <epan/dissectors/packet-h225.h>
+#include <epan/dissectors/packet-h245.h>
+#include <epan/dissectors/packet-q931.h>
+#include <epan/dissectors/packet-sdp.h>
+#include <epan/dissectors/packet-mgcp.h>
+#include <epan/dissectors/packet-actrace.h>
+#include <epan/dissectors/packet-rtp.h>
+#include <epan/dissectors/packet-rtp-events.h>
+#include <epan/dissectors/packet-t38.h>
+#include <epan/dissectors/packet-t30.h>
+#include <epan/dissectors/packet-h248.h>
+#include <epan/dissectors/packet-sccp.h>
+#include <plugins/unistim/packet-unistim.h>
+#include <epan/dissectors/packet-skinny.h>
+#include <epan/dissectors/packet-iax2.h>
+#include <epan/rtp_pt.h>
+
+#include "../alert_box.h"
+#include "../simple_dialog.h"
+#include "../ui_util.h"
+
+#include "ui/gtk/graph_analysis.h"
+#include "ui/gtk/voip_calls.h"
+#include "ui/gtk/voip_calls_dlg.h"
+#include "ui/gtk/main.h"
+
+#ifdef HAVE_LIBPORTAUDIO
+#include "ui/gtk/rtp_player.h"
+#endif /* HAVE_LIBPORTAUDIO */
+
+
+const char *voip_call_state_name[8]={
+ "",
+ "CALL SETUP",
+ "RINGING",
+ "IN CALL",
+ "CANCELLED",
+ "COMPLETED",
+ "REJECTED",
+ "UNKNOWN"
+};
+
+/* defines whether we can consider the call active */
+const char *voip_protocol_name[]={
+ "SIP",
+ "ISUP",
+ "H.323",
+ "MGCP",
+ "AC_ISDN",
+ "AC_CAS",
+ "T.38",
+ "H.248",
+ "SCCP",
+ "BSSMAP",
+ "RANAP",
+ "UNISTIM",
+ "SKINNY",
+ "IAX2",
+ "VoIP"
+};
+
+typedef struct {
+ gchar *frame_label;
+ gchar *comment;
+} graph_str;
+
+#define H245_MAX 6
+
+typedef struct {
+ guint32 frame_num;
+ gint8 labels_count;
+ graph_str labels[H245_MAX];
+} h245_labels_t;
+
+static h245_labels_t h245_labels;
+
+/* defines a RTP stream */
+typedef struct _voip_rtp_stream_info {
+ address src_addr;
+ guint16 src_port;
+ address dest_addr;
+ guint16 dest_port;
+ guint32 ssrc;
+ guint32 pt;
+ gchar *pt_str;
+ gboolean is_srtp;
+ guint32 npackets;
+ gboolean end_stream;
+
+ guint32 setup_frame_number; /* frame number of setup message */
+ /* The frame_data struct holds the frame number and timing information needed. */
+ frame_data *start_fd;
+ frame_data *stop_fd;
+ gint32 rtp_event;
+} voip_rtp_stream_info_t;
+
+/****************************************************************************/
+/* the one and only global voip_calls_tapinfo_t structure */
+static voip_calls_tapinfo_t the_tapinfo_struct =
+ {0, NULL, 0, NULL, 0, 0, 0, 0, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+/* the one and only global voip_rtp_tapinfo_t structure */
+static voip_rtp_tapinfo_t the_tapinfo_rtp_struct =
+ {0, NULL, 0, 0};
+
+/****************************************************************************/
+/* when there is a [re]reading of packet's */
+void voip_calls_reset(voip_calls_tapinfo_t *tapinfo)
+{
+ voip_calls_info_t *callsinfo;
+ voip_rtp_tapinfo_t *rtp_tapinfo = &the_tapinfo_rtp_struct;
+ voip_rtp_stream_info_t *strinfo;
+ graph_analysis_item_t *graph_item;
+ GList *list;
+
+#ifdef HAVE_LIBPORTAUDIO
+ /* reset the RTP player */
+ reset_rtp_player();
+#endif
+
+ /* free the data items first */
+ list = g_list_first(tapinfo->callsinfo_list);
+ while (list)
+ {
+ callsinfo = list->data;
+ g_free(callsinfo->call_id);
+ g_free(callsinfo->from_identity);
+ g_free(callsinfo->to_identity);
+ g_free((void *)(callsinfo->initial_speaker.data));
+ g_free(callsinfo->protocol_name);
+ g_free(callsinfo->call_comment);
+
+ if (callsinfo->free_prot_info && callsinfo->prot_info)
+ callsinfo->free_prot_info(callsinfo->prot_info);
+
+ g_free(list->data);
+ list = g_list_next(list);
+ }
+ g_list_free(tapinfo->callsinfo_list);
+ tapinfo->callsinfo_list = NULL;
+ tapinfo->ncalls = 0;
+ tapinfo->npackets = 0;
+ tapinfo->start_packets = 0;
+ tapinfo->completed_calls = 0;
+ tapinfo->rejected_calls = 0;
+
+ /* free the graph data items first */
+ list = g_list_first(tapinfo->graph_analysis->list);
+ while (list)
+ {
+ graph_item = list->data;
+ g_free(graph_item->frame_label);
+ g_free(graph_item->comment);
+ g_free((void *)graph_item->src_addr.data);
+ g_free((void *)graph_item->dst_addr.data);
+ g_free(list->data);
+ list = g_list_next(list);
+ }
+ g_list_free(tapinfo->graph_analysis->list);
+ tapinfo->graph_analysis->nconv = 0;
+ tapinfo->graph_analysis->list = NULL;
+
+ ++(tapinfo->launch_count);
+
+ /* free the strinfo data items first */
+ list = g_list_first(rtp_tapinfo->list);
+ while(list)
+ {
+ strinfo = list->data;
+ g_free(strinfo->pt_str);
+ list = g_list_next(list);
+ }
+ g_list_free(rtp_tapinfo->list);
+ rtp_tapinfo->list = NULL;
+
+ return;
+}
+
+/****************************************************************************/
+void graph_analysis_data_init(void){
+ the_tapinfo_struct.graph_analysis = g_malloc(sizeof(graph_analysis_info_t));
+ the_tapinfo_struct.graph_analysis->nconv = 0;
+ the_tapinfo_struct.graph_analysis->list = NULL;
+}
+
+/****************************************************************************/
+/* Add a new item into the graph */
+static void add_to_graph(voip_calls_tapinfo_t *tapinfo _U_, packet_info *pinfo, const gchar *frame_label, const gchar *comment, guint16 call_num, address *src_addr, address *dst_addr, guint16 line_style)
+{
+ graph_analysis_item_t *gai;
+
+ gai = g_malloc(sizeof(graph_analysis_item_t));
+ gai->fd = pinfo->fd;
+ COPY_ADDRESS(&(gai->src_addr),src_addr);
+ COPY_ADDRESS(&(gai->dst_addr),dst_addr);
+
+ gai->port_src=pinfo->srcport;
+ gai->port_dst=pinfo->destport;
+ if (frame_label != NULL)
+ gai->frame_label = g_strdup(frame_label);
+ else
+ gai->frame_label = g_strdup("");
+
+ if (comment != NULL)
+ gai->comment = g_strdup(comment);
+ else
+ gai->comment = g_strdup("");
+ gai->conv_num=call_num;
+ gai->line_style=line_style;
+ gai->display=FALSE;
+
+ tapinfo->graph_analysis->list = g_list_append(tapinfo->graph_analysis->list, gai);
+
+}
+
+/****************************************************************************/
+/* Append str to frame_label and comment in a graph item */
+/* return 0 if the frame_num is not in the graph list */
+static int append_to_frame_graph(voip_calls_tapinfo_t *tapinfo _U_, guint32 frame_num, const gchar *new_frame_label, const gchar *new_comment)
+{
+ graph_analysis_item_t *gai;
+ GList *list;
+ gchar *frame_label = NULL;
+ gchar *comment = NULL;
+
+ list = g_list_first(tapinfo->graph_analysis->list);
+ while (list)
+ {
+ gai = list->data;
+ if (gai->fd->num == frame_num){
+ frame_label = gai->frame_label;
+ comment = gai->comment;
+
+ if (new_frame_label != NULL){
+ gai->frame_label = g_strdup_printf("%s %s", frame_label, new_frame_label);
+ g_free(frame_label);
+ }
+
+ if (new_comment != NULL){
+ gai->comment = g_strdup_printf("%s %s", comment, new_comment);
+ g_free(comment);
+ }
+ break;
+ }
+ list = g_list_next(list);
+ }
+
+ return list ? 1 : 0;
+
+}
+
+/****************************************************************************/
+/* Change the frame_label and comment in a graph item if not NULL*/
+/* return 0 if the frame_num is not in the graph list */
+static int change_frame_graph(voip_calls_tapinfo_t *tapinfo _U_, guint32 frame_num, const gchar *new_frame_label, const gchar *new_comment)
+{
+ graph_analysis_item_t *gai;
+ GList *list;
+ gchar *frame_label = NULL;
+ gchar *comment = NULL;
+
+ list = g_list_first(tapinfo->graph_analysis->list);
+ while (list)
+ {
+ gai = list->data;
+ if (gai->fd->num == frame_num){
+ frame_label = gai->frame_label;
+ comment = gai->comment;
+
+ if (new_frame_label != NULL){
+ gai->frame_label = g_strdup(new_frame_label);
+ g_free(frame_label);
+ }
+
+ if (new_comment != NULL){
+ gai->comment = g_strdup(new_comment);
+ g_free(comment);
+ }
+ break;
+ }
+ list = g_list_next(list);
+ }
+
+ return list ? 1 : 0;
+
+}
+
+/****************************************************************************/
+/* Change all the graph items with call_num to new_call_num */
+static guint change_call_num_graph(voip_calls_tapinfo_t *tapinfo _U_, guint16 call_num, guint16 new_call_num)
+{
+ graph_analysis_item_t *gai;
+ GList *list;
+ guint items_changed;
+
+ items_changed = 0;
+ list = g_list_first(tapinfo->graph_analysis->list);
+ while (list)
+ {
+ gai = list->data;
+ if (gai->conv_num == call_num){
+ gai->conv_num = new_call_num;
+ items_changed++;
+ }
+ list = g_list_next(list);
+ }
+ return items_changed;
+}
+
+/****************************************************************************/
+/* Insert the item in the graph list */
+static void insert_to_graph_t38(voip_calls_tapinfo_t *tapinfo _U_, packet_info *pinfo, const gchar *frame_label, const gchar *comment, guint16 call_num, address *src_addr, address *dst_addr, guint16 line_style, guint32 frame_num)
+{
+ graph_analysis_item_t *gai, *new_gai;
+ GList *list;
+ guint item_num;
+ gboolean inserted;
+
+ new_gai = g_malloc(sizeof(graph_analysis_item_t));
+ new_gai->fd = new_packet_list_get_row_data(frame_num);
+ COPY_ADDRESS(&(new_gai->src_addr),src_addr);
+ COPY_ADDRESS(&(new_gai->dst_addr),dst_addr);
+
+ new_gai->port_src=pinfo->srcport;
+ new_gai->port_dst=pinfo->destport;
+ if (frame_label != NULL)
+ new_gai->frame_label = g_strdup(frame_label);
+ else
+ new_gai->frame_label = g_strdup("");
+
+ if (comment != NULL)
+ new_gai->comment = g_strdup(comment);
+ else
+ new_gai->comment = g_strdup("");
+ new_gai->conv_num=call_num;
+ new_gai->line_style=line_style;
+ new_gai->display=FALSE;
+
+ item_num = 0;
+ inserted = FALSE;
+ list = g_list_first(tapinfo->graph_analysis->list);
+ while (list)
+ {
+ gai = list->data;
+ if (gai->fd->num > frame_num){
+ the_tapinfo_struct.graph_analysis->list = g_list_insert(the_tapinfo_struct.graph_analysis->list, new_gai, item_num);
+ inserted = TRUE;
+ break;
+ }
+ list = g_list_next(list);
+ item_num++;
+ }
+
+ if (!inserted) tapinfo->graph_analysis->list = g_list_append(tapinfo->graph_analysis->list, new_gai);
+}
+
+/****************************************************************************/
+/* ***************************TAP for RTP Events*****************************/
+/****************************************************************************/
+
+static guint32 rtp_evt_frame_num = 0;
+static guint8 rtp_evt = 0;
+static gboolean rtp_evt_end = FALSE;
+/*static guint32 rtp_evt_setup_frame_num = 0;*/
+
+/****************************************************************************/
+/* whenever a rtp event packet is seen by the tap listener */
+static int
+rtp_event_packet(void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *rtp_event_info)
+{
+ const struct _rtp_event_info *pi = rtp_event_info;
+
+ /* do not consider RTP events packets without a setup frame */
+ if (pi->info_setup_frame_num == 0){
+ return 0;
+ }
+
+ rtp_evt_frame_num = pinfo->fd->num;
+ rtp_evt = pi->info_rtp_evt;
+ rtp_evt_end = pi->info_end;
+
+ return 0;
+}
+
+/****************************************************************************/
+static gboolean have_rtp_event_tap_listener=FALSE;
+
+void
+rtp_event_init_tap(void)
+{
+ GString *error_string;
+
+
+ if(have_rtp_event_tap_listener==FALSE)
+ {
+ error_string = register_tap_listener("rtpevent", &(the_tapinfo_rtp_struct.rtp_event_dummy),
+ NULL,
+ 0,
+ NULL,
+ rtp_event_packet,
+ NULL
+ );
+
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+ have_rtp_event_tap_listener=TRUE;
+ }
+}
+
+/****************************************************************************/
+
+void
+remove_tap_listener_rtp_event(void)
+{
+ protect_thread_critical_region();
+ remove_tap_listener(&(the_tapinfo_rtp_struct.rtp_event_dummy));
+ unprotect_thread_critical_region();
+
+ have_rtp_event_tap_listener=FALSE;
+}
+
+/****************************************************************************/
+/* ***************************TAP for RTP **********************************/
+/****************************************************************************/
+
+/****************************************************************************/
+/* when there is a [re]reading of RTP packet's */
+static void voip_rtp_reset(void *ptr _U_)
+{
+ voip_rtp_tapinfo_t *tapinfo = &the_tapinfo_rtp_struct;
+ GList *list;
+ /* free the data items first */
+ list = g_list_first(tapinfo->list);
+ while (list)
+ {
+ g_free(list->data);
+ list = g_list_next(list);
+ }
+ g_list_free(tapinfo->list);
+ tapinfo->list = NULL;
+ tapinfo->nstreams = 0;
+ return;
+}
+
+/****************************************************************************/
+/* whenever a RTP packet is seen by the tap listener */
+static int
+RTP_packet(void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, void const *RTPinfo)
+{
+ voip_rtp_tapinfo_t *tapinfo = &the_tapinfo_rtp_struct;
+ voip_rtp_stream_info_t *tmp_listinfo;
+ voip_rtp_stream_info_t *strinfo = NULL;
+ GList *list;
+ struct _rtp_conversation_info *p_conv_data = NULL;
+
+ const struct _rtp_info *pi = RTPinfo;
+
+ /* do not consider RTP packets without a setup frame */
+ if (pi->info_setup_frame_num == 0){
+ return 0;
+ }
+
+ /* add this RTP for future listening using the RTP Player*/
+#ifdef HAVE_LIBPORTAUDIO
+ add_rtp_packet(pi, pinfo);
+#endif
+
+ /* check whether we already have a RTP stream with this setup frame and ssrc in the list */
+ list = g_list_first(tapinfo->list);
+ while (list)
+ {
+ tmp_listinfo=list->data;
+ if ( (tmp_listinfo->setup_frame_number == pi->info_setup_frame_num)
+ && (tmp_listinfo->ssrc == pi->info_sync_src) && (tmp_listinfo->end_stream == FALSE)){
+ /* if the payload type has changed, we mark the stream as finished to create a new one
+ this is to show multiple payload changes in the Graph for example for DTMF RFC2833 */
+ if ( tmp_listinfo->pt != pi->info_payload_type ) {
+ tmp_listinfo->end_stream = TRUE;
+ } else {
+ strinfo = (voip_rtp_stream_info_t*)(list->data);
+ break;
+ }
+ }
+ list = g_list_next(list);
+ }
+
+ /* if this is a duplicated RTP Event End, just return */
+ if ((rtp_evt_frame_num == pinfo->fd->num) && !strinfo && (rtp_evt_end == TRUE)) {
+ return 0;
+ }
+
+ /* not in the list? then create a new entry */
+ if (strinfo==NULL){
+ strinfo = g_malloc(sizeof(voip_rtp_stream_info_t));
+ COPY_ADDRESS(&(strinfo->src_addr), &(pinfo->src));
+ strinfo->src_port = pinfo->srcport;
+ COPY_ADDRESS(&(strinfo->dest_addr), &(pinfo->dst));
+ strinfo->dest_port = pinfo->destport;
+ strinfo->ssrc = pi->info_sync_src;
+ strinfo->end_stream = FALSE;
+ strinfo->pt = pi->info_payload_type;
+ strinfo->pt_str = NULL;
+ strinfo->is_srtp = pi->info_is_srtp;
+ /* if it is dynamic payload, let use the conv data to see if it is defined */
+ if ( (strinfo->pt >= PT_UNDF_96) && (strinfo->pt <= PT_UNDF_127) ) {
+ /* Use existing packet info if available */
+ p_conv_data = p_get_proto_data(pinfo->fd, proto_get_id_by_filter_name("rtp"));
+ if (p_conv_data && p_conv_data->rtp_dyn_payload) {
+ encoding_name_and_rate_t *encoding_name_and_rate_pt = NULL;
+ encoding_name_and_rate_pt = g_hash_table_lookup(p_conv_data->rtp_dyn_payload, &strinfo->pt);
+ if (encoding_name_and_rate_pt) {
+ strinfo->pt_str = g_strdup(encoding_name_and_rate_pt->encoding_name);
+ }
+ }
+ }
+ if (!strinfo->pt_str) strinfo->pt_str = g_strdup(val_to_str_ext(strinfo->pt, &rtp_payload_type_short_vals_ext, "%u"));
+ strinfo->npackets = 0;
+ strinfo->start_fd = pinfo->fd;
+ strinfo->setup_frame_number = pi->info_setup_frame_num;
+ strinfo->rtp_event = -1;
+ tapinfo->list = g_list_append(tapinfo->list, strinfo);
+ }
+
+ if (strinfo!=NULL){
+ /* Add the info to the existing RTP stream */
+ strinfo->npackets++;
+ strinfo->stop_fd = pinfo->fd;
+
+ /* process RTP Event */
+ if (rtp_evt_frame_num == pinfo->fd->num) {
+ strinfo->rtp_event = rtp_evt;
+ if (rtp_evt_end == TRUE) {
+ strinfo->end_stream = TRUE;
+ }
+ }
+ }
+
+ the_tapinfo_struct.redraw = TRUE;
+
+ return 1;
+}
+
+/****************************************************************************/
+/* whenever a redraw in the RTP tap listener */
+static void RTP_packet_draw(void *prs _U_)
+{
+ voip_rtp_tapinfo_t *rtp_tapinfo = &the_tapinfo_rtp_struct;
+ GList *rtp_streams_list;
+ voip_rtp_stream_info_t *rtp_listinfo;
+ GList *voip_calls_graph_list;
+ guint item;
+ graph_analysis_item_t *gai;
+ graph_analysis_item_t *new_gai;
+ guint16 conv_num;
+ guint32 duration;
+
+ /* add each rtp stream to the graph */
+ rtp_streams_list = g_list_first(rtp_tapinfo->list);
+ while (rtp_streams_list)
+ {
+ rtp_listinfo = rtp_streams_list->data;
+
+ /* using the setup frame number of the RTP stream, we get the call number that it belongs */
+ voip_calls_graph_list = g_list_first(the_tapinfo_struct.graph_analysis->list);
+ while (voip_calls_graph_list)
+ {
+ gai = voip_calls_graph_list->data;
+ conv_num = gai->conv_num;
+ /* if we get the setup frame number, then get the time position to graph the RTP arrow */
+ if (rtp_listinfo->setup_frame_number == gai->fd->num){
+ /* look again from the begining because there are cases where the Setup frame is after the RTP */
+ voip_calls_graph_list = g_list_first(the_tapinfo_struct.graph_analysis->list);
+ item = 0;
+ while(voip_calls_graph_list){
+ gai = voip_calls_graph_list->data;
+ /* if RTP was already in the Graph, just update the comment information */
+ if (rtp_listinfo->start_fd->num == gai->fd->num){
+ duration = (guint32)(nstime_to_msec(&rtp_listinfo->stop_fd->rel_ts) - nstime_to_msec(&rtp_listinfo->start_fd->rel_ts));
+ g_free(gai->comment);
+ gai->comment = g_strdup_printf("%s Num packets:%u Duration:%u.%03us SSRC:0x%X",
+ (rtp_listinfo->is_srtp)?"SRTP":"RTP", rtp_listinfo->npackets,
+ duration/1000,(duration%1000), rtp_listinfo->ssrc);
+ break;
+ }
+
+ /* we increment the list here to be able to check if it is the last item in this calls, which means the RTP is after so we have to draw it */
+ voip_calls_graph_list = g_list_next(voip_calls_graph_list);
+ if (!voip_calls_graph_list) item++;
+
+ /* add the RTP item to the graph if was not there*/
+ if (rtp_listinfo->start_fd->num<gai->fd->num || !voip_calls_graph_list){
+ new_gai = g_malloc(sizeof(graph_analysis_item_t));
+ new_gai->fd = rtp_listinfo->start_fd;
+ COPY_ADDRESS(&(new_gai->src_addr),&(rtp_listinfo->src_addr));
+ COPY_ADDRESS(&(new_gai->dst_addr),&(rtp_listinfo->dest_addr));
+ new_gai->port_src = rtp_listinfo->src_port;
+ new_gai->port_dst = rtp_listinfo->dest_port;
+ duration = (guint32)(nstime_to_msec(&rtp_listinfo->stop_fd->rel_ts) - nstime_to_msec(&rtp_listinfo->start_fd->rel_ts));
+ new_gai->frame_label = g_strdup_printf("%s (%s) %s",
+ (rtp_listinfo->is_srtp)?"SRTP":"RTP",
+ rtp_listinfo->pt_str,
+ (rtp_listinfo->rtp_event == -1)?
+ "":val_to_str_const(rtp_listinfo->rtp_event, rtp_event_type_values, "Unknown RTP Event"));
+ new_gai->comment = g_strdup_printf("%s Num packets:%u Duration:%u.%03us SSRC:0x%X",
+ (rtp_listinfo->is_srtp)?"SRTP":"RTP", rtp_listinfo->npackets,
+ duration/1000,(duration%1000), rtp_listinfo->ssrc);
+ new_gai->conv_num = conv_num;
+ new_gai->display=FALSE;
+ new_gai->line_style = 2; /* the arrow line will be 2 pixels width */
+ the_tapinfo_struct.graph_analysis->list = g_list_insert(the_tapinfo_struct.graph_analysis->list, new_gai, item);
+ break;
+ }
+ if (voip_calls_graph_list) item++;
+ }
+ break;
+ }
+ voip_calls_graph_list = g_list_next(voip_calls_graph_list);
+ }
+ rtp_streams_list = g_list_next(rtp_streams_list);
+ }
+}
+
+static gboolean have_RTP_tap_listener=FALSE;
+/****************************************************************************/
+void
+rtp_init_tap(void)
+{
+ GString *error_string;
+
+ if(have_RTP_tap_listener==FALSE)
+ {
+ /* don't register tap listener, if we have it already */
+ error_string = register_tap_listener("rtp", &(the_tapinfo_rtp_struct.rtp_dummy), NULL,
+ 0,
+ voip_rtp_reset,
+ RTP_packet,
+ RTP_packet_draw
+ );
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+ have_RTP_tap_listener=TRUE;
+ }
+}
+
+/****************************************************************************/
+void
+remove_tap_listener_rtp(void)
+{
+ protect_thread_critical_region();
+ remove_tap_listener(&(the_tapinfo_rtp_struct.rtp_dummy));
+ unprotect_thread_critical_region();
+
+ have_RTP_tap_listener=FALSE;
+}
+
+/****************************************************************************/
+/******************************TAP for T38 **********************************/
+/****************************************************************************/
+
+/****************************************************************************/
+/* whenever a T38 packet is seen by the tap listener */
+static int
+T38_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *T38info)
+{
+ voip_calls_tapinfo_t *tapinfo = &the_tapinfo_struct;
+
+ voip_calls_info_t *callsinfo = NULL;
+ voip_calls_info_t *tmp_listinfo;
+ GList *voip_calls_graph_list;
+ GList *list;
+ gchar *frame_label = NULL;
+ gchar *comment = NULL;
+ graph_analysis_item_t *tmp_gai, *gai = NULL;
+ guint16 line_style = 2;
+ double duration;
+ int conv_num = -1;
+
+ const t38_packet_info *pi = T38info;
+
+ if (pi->setup_frame_number != 0) {
+ /* using the setup frame number of the T38 packet, we get the call number that it belongs */
+ voip_calls_graph_list = g_list_first(tapinfo->graph_analysis->list);
+ while (voip_calls_graph_list)
+ {
+ tmp_gai = voip_calls_graph_list->data;
+ if (pi->setup_frame_number == tmp_gai->fd->num){
+ gai = tmp_gai;
+ break;
+ }
+ voip_calls_graph_list = g_list_next(voip_calls_graph_list);
+ }
+ if (gai) conv_num = (int) gai->conv_num;
+ }
+
+ /* if setup_frame_number in the t38 packet is 0, it means it was not set using an SDP or H245 sesion, which means we don't
+ * have the associated Voip calls. It probably means the the packet was decoded using the default t38 port, or using "Decode as.."
+ * in this case we create a "voip" call that only have t38 media (no signaling)
+ * OR if we have not found the Setup message in the graph.
+ */
+ if ( (pi->setup_frame_number == 0) || (gai == NULL) ){
+ /* check whether we already have a call with these parameters in the list */
+ list = g_list_first(tapinfo->callsinfo_list);
+ while (list)
+ {
+ tmp_listinfo=list->data;
+ if (tmp_listinfo->protocol == MEDIA_T38){
+ callsinfo = (voip_calls_info_t*)(list->data);
+ break;
+ }
+ list = g_list_next (list);
+ }
+
+ /* not in the list? then create a new entry */
+ if (callsinfo==NULL){
+ callsinfo = g_malloc0(sizeof(voip_calls_info_t));
+ callsinfo->call_active_state = VOIP_ACTIVE;
+ callsinfo->call_state = VOIP_UNKNOWN;
+ callsinfo->from_identity=g_strdup("T38 Media only");
+ callsinfo->to_identity=g_strdup("T38 Media only");
+ COPY_ADDRESS(&(callsinfo->initial_speaker),&(pinfo->src));
+ callsinfo->selected=FALSE;
+ callsinfo->start_fd = pinfo->fd;
+ callsinfo->protocol=MEDIA_T38;
+ callsinfo->prot_info=NULL;
+ callsinfo->free_prot_info = NULL;
+ callsinfo->npackets = 0;
+ callsinfo->call_num = tapinfo->ncalls++;
+ tapinfo->callsinfo_list = g_list_append(tapinfo->callsinfo_list, callsinfo);
+ }
+ ++(callsinfo->npackets);
+ /* increment the packets counter of all calls */
+ ++(tapinfo->npackets);
+
+ conv_num = (int) callsinfo->call_num;
+ }
+
+ /* at this point we should have found the call num for this t38 packets belong */
+ if (conv_num == -1) {
+ return 0;
+ }
+
+ /* add the item to the graph list */
+ if (pi->type_msg == 0) { /* 0=t30-indicator */
+ frame_label = g_strdup(val_to_str(pi->t30ind_value, t38_T30_indicator_vals, "Ukn (0x%02X)") );
+ comment = g_strdup_printf("t38:t30 Ind:%s",val_to_str(pi->t30ind_value, t38_T30_indicator_vals, "Ukn (0x%02X)") );
+ line_style = 1;
+ } else if (pi->type_msg == 1) { /* 1=data */
+ switch(pi->Data_Field_field_type_value){
+ case 0: /* hdlc-data */
+ break;
+ case 2: /* hdlc-fcs-OK */
+ case 4: /* hdlc-fcs-OK-sig-end */
+ frame_label = g_strdup_printf("%s %s", val_to_str(pi->t30_Facsimile_Control & 0x7F, t30_facsimile_control_field_vals_short, "Ukn (0x%02X)"), pi->desc);
+ comment = g_strdup_printf("t38:%s:HDLC:%s",val_to_str(pi->data_value, t38_T30_data_vals, "Ukn (0x%02X)"), val_to_str(pi->t30_Facsimile_Control & 0x7F, t30_facsimile_control_field_vals, "Ukn (0x%02X)"));
+ break;
+ case 3: /* hdlc-fcs-BAD */
+ case 5: /* hdlc-fcs-BAD-sig-end */
+ frame_label = g_strdup(pi->Data_Field_field_type_value == 3 ? "fcs-BAD" : "fcs-BAD-sig-end");
+ comment = g_strdup_printf("WARNING: received t38:%s:HDLC:%s", val_to_str(pi->data_value, t38_T30_data_vals, "Ukn (0x%02X)"), pi->Data_Field_field_type_value == 3 ? "fcs-BAD" : "fcs-BAD-sig-end");
+ break;
+ case 7: /* t4-non-ecm-sig-end */
+ duration = nstime_to_sec(&pinfo->fd->rel_ts) - pi->time_first_t4_data;
+ frame_label = g_strdup_printf("t4-non-ecm-data:%s",val_to_str(pi->data_value, t38_T30_data_vals, "Ukn (0x%02X)") );
+ comment = g_strdup_printf("t38:t4-non-ecm-data:%s Duration: %.2fs %s",val_to_str(pi->data_value, t38_T30_data_vals, "Ukn (0x%02X)"), duration, pi->desc_comment );
+ insert_to_graph_t38(tapinfo, pinfo, frame_label, comment, (guint16)conv_num, &(pinfo->src), &(pinfo->dst), line_style, pi->frame_num_first_t4_data);
+ break;
+ }
+ }
+
+ if (frame_label && !(pi->Data_Field_field_type_value == 7 && pi->type_msg == 1)) {
+ add_to_graph(tapinfo, pinfo, frame_label, comment, (guint16)conv_num, &(pinfo->src), &(pinfo->dst), line_style);
+ }
+
+ g_free(comment);
+ g_free(frame_label);
+
+ tapinfo->redraw = TRUE;
+
+ return 1; /* refresh output */
+}
+
+static gboolean have_T38_tap_listener=FALSE;
+/****************************************************************************/
+void
+t38_init_tap(void)
+{
+ GString *error_string;
+
+ if(have_T38_tap_listener==FALSE)
+ {
+ /* don't register tap listener, if we have it already */
+ error_string = register_tap_listener("t38", &(the_tapinfo_struct.t38_dummy), NULL,
+ 0,
+ voip_calls_dlg_reset,
+ T38_packet,
+ voip_calls_dlg_draw
+ );
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+ have_T38_tap_listener=TRUE;
+ }
+}
+
+/****************************************************************************/
+void
+remove_tap_listener_t38(void)
+{
+ protect_thread_critical_region();
+ remove_tap_listener(&(the_tapinfo_struct.t38_dummy));
+ unprotect_thread_critical_region();
+
+ have_T38_tap_listener=FALSE;
+}
+
+
+/****************************************************************************/
+static gchar *sdp_summary = NULL;
+static guint32 sdp_frame_num = 0;
+
+/****************************************************************************/
+/* ***************************TAP for SIP **********************************/
+/****************************************************************************/
+
+
+static void free_sip_info(gpointer p) {
+ sip_calls_info_t *si = p;
+
+ g_free(si->call_identifier);
+ g_free(si);
+}
+
+/****************************************************************************/
+/* whenever a SIP packet is seen by the tap listener */
+static int
+SIPcalls_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *SIPinfo)
+{
+ voip_calls_tapinfo_t *tapinfo = &the_tapinfo_struct;
+ /* we just take note of the ISUP data here; when we receive the MTP3 part everything will
+ be compared with existing calls */
+
+ voip_calls_info_t *tmp_listinfo;
+ voip_calls_info_t *callsinfo = NULL;
+ sip_calls_info_t *tmp_sipinfo = NULL;
+ GList *list;
+ address tmp_src, tmp_dst;
+ gchar *frame_label = NULL;
+ gchar *comment = NULL;
+
+ const sip_info_value_t *pi = SIPinfo;
+
+ /* do not consider packets without call_id */
+ if (pi->tap_call_id ==NULL){
+ return 0;
+ }
+
+ /* check whether we already have a call with these parameters in the list */
+ list = g_list_first(tapinfo->callsinfo_list);
+ while (list)
+ {
+ tmp_listinfo=list->data;
+ if (tmp_listinfo->protocol == VOIP_SIP){
+ tmp_sipinfo = tmp_listinfo->prot_info;
+ if (strcmp(tmp_sipinfo->call_identifier,pi->tap_call_id)==0){
+ callsinfo = (voip_calls_info_t*)(list->data);
+ break;
+ }
+ }
+ list = g_list_next (list);
+ }
+
+ /* not in the list? then create a new entry if the message is INVITE -i.e. if this session is a call*/
+ if ((callsinfo==NULL) &&(pi->request_method!=NULL)){
+ if (strcmp(pi->request_method,"INVITE")==0){
+ callsinfo = g_malloc0(sizeof(voip_calls_info_t));
+ callsinfo->call_active_state = VOIP_ACTIVE;
+ callsinfo->call_state = VOIP_CALL_SETUP;
+ callsinfo->from_identity=g_strdup(pi->tap_from_addr);
+ callsinfo->to_identity=g_strdup(pi->tap_to_addr);
+ COPY_ADDRESS(&(callsinfo->initial_speaker),&(pinfo->src));
+ callsinfo->selected=FALSE;
+ callsinfo->start_fd=pinfo->fd;
+ callsinfo->protocol=VOIP_SIP;
+ callsinfo->prot_info=g_malloc(sizeof(sip_calls_info_t));
+ callsinfo->free_prot_info = free_sip_info;
+ tmp_sipinfo = callsinfo->prot_info;
+ tmp_sipinfo->call_identifier = g_strdup(pi->tap_call_id);
+ tmp_sipinfo->sip_state = SIP_INVITE_SENT;
+ tmp_sipinfo->invite_cseq = pi->tap_cseq_number;
+ callsinfo->npackets = 0;
+ callsinfo->call_num = tapinfo->ncalls++;
+ tapinfo->callsinfo_list = g_list_append(tapinfo->callsinfo_list, callsinfo);
+ }
+ }
+
+ if (callsinfo!=NULL){
+
+ /* let's analyze the call state */
+
+ COPY_ADDRESS(&(tmp_src), &(pinfo->src));
+ COPY_ADDRESS(&(tmp_dst), &(pinfo->dst));
+
+ if (pi->request_method == NULL){
+ frame_label = g_strdup_printf("%u %s", pi->response_code, pi->reason_phrase );
+ comment = g_strdup("SIP Status");
+
+ if ((tmp_sipinfo && pi->tap_cseq_number == tmp_sipinfo->invite_cseq)&&(ADDRESSES_EQUAL(&tmp_dst,&(callsinfo->initial_speaker)))){
+ if ((pi->response_code > 199) && (pi->response_code<300) && (tmp_sipinfo->sip_state == SIP_INVITE_SENT)){
+ tmp_sipinfo->sip_state = SIP_200_REC;
+ }
+ else if ((pi->response_code>299)&&(tmp_sipinfo->sip_state == SIP_INVITE_SENT)){
+ callsinfo->call_state = VOIP_REJECTED;
+ tapinfo->rejected_calls++;
+ }
+ }
+
+ }
+ else{
+ frame_label = g_strdup(pi->request_method);
+
+ if ((strcmp(pi->request_method,"INVITE")==0)&&(ADDRESSES_EQUAL(&tmp_src,&(callsinfo->initial_speaker)))){
+ tmp_sipinfo->invite_cseq = pi->tap_cseq_number;
+ callsinfo->call_state = VOIP_CALL_SETUP;
+ comment = g_strdup_printf("SIP From: %s To:%s", callsinfo->from_identity, callsinfo->to_identity);
+ }
+ else if ((strcmp(pi->request_method,"ACK")==0)&&(pi->tap_cseq_number == tmp_sipinfo->invite_cseq)
+ &&(ADDRESSES_EQUAL(&tmp_src,&(callsinfo->initial_speaker)))&&(tmp_sipinfo->sip_state==SIP_200_REC)
+ &&(callsinfo->call_state == VOIP_CALL_SETUP)){
+ callsinfo->call_state = VOIP_IN_CALL;
+ comment = g_strdup("SIP Request");
+ }
+ else if (strcmp(pi->request_method,"BYE")==0){
+ callsinfo->call_state = VOIP_COMPLETED;
+ tapinfo->completed_calls++;
+ comment = g_strdup("SIP Request");
+ }
+ else if ((strcmp(pi->request_method,"CANCEL")==0)&&(pi->tap_cseq_number == tmp_sipinfo->invite_cseq)
+ &&(ADDRESSES_EQUAL(&tmp_src,&(callsinfo->initial_speaker)))&&(callsinfo->call_state==VOIP_CALL_SETUP)){
+ callsinfo->call_state = VOIP_CANCELLED;
+ tmp_sipinfo->sip_state = SIP_CANCEL_SENT;
+ comment = g_strdup("SIP Request");
+ } else {
+ comment = g_strdup("SIP Request");
+ }
+ }
+
+ callsinfo->stop_fd = pinfo->fd;
+ ++(callsinfo->npackets);
+ /* increment the packets counter of all calls */
+ ++(tapinfo->npackets);
+
+ /* add to the graph */
+ add_to_graph(tapinfo, pinfo, frame_label, comment, callsinfo->call_num, &(pinfo->src), &(pinfo->dst), 1);
+ g_free(comment);
+ g_free(frame_label);
+ g_free((void *)tmp_src.data);
+ g_free((void *)tmp_dst.data);
+
+ /* add SDP info if apply */
+ if ( (sdp_summary != NULL) && (sdp_frame_num == pinfo->fd->num) ){
+ append_to_frame_graph(tapinfo, pinfo->fd->num, sdp_summary, NULL);
+ g_free(sdp_summary);
+ sdp_summary = NULL;
+ }
+ }
+
+ tapinfo->redraw = TRUE;
+
+ return 1; /* refresh output */
+}
+
+
+/****************************************************************************/
+voip_calls_tapinfo_t* voip_calls_get_info(void)
+{
+ return &the_tapinfo_struct;
+}
+
+
+/****************************************************************************/
+/* TAP INTERFACE */
+/****************************************************************************/
+static gboolean have_SIP_tap_listener=FALSE;
+/****************************************************************************/
+void
+sip_calls_init_tap(void)
+{
+ GString *error_string;
+
+ if(have_SIP_tap_listener==FALSE)
+ {
+ /* don't register tap listener, if we have it already */
+ error_string = register_tap_listener("sip", &(the_tapinfo_struct.sip_dummy), NULL,
+ 0,
+ voip_calls_dlg_reset,
+ SIPcalls_packet,
+ voip_calls_dlg_draw
+ );
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+ have_SIP_tap_listener=TRUE;
+ }
+}
+
+/****************************************************************************/
+void
+remove_tap_listener_sip_calls(void)
+{
+ protect_thread_critical_region();
+ remove_tap_listener(&(the_tapinfo_struct.sip_dummy));
+ unprotect_thread_critical_region();
+
+ have_SIP_tap_listener=FALSE;
+}
+
+/****************************************************************************/
+/* ***************************TAP for ISUP **********************************/
+/****************************************************************************/
+
+static guint32 mtp3_opc, mtp3_dpc;
+static guint8 mtp3_ni;
+static guint32 mtp3_frame_num;
+
+
+/****************************************************************************/
+/* whenever a isup_ packet is seen by the tap listener */
+static int
+isup_calls_packet(void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *isup_info _U_)
+{
+ voip_calls_tapinfo_t *tapinfo = &the_tapinfo_struct;
+ voip_calls_info_t *tmp_listinfo;
+ voip_calls_info_t *callsinfo = NULL;
+ isup_calls_info_t *tmp_isupinfo;
+ gboolean found = FALSE;
+ gboolean forward = FALSE;
+ gboolean right_pair;
+ GList *list;
+ gchar *frame_label = NULL;
+ gchar *comment = NULL;
+
+ /*voip_calls_tapinfo_t *tapinfo = &the_tapinfo_struct; unused */
+ const isup_tap_rec_t *pi = isup_info;
+
+ /* check if the lower layer is MTP matching the frame number */
+ if (mtp3_frame_num != pinfo->fd->num) return 0;
+
+ /* check whether we already have a call with these parameters in the list */
+ list = g_list_first(tapinfo->callsinfo_list);
+ while (list)
+ {
+ right_pair = TRUE;
+ tmp_listinfo=list->data;
+ if ((tmp_listinfo->protocol == VOIP_ISUP)&&(tmp_listinfo->call_active_state==VOIP_ACTIVE)){
+ tmp_isupinfo = tmp_listinfo->prot_info;
+ if ((tmp_isupinfo->cic == pinfo->circuit_id)&&(tmp_isupinfo->ni == mtp3_ni)) {
+ if ((tmp_isupinfo->opc == mtp3_opc)&&(tmp_isupinfo->dpc == mtp3_dpc)){
+ forward = TRUE;
+ } else if ((tmp_isupinfo->dpc == mtp3_opc)&&(tmp_isupinfo->opc == mtp3_dpc)){
+ forward = FALSE;
+ } else{
+ right_pair = FALSE;
+ }
+
+ if (right_pair){
+ /* if there is an IAM for a call that is not in setup state, that means the previous call in the same
+ cic is no longer active */
+ if (tmp_listinfo->call_state == VOIP_CALL_SETUP){
+ found = TRUE;
+ } else if (pi->message_type != 1){
+ found = TRUE;
+ } else{
+ tmp_listinfo->call_active_state=VOIP_INACTIVE;
+ }
+ }
+
+ if (found){
+ callsinfo = (voip_calls_info_t*)(list->data);
+ break;
+ }
+ }
+ }
+ list = g_list_next (list);
+ }
+
+ /* not in the list? then create a new entry if the message is IAM
+ -i.e. if this session is a call*/
+
+
+ if ((callsinfo==NULL) &&(pi->message_type==1)){
+ callsinfo = g_malloc0(sizeof(voip_calls_info_t));
+ callsinfo->call_active_state = VOIP_ACTIVE;
+ callsinfo->call_state = VOIP_UNKNOWN;
+ COPY_ADDRESS(&(callsinfo->initial_speaker),&(pinfo->src));
+ callsinfo->selected=FALSE;
+ callsinfo->start_fd=pinfo->fd;
+ callsinfo->protocol=VOIP_ISUP;
+ if (pi->calling_number!=NULL){
+ callsinfo->from_identity=g_strdup(pi->calling_number);
+ }
+ if (pi->called_number!=NULL){
+ callsinfo->to_identity=g_strdup(pi->called_number);
+ }
+ callsinfo->prot_info=g_malloc(sizeof(isup_calls_info_t));
+ callsinfo->free_prot_info = g_free;
+ tmp_isupinfo=callsinfo->prot_info;
+ tmp_isupinfo->opc = mtp3_opc;
+ tmp_isupinfo->dpc = mtp3_dpc;
+ tmp_isupinfo->ni = mtp3_ni;
+ tmp_isupinfo->cic = pinfo->circuit_id;
+ callsinfo->npackets = 0;
+ callsinfo->call_num = tapinfo->ncalls++;
+ tapinfo->callsinfo_list = g_list_append(tapinfo->callsinfo_list, callsinfo);
+ }
+
+
+ if (callsinfo!=NULL){
+ callsinfo->stop_fd = pinfo->fd;
+ ++(callsinfo->npackets);
+
+ /* Let's analyze the call state */
+
+ frame_label = g_strdup(val_to_str_ext_const(pi->message_type, &isup_message_type_value_acro_ext, "Unknown"));
+
+ if (callsinfo->npackets == 1){ /* this is the first packet, that must be an IAM */
+
+ if ((pi->calling_number!=NULL)&&(pi->called_number !=NULL)){
+ comment = g_strdup_printf("Call from %s to %s",
+ pi->calling_number, pi->called_number);
+ }
+ } else if (callsinfo->npackets == 2){ /* in the second packet we show the SPs */
+ if (forward){
+ comment = g_strdup_printf("%i-%i -> %i-%i. Cic:%i",
+ mtp3_ni, mtp3_opc,
+ mtp3_ni, mtp3_dpc, pinfo->circuit_id);
+ } else {
+ comment = g_strdup_printf("%i-%i -> %i-%i. Cic:%i",
+ mtp3_ni, mtp3_dpc,
+ mtp3_ni, mtp3_opc, pinfo->circuit_id);
+ }
+ }
+
+
+ switch(pi->message_type){
+ case 1: /* IAM */
+ callsinfo->call_state=VOIP_CALL_SETUP;
+ break;
+ case 7: /* CONNECT */
+ case 9: /* ANSWER */
+ callsinfo->call_state=VOIP_IN_CALL;
+ break;
+ case 12: /* RELEASE */
+ if (callsinfo->call_state==VOIP_CALL_SETUP){
+ if (forward){
+ callsinfo->call_state=VOIP_CANCELLED;
+ }
+ else{
+ callsinfo->call_state=VOIP_REJECTED;
+ tapinfo->rejected_calls++;
+ }
+ }
+ else if (callsinfo->call_state == VOIP_IN_CALL){
+ callsinfo->call_state = VOIP_COMPLETED;
+ tapinfo->completed_calls++;
+ }
+ comment = g_strdup_printf("Cause %i - %s",
+ pi->cause_value,
+ val_to_str_ext_const(pi->cause_value, &q931_cause_code_vals_ext, "(Unknown)"));
+ break;
+ }
+
+ /* increment the packets counter of all calls */
+ ++(tapinfo->npackets);
+
+ /* add to the graph */
+ add_to_graph(tapinfo, pinfo, frame_label, comment, callsinfo->call_num, &(pinfo->src), &(pinfo->dst), 1);
+ g_free(comment);
+ g_free(frame_label);
+ }
+
+ tapinfo->redraw = TRUE;
+
+ return 1; /* refresh output */
+}
+
+/****************************************************************************/
+
+static gboolean have_isup_tap_listener=FALSE;
+
+void
+isup_calls_init_tap(void)
+{
+ GString *error_string;
+
+
+ if(have_isup_tap_listener==FALSE)
+ {
+ error_string = register_tap_listener("isup", &(the_tapinfo_struct.isup_dummy),
+ NULL,
+ 0,
+ voip_calls_dlg_reset,
+ isup_calls_packet,
+ voip_calls_dlg_draw
+ );
+
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+ have_isup_tap_listener=TRUE;
+ }
+}
+
+/****************************************************************************/
+
+void
+remove_tap_listener_isup_calls(void)
+{
+ protect_thread_critical_region();
+ remove_tap_listener(&(the_tapinfo_struct.isup_dummy));
+ unprotect_thread_critical_region();
+
+ have_isup_tap_listener=FALSE;
+}
+
+
+/****************************************************************************/
+/* ***************************TAP for MTP3 **********************************/
+/****************************************************************************/
+
+
+/****************************************************************************/
+/* whenever a mtp3_ packet is seen by the tap listener */
+static int
+mtp3_calls_packet(void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *mtp3_info _U_)
+{
+ const mtp3_tap_rec_t *pi = mtp3_info;
+
+ /* keep the data in memory to use when the ISUP information arrives */
+
+ mtp3_opc = pi->addr_opc.pc;
+ mtp3_dpc = pi->addr_dpc.pc;
+ mtp3_ni = pi->addr_opc.ni;
+ mtp3_frame_num = pinfo->fd->num;
+
+ return 0;
+}
+
+/****************************************************************************/
+
+static gboolean have_mtp3_tap_listener=FALSE;
+static gboolean have_m3ua_tap_listener=FALSE;
+
+void
+mtp3_calls_init_tap(void)
+{
+ GString *error_string;
+
+
+ if(have_mtp3_tap_listener==FALSE)
+ {
+ error_string = register_tap_listener("mtp3", &(the_tapinfo_struct.mtp3_dummy),
+ NULL,
+ 0,
+ voip_calls_dlg_reset,
+ mtp3_calls_packet,
+ voip_calls_dlg_draw
+ );
+
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+ have_mtp3_tap_listener=TRUE;
+ }
+
+ if(have_m3ua_tap_listener==FALSE)
+ {
+ error_string = register_tap_listener("m3ua", &(the_tapinfo_struct.mtp3_dummy),
+ NULL,
+ 0,
+ voip_calls_dlg_reset,
+ mtp3_calls_packet,
+ voip_calls_dlg_draw
+ );
+
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+ have_m3ua_tap_listener=TRUE;
+ }
+
+}
+
+/****************************************************************************/
+
+void
+remove_tap_listener_mtp3_calls(void)
+{
+ protect_thread_critical_region();
+ remove_tap_listener(&(the_tapinfo_struct.mtp3_dummy));
+ remove_tap_listener(&(the_tapinfo_struct.m3ua_dummy));
+ unprotect_thread_critical_region();
+
+ have_mtp3_tap_listener=FALSE;
+ have_m3ua_tap_listener=FALSE;
+}
+
+/****************************************************************************/
+/* ***************************TAP for Q931 **********************************/
+/****************************************************************************/
+void h245_add_to_graph(guint32 new_frame_num);
+static const e_guid_t guid_allzero = {0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } };
+/* defines specific H323 data */
+
+static gchar *q931_calling_number;
+static gchar *q931_called_number;
+static guint8 q931_cause_value;
+static gint32 q931_crv;
+static guint32 q931_frame_num;
+
+static guint32 h225_frame_num = 0;
+static guint16 h225_call_num = 0;
+static h225_cs_type h225_cstype = H225_OTHER;
+static gboolean h225_is_faststart;
+
+static guint32 actrace_frame_num = 0;
+static gint32 actrace_trunk = 0;
+static gint32 actrace_direction = 0;
+
+
+/****************************************************************************/
+/* whenever a q931_ packet is seen by the tap listener */
+static int
+q931_calls_packet(void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *q931_info)
+{
+ GList *list,*list2;
+ voip_calls_tapinfo_t *tapinfo = &the_tapinfo_struct;
+ h323_calls_info_t *tmp_h323info,*tmp2_h323info;
+ actrace_isdn_calls_info_t *tmp_actrace_isdn_info;
+ voip_calls_info_t *tmp_listinfo;
+ voip_calls_info_t *callsinfo = NULL;
+ h245_address_t *h245_add = NULL;
+ gchar *comment;
+
+ const q931_packet_info *pi = q931_info;
+
+ /* free previously allocated q931_calling/ed_number */
+ g_free(q931_calling_number);
+ g_free(q931_called_number);
+
+ if (pi->calling_number!=NULL)
+ q931_calling_number = g_strdup(pi->calling_number);
+ else
+ q931_calling_number = g_strdup("");
+
+ if (pi->called_number!=NULL)
+ q931_called_number = g_strdup(pi->called_number);
+ else
+ q931_called_number = g_strdup("");
+ q931_cause_value = pi->cause_value;
+ q931_frame_num = pinfo->fd->num;
+ q931_crv = pi->crv;
+
+
+ /* add staff to H323 calls */
+ if (h225_frame_num == q931_frame_num) {
+ tmp_h323info = NULL;
+ list = g_list_first(tapinfo->callsinfo_list);
+ while (list)
+ {
+ tmp_listinfo=list->data;
+ if ( (tmp_listinfo->protocol == VOIP_H323) && (tmp_listinfo->call_num == h225_call_num) ){
+ tmp_h323info = tmp_listinfo->prot_info;
+ callsinfo = (voip_calls_info_t*)(list->data);
+
+ /* Add the CRV to the h323 call */
+ if (tmp_h323info->q931_crv == -1) {
+ tmp_h323info->q931_crv = q931_crv;
+ } else if (tmp_h323info->q931_crv != q931_crv) {
+ tmp_h323info->q931_crv2 = q931_crv;
+ }
+ break;
+ }
+ list = g_list_next (list);
+ }
+
+ if (callsinfo != NULL) {
+ comment = NULL;
+ if (h225_cstype == H225_SETUP) {
+ /* set te calling and called number from the Q931 packet */
+ if (q931_calling_number != NULL){
+ g_free(callsinfo->from_identity);
+ callsinfo->from_identity=g_strdup(q931_calling_number);
+ }
+ if (q931_called_number != NULL){
+ g_free(callsinfo->to_identity);
+ callsinfo->to_identity=g_strdup(q931_called_number);
+ }
+
+ /* check if there is an LRQ/LCF that match this Setup */
+ /* TODO: we are just checking the DialedNumer in LRQ/LCF agains the Setup
+ we should also check if the h225 signaling IP and port match the destination
+ Setup ip and port */
+ list = g_list_first(tapinfo->callsinfo_list);
+ while (list)
+ {
+ tmp_listinfo=list->data;
+ if (tmp_listinfo->protocol == VOIP_H323){
+ tmp2_h323info = tmp_listinfo->prot_info;
+
+ /* check if the called number match a LRQ/LCF */
+ if ( (strcmp(callsinfo->to_identity, tmp_listinfo->to_identity)==0)
+ && (memcmp(&tmp2_h323info->guid, &guid_allzero, GUID_LEN) == 0) ){
+ /* change the call graph to the LRQ/LCF to belong to this call */
+ callsinfo->npackets += change_call_num_graph(tapinfo, tmp_listinfo->call_num, callsinfo->call_num);
+
+ /* remove this LRQ/LCF call entry because we have found the Setup that match them */
+ g_free(tmp_listinfo->from_identity);
+ g_free(tmp_listinfo->to_identity);
+ g_free(tmp2_h323info->guid);
+
+ list2 = g_list_first(tmp2_h323info->h245_list);
+ while (list2)
+ {
+ h245_add=list2->data;
+ g_free((void *)h245_add->h245_address.data);
+ g_free(list2->data);
+ list2 = g_list_next(list2);
+ }
+ g_list_free(tmp_h323info->h245_list);
+ tmp_h323info->h245_list = NULL;
+ g_free(tmp_listinfo->prot_info);
+ tapinfo->callsinfo_list = g_list_remove(tapinfo->callsinfo_list, tmp_listinfo);
+ break;
+ }
+ }
+ list = g_list_next (list);
+ }
+
+ comment = g_strdup_printf("H225 From: %s To:%s TunnH245:%s FS:%s", callsinfo->from_identity, callsinfo->to_identity, (tmp_h323info->is_h245Tunneling==TRUE?"on":"off"),
+ (h225_is_faststart==TRUE?"on":"off"));
+ } else if (h225_cstype == H225_RELEASE_COMPLET) {
+ /* get the Q931 Release cause code */
+ if (q931_cause_value != 0xFF){
+ comment = g_strdup_printf("H225 Q931 Rel Cause (%i):%s", q931_cause_value,
+ val_to_str_ext_const(q931_cause_value, &q931_cause_code_vals_ext, "<unknown>"));
+ } else { /* Cause not set */
+ comment = g_strdup("H225 No Q931 Rel Cause");
+ }
+ }
+ /* change the graph comment for this new one */
+ if (comment != NULL) {
+ change_frame_graph(tapinfo, h225_frame_num, NULL, comment);
+ g_free(comment);
+ }
+ }
+ /* we reset the h225_frame_num to 0 because there could be empty h225 in the same frame
+ as non empty h225 (e.g connect), so we don't have to be here twice */
+ h225_frame_num = 0;
+
+ /* add staff to H245 */
+ } else if (h245_labels.frame_num == q931_frame_num) {
+ /* there are empty H225 frames that don't have guid (guaid=0) but they have h245 info,
+ so the only way to match those frames is with the Q931 CRV number */
+ list = g_list_first(tapinfo->callsinfo_list);
+ while (list)
+ {
+ tmp_listinfo=list->data;
+ if (tmp_listinfo->protocol == VOIP_H323){
+ tmp_h323info = tmp_listinfo->prot_info;
+ if ( ((tmp_h323info->q931_crv == q931_crv) || (tmp_h323info->q931_crv2 == q931_crv)) && (q931_crv!=-1)){
+ /* if the frame number exists in graph, append to it*/
+ if (!append_to_frame_graph(tapinfo, q931_frame_num, NULL, NULL)) {
+ /* if not exist, add to the graph */
+ add_to_graph(tapinfo, pinfo, NULL, NULL, tmp_listinfo->call_num, &(pinfo->src), &(pinfo->dst), 1);
+ ++(tmp_listinfo->npackets);
+ /* increment the packets counter of all calls */
+ ++(tapinfo->npackets);
+ }
+
+ /* Add the H245 info if exists to the Graph */
+ h245_add_to_graph(pinfo->fd->num);
+ break;
+ }
+ }
+ list = g_list_next (list);
+ }
+
+ /* add staff to ACTRACE */
+ } else if (actrace_frame_num == q931_frame_num) {
+ address pstn_add;
+
+ comment = NULL;
+ callsinfo = NULL;
+ list = g_list_first(tapinfo->callsinfo_list);
+ while (list)
+ {
+ tmp_listinfo=list->data;
+ if ( tmp_listinfo->protocol == VOIP_AC_ISDN ){
+ tmp_actrace_isdn_info = tmp_listinfo->prot_info;
+ /* TODO: Also check the IP of the Blade, and if the call is complete (no active) */
+ if ( (tmp_actrace_isdn_info->crv == q931_crv) && (tmp_actrace_isdn_info->trunk == actrace_trunk) ) {
+ callsinfo = (voip_calls_info_t*)(list->data);
+ break;
+ }
+ }
+ list = g_list_next (list);
+ }
+
+ SET_ADDRESS(&pstn_add, AT_STRINGZ, 5, g_strdup("PSTN"));
+
+ /* if it is a new call, add it to the list */
+ if (!callsinfo) {
+ callsinfo = g_malloc0(sizeof(voip_calls_info_t));
+ callsinfo->call_active_state = VOIP_ACTIVE;
+ callsinfo->call_state = VOIP_CALL_SETUP;
+ callsinfo->from_identity=g_strdup(q931_calling_number);
+ callsinfo->to_identity=g_strdup(q931_called_number);
+ COPY_ADDRESS(&(callsinfo->initial_speaker),actrace_direction?&pstn_add:&(pinfo->src));
+ callsinfo->selected=FALSE;
+ callsinfo->start_fd=pinfo->fd;
+ callsinfo->protocol=VOIP_AC_ISDN;
+ callsinfo->prot_info=g_malloc(sizeof(actrace_isdn_calls_info_t));
+ callsinfo->free_prot_info = g_free;
+ tmp_actrace_isdn_info=callsinfo->prot_info;
+ tmp_actrace_isdn_info->crv=q931_crv;
+ tmp_actrace_isdn_info->trunk=actrace_trunk;
+ callsinfo->npackets = 0;
+ callsinfo->call_num = tapinfo->ncalls++;
+ tapinfo->callsinfo_list = g_list_append(tapinfo->callsinfo_list, callsinfo);
+ }
+
+ callsinfo->stop_fd = pinfo->fd;
+ ++(callsinfo->npackets);
+ /* increment the packets counter of all calls */
+ ++(tapinfo->npackets);
+
+ switch(pi->message_type){
+ case Q931_SETUP:
+ comment = g_strdup_printf("AC_ISDN trunk:%u Calling: %s Called:%s", actrace_trunk, q931_calling_number, q931_called_number);
+ callsinfo->call_state=VOIP_CALL_SETUP;
+ break;
+ case Q931_CONNECT:
+ callsinfo->call_state=VOIP_IN_CALL;
+ break;
+ case Q931_RELEASE_COMPLETE:
+ case Q931_RELEASE:
+ case Q931_DISCONNECT:
+ if (callsinfo->call_state==VOIP_CALL_SETUP){
+ if (ADDRESSES_EQUAL(&(callsinfo->initial_speaker), actrace_direction?&pstn_add:&(pinfo->src) )){ /* forward direction */
+ callsinfo->call_state=VOIP_CANCELLED;
+ }
+ else{ /* reverse */
+ callsinfo->call_state=VOIP_REJECTED;
+ tapinfo->rejected_calls++;
+ }
+ } else if ( (callsinfo->call_state!=VOIP_CANCELLED) && (callsinfo->call_state!=VOIP_REJECTED) ){
+ callsinfo->call_state=VOIP_COMPLETED;
+ tapinfo->completed_calls++;
+ }
+ if (q931_cause_value != 0xFF){
+ comment = g_strdup_printf("AC_ISDN trunk:%u Q931 Rel Cause (%i):%s", actrace_trunk, q931_cause_value,
+ val_to_str_ext_const(q931_cause_value, &q931_cause_code_vals_ext, "<unknown>"));
+ } else { /* Cause not set */
+ comment = g_strdup("AC_ISDN No Q931 Rel Cause");
+ }
+ break;
+ }
+
+ if (!comment)
+ comment = g_strdup_printf("AC_ISDN trunk:%u", actrace_trunk );
+
+ add_to_graph(tapinfo, pinfo, val_to_str(pi->message_type, q931_message_type_vals, "<unknown>") , comment, callsinfo->call_num,
+ actrace_direction?&pstn_add:&(pinfo->src),
+ actrace_direction?&(pinfo->src):&pstn_add,
+ 1 );
+
+ g_free(comment);
+ g_free((char *)pstn_add.data);
+ }
+
+ tapinfo->redraw = TRUE;
+
+ return 1; /* refresh output */
+}
+
+/****************************************************************************/
+static gboolean have_q931_tap_listener=FALSE;
+
+void
+q931_calls_init_tap(void)
+{
+ GString *error_string;
+
+
+ if(have_q931_tap_listener==FALSE)
+ {
+ error_string = register_tap_listener("q931", &(the_tapinfo_struct.q931_dummy),
+ NULL,
+ 0,
+ voip_calls_dlg_reset,
+ q931_calls_packet,
+ voip_calls_dlg_draw
+ );
+
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+ have_q931_tap_listener=TRUE;
+ }
+}
+
+/****************************************************************************/
+
+void
+remove_tap_listener_q931_calls(void)
+{
+ protect_thread_critical_region();
+ remove_tap_listener(&(the_tapinfo_struct.q931_dummy));
+ unprotect_thread_critical_region();
+
+ have_q931_tap_listener=FALSE;
+}
+
+/****************************************************************************/
+/****************************TAP for H323 ***********************************/
+/****************************************************************************/
+
+static void add_h245_Address(h323_calls_info_t *h323info, h245_address_t *h245_address)
+{
+ h323info->h245_list = g_list_append(h323info->h245_list, h245_address);
+}
+
+
+static void free_h225_info(gpointer p) {
+ h323_calls_info_t *tmp_h323info = p;
+
+ g_free(tmp_h323info->guid);
+
+ if (tmp_h323info->h245_list) {
+ GList *list2 = g_list_first(tmp_h323info->h245_list);
+ while (list2)
+ {
+ h245_address_t *h245_add=list2->data;
+ g_free((void *)h245_add->h245_address.data);
+ g_free(list2->data);
+ list2 = g_list_next(list2);
+ }
+
+ g_list_free(tmp_h323info->h245_list);
+
+ }
+
+ g_free(p);
+}
+/****************************************************************************/
+/* whenever a H225 packet is seen by the tap listener */
+static int
+H225calls_packet(void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *H225info)
+{
+ voip_calls_tapinfo_t *tapinfo = &the_tapinfo_struct;
+ voip_calls_info_t *tmp_listinfo;
+ voip_calls_info_t *callsinfo = NULL;
+ h323_calls_info_t *tmp_h323info = NULL;
+ gchar *frame_label;
+ gchar *comment;
+ GList *list;
+ h245_address_t *h245_add = NULL;
+
+ const h225_packet_info *pi = H225info;
+
+ /* if not guid and RAS and not LRQ, LCF or LRJ return because did not belong to a call */
+ /* OR, if not guid and is H225 return because doesn't belong to a call */
+ if ((memcmp(&pi->guid, &guid_allzero, GUID_LEN) == 0))
+ if ( ((pi->msg_type == H225_RAS) && ((pi->msg_tag < 18) || (pi->msg_tag > 20))) || (pi->msg_type != H225_RAS) )
+ return 0;
+
+ /* if it is RAS LCF or LRJ*/
+ if ( (pi->msg_type == H225_RAS) && ((pi->msg_tag == 19) || (pi->msg_tag == 20))) {
+ /* if the LCF/LRJ doesn't match to a LRQ, just return */
+ if (!pi->request_available) return 0;
+
+ /* check whether we already have a call with this request SeqNum */
+ list = g_list_first(tapinfo->callsinfo_list);
+ while (list)
+ {
+ tmp_listinfo=list->data;
+ g_assert(tmp_listinfo != NULL);
+ if (tmp_listinfo->protocol == VOIP_H323){
+ tmp_h323info = tmp_listinfo->prot_info;
+ if (tmp_h323info->requestSeqNum == pi->requestSeqNum) {
+ callsinfo = (voip_calls_info_t*)(list->data);
+ break;
+ }
+ }
+ list = g_list_next (list);
+ }
+ } else {
+ /* check whether we already have a call with this guid in the list */
+ list = g_list_first(tapinfo->callsinfo_list);
+ while (list)
+ {
+ tmp_listinfo=list->data;
+ if (tmp_listinfo->protocol == VOIP_H323){
+ tmp_h323info = tmp_listinfo->prot_info;
+ g_assert(tmp_h323info != NULL);
+ if ( (memcmp(tmp_h323info->guid, &guid_allzero, GUID_LEN) != 0) && (memcmp(tmp_h323info->guid, &pi->guid,GUID_LEN)==0) ){
+ callsinfo = (voip_calls_info_t*)(list->data);
+ break;
+ }
+ }
+ list = g_list_next (list);
+ }
+ }
+
+ h225_cstype = pi->cs_type;
+ h225_is_faststart = pi->is_faststart;
+
+ /* not in the list? then create a new entry */
+ if (callsinfo==NULL){
+ callsinfo = g_malloc0(sizeof(voip_calls_info_t));
+ callsinfo->call_active_state = VOIP_ACTIVE;
+ callsinfo->call_state = VOIP_UNKNOWN;
+ callsinfo->from_identity=g_strdup("");
+ callsinfo->to_identity=g_strdup("");
+ COPY_ADDRESS(&(callsinfo->initial_speaker),&(pinfo->src));
+ callsinfo->selected=FALSE;
+ callsinfo->start_fd=pinfo->fd;
+ callsinfo->protocol=VOIP_H323;
+ callsinfo->prot_info=g_malloc(sizeof(h323_calls_info_t));
+ callsinfo->free_prot_info = free_h225_info;
+
+ tmp_h323info = callsinfo->prot_info;
+ g_assert(tmp_h323info != NULL);
+ tmp_h323info->guid = g_memdup(&pi->guid, sizeof pi->guid);
+ tmp_h323info->h225SetupAddr.type = AT_NONE;
+ tmp_h323info->h225SetupAddr.len = 0;
+ tmp_h323info->h245_list = NULL;
+ tmp_h323info->is_faststart_Setup = FALSE;
+ tmp_h323info->is_faststart_Proc = FALSE;
+ tmp_h323info->is_h245Tunneling = FALSE;
+ tmp_h323info->is_h245 = FALSE;
+ tmp_h323info->q931_crv = -1;
+ tmp_h323info->q931_crv2 = -1;
+ tmp_h323info->requestSeqNum = 0;
+ callsinfo->call_num = tapinfo->ncalls++;
+ callsinfo->npackets = 0;
+
+ tapinfo->callsinfo_list = g_list_append(tapinfo->callsinfo_list, callsinfo);
+ }
+
+ if (callsinfo!=NULL){
+
+ h225_frame_num = pinfo->fd->num;
+ h225_call_num = callsinfo->call_num;
+
+ /* let's analyze the call state */
+
+ callsinfo->stop_fd = pinfo->fd;
+ ++(callsinfo->npackets);
+ /* increment the packets counter of all calls */
+ ++(tapinfo->npackets);
+
+
+ /* XXX: it is supposed to be initialized isn't it? */
+ g_assert(tmp_h323info != NULL);
+
+ /* change the status */
+ if (pi->msg_type == H225_CS){
+
+ /* this is still IPv4 only, because the dissector is */
+ if (pi->is_h245 == TRUE){
+ h245_add = g_malloc(sizeof (h245_address_t));
+ h245_add->h245_address.type=AT_IPv4;
+ h245_add->h245_address.len=4;
+ h245_add->h245_address.data = g_malloc(sizeof(pi->h245_address));
+ memcpy((void *)(h245_add->h245_address.data), &(pi->h245_address), 4);
+ h245_add->h245_port = pi->h245_port;
+ add_h245_Address(tmp_h323info, h245_add);
+ }
+
+ if (pi->cs_type != H225_RELEASE_COMPLET) tmp_h323info->is_h245Tunneling = pi->is_h245Tunneling;
+
+ frame_label = g_strdup(pi->frame_label);
+
+ switch(pi->cs_type){
+ case H225_SETUP:
+ tmp_h323info->is_faststart_Setup = pi->is_faststart;
+
+ /* Set the Setup address if it was not set */
+ if (tmp_h323info->h225SetupAddr.type == AT_NONE)
+ COPY_ADDRESS(&(tmp_h323info->h225SetupAddr), &(pinfo->src));
+ callsinfo->call_state=VOIP_CALL_SETUP;
+ comment = g_strdup_printf("H225 TunnH245:%s FS:%s", (tmp_h323info->is_h245Tunneling==TRUE?"on":"off"),
+ (pi->is_faststart==TRUE?"on":"off"));
+ break;
+ case H225_CONNECT:
+ callsinfo->call_state=VOIP_IN_CALL;
+ if (pi->is_faststart == TRUE) tmp_h323info->is_faststart_Proc = TRUE;
+ comment = g_strdup_printf("H225 TunnH245:%s FS:%s", (tmp_h323info->is_h245Tunneling==TRUE?"on":"off"),
+ (pi->is_faststart==TRUE?"on":"off"));
+ break;
+ case H225_RELEASE_COMPLET:
+ if (callsinfo->call_state==VOIP_CALL_SETUP){
+ if (ADDRESSES_EQUAL(&(tmp_h323info->h225SetupAddr),&(pinfo->src))){ /* forward direction */
+ callsinfo->call_state=VOIP_CANCELLED;
+ }
+ else{ /* reverse */
+ callsinfo->call_state=VOIP_REJECTED;
+ tapinfo->rejected_calls++;
+ }
+ } else {
+ callsinfo->call_state=VOIP_COMPLETED;
+ tapinfo->completed_calls++;
+ }
+ comment = g_strdup("H225 No Q931 Rel Cause");
+ break;
+ case H225_PROGRESS:
+ case H225_ALERTING:
+ case H225_CALL_PROCEDING:
+ if (pi->is_faststart == TRUE) tmp_h323info->is_faststart_Proc = TRUE;
+ comment = g_strdup_printf("H225 TunnH245:%s FS:%s", (tmp_h323info->is_h245Tunneling==TRUE?"on":"off"),
+ (pi->is_faststart==TRUE?"on":"off"));
+ break;
+ default:
+ comment = g_strdup_printf("H225 TunnH245:%s FS:%s", (tmp_h323info->is_h245Tunneling==TRUE?"on":"off"),
+ (pi->is_faststart==TRUE?"on":"off"));
+
+ }
+ }
+ else if (pi->msg_type == H225_RAS){
+ switch(pi->msg_tag){
+ case 18: /* LRQ */
+ if (!pi->is_duplicate){
+ g_free(callsinfo->to_identity);
+ callsinfo->to_identity=g_strdup(pi->dialedDigits);
+ tmp_h323info->requestSeqNum = pi->requestSeqNum;
+ }
+ case 19: /* LCF */
+ if (strlen(pi->dialedDigits))
+ comment = g_strdup_printf("H225 RAS dialedDigits: %s", pi->dialedDigits);
+ else
+ comment = g_strdup("H225 RAS");
+ break;
+ default:
+ comment = g_strdup("H225 RAS");
+ }
+ frame_label = g_strdup(val_to_str_const(pi->msg_tag, h225_RasMessage_vals, "<unknown>"));
+ } else {
+ frame_label = g_strdup("H225: Unknown");
+ comment = NULL;
+ }
+
+ /* add to graph analysis */
+
+ /* if the frame number exists in graph, append to it*/
+ if (!append_to_frame_graph(tapinfo, pinfo->fd->num, pi->frame_label, comment)) {
+ /* if not exist, add to the graph */
+ add_to_graph(tapinfo, pinfo, frame_label, comment, callsinfo->call_num, &(pinfo->src), &(pinfo->dst), 1);
+ }
+
+ /* Add the H245 info if exists to the Graph */
+ h245_add_to_graph(pinfo->fd->num);
+
+ g_free(frame_label);
+ g_free(comment);
+ }
+
+ tapinfo->redraw = TRUE;
+
+ return 1; /* refresh output */
+}
+
+
+/****************************************************************************/
+/* TAP INTERFACE */
+/****************************************************************************/
+static gboolean have_H225_tap_listener=FALSE;
+/****************************************************************************/
+void
+h225_calls_init_tap(void)
+{
+ GString *error_string;
+
+ if(have_H225_tap_listener==FALSE)
+ {
+ /* don't register tap listener, if we have it already */
+ error_string = register_tap_listener("h225", &(the_tapinfo_struct.h225_dummy), NULL,
+ 0,
+ voip_calls_dlg_reset,
+ H225calls_packet,
+ voip_calls_dlg_draw
+ );
+
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+ have_H225_tap_listener=TRUE;
+ }
+}
+
+/****************************************************************************/
+void
+remove_tap_listener_h225_calls(void)
+{
+ protect_thread_critical_region();
+ remove_tap_listener(&(the_tapinfo_struct.h225_dummy));
+ unprotect_thread_critical_region();
+
+ have_H225_tap_listener=FALSE;
+}
+
+/* Add the h245 label info to the graph */
+void h245_add_to_graph(guint32 new_frame_num)
+{
+ gint8 n;
+
+ if (new_frame_num != h245_labels.frame_num) return;
+
+ for (n=0; n<h245_labels.labels_count; n++) {
+ append_to_frame_graph(&the_tapinfo_struct, new_frame_num, h245_labels.labels[n].frame_label, h245_labels.labels[n].comment);
+ g_free(h245_labels.labels[n].frame_label);
+ h245_labels.labels[n].frame_label = NULL;
+ g_free(h245_labels.labels[n].comment);
+ h245_labels.labels[n].comment = NULL;
+ }
+ h245_labels.frame_num = 0;
+ h245_labels.labels_count = 0;
+}
+
+/* free the h245_labels if the frame number is different */
+static void h245_free_labels(guint32 new_frame_num)
+{
+ gint8 n;
+
+ if (new_frame_num == h245_labels.frame_num) return;
+
+ for (n=0; n<h245_labels.labels_count; n++) {
+ g_free(h245_labels.labels[n].frame_label);
+ h245_labels.labels[n].frame_label = NULL;
+ g_free(h245_labels.labels[n].comment);
+ h245_labels.labels[n].comment = NULL;
+ }
+ h245_labels.frame_num = 0;
+ h245_labels.labels_count = 0;
+}
+
+/* add the frame_label and comment to h245_labels and free the actual one if it is different frame num */
+static void h245_add_label(guint32 new_frame_num, const gchar *frame_label, const gchar *comment)
+{
+ h245_free_labels(new_frame_num);
+
+ h245_labels.frame_num = new_frame_num;
+ h245_labels.labels[h245_labels.labels_count].frame_label = g_strdup(frame_label);
+ h245_labels.labels[h245_labels.labels_count].comment = g_strdup(comment);
+
+ if (h245_labels.labels_count < (H245_MAX-1))
+ h245_labels.labels_count++;
+
+}
+
+/****************************************************************************/
+/* whenever a H245dg packet is seen by the tap listener (when H245 tunneling is ON) */
+static int
+H245dgcalls_packet(void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *H245info)
+{
+ voip_calls_tapinfo_t *tapinfo = &the_tapinfo_struct;
+ voip_calls_info_t *tmp_listinfo;
+ voip_calls_info_t *callsinfo = NULL;
+ h323_calls_info_t *tmp_h323info;
+ GList *list;
+ GList *list2;
+ h245_address_t *h245_add = NULL;
+
+ const h245_packet_info *pi = H245info;
+
+ /* check if Tunneling is OFF and we have a call with this H245 add */
+ list = g_list_first(tapinfo->callsinfo_list);
+ while (list)
+ {
+ tmp_listinfo=list->data;
+ if (tmp_listinfo->protocol == VOIP_H323){
+ tmp_h323info = tmp_listinfo->prot_info;
+
+ list2 = g_list_first(tmp_h323info->h245_list);
+ while (list2)
+ {
+ h245_add=list2->data;
+ if ( (ADDRESSES_EQUAL(&(h245_add->h245_address),&(pinfo->src)) && (h245_add->h245_port == pinfo->srcport))
+ || (ADDRESSES_EQUAL(&(h245_add->h245_address),&(pinfo->dst)) && (h245_add->h245_port == pinfo->destport)) ){
+ callsinfo = (voip_calls_info_t*)(list->data);
+
+ ++(callsinfo->npackets);
+ /* increment the packets counter of all calls */
+ ++(tapinfo->npackets);
+
+ break;
+ }
+ list2 = g_list_next(list2);
+ }
+ if (callsinfo!=NULL) break;
+ }
+ list = g_list_next(list);
+ }
+
+ /* Tunnel is OFF, and we matched the h245 add so we add it to graph */
+ if (callsinfo!=NULL){
+ ++(callsinfo->npackets);
+ /* increment the packets counter of all calls */
+ ++(tapinfo->npackets);
+ /* if the frame number exists in graph, append to it*/
+ if (!append_to_frame_graph(tapinfo, pinfo->fd->num, pi->frame_label, pi->comment)) {
+ /* if not exist, add to the graph */
+ add_to_graph(tapinfo, pinfo, pi->frame_label, pi->comment, callsinfo->call_num, &(pinfo->src), &(pinfo->dst), 1);
+ }
+ } else {
+ /* Tunnel is ON, so we save the label info to use it into h225 or q931 tap. OR may be
+ tunnel OFF but we did not matched the h245 add, in this case nobady will set this label
+ since the frame_num will not match */
+
+ h245_add_label(pinfo->fd->num, (gchar *) pi->frame_label, (gchar *) pi->comment);
+ }
+
+ tapinfo->redraw = TRUE;
+
+ return 1; /* refresh output */
+}
+
+
+/****************************************************************************/
+/* TAP INTERFACE */
+/****************************************************************************/
+static gboolean have_H245dg_tap_listener=FALSE;
+/****************************************************************************/
+void
+h245dg_calls_init_tap(void)
+{
+ GString *error_string;
+
+ if(have_H245dg_tap_listener==FALSE)
+ {
+ /* don't register tap listener, if we have it already */
+ error_string = register_tap_listener("h245dg", &(the_tapinfo_struct.h245dg_dummy), NULL,
+ 0,
+ voip_calls_dlg_reset,
+ H245dgcalls_packet,
+ voip_calls_dlg_draw
+ );
+
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+ have_H245dg_tap_listener=TRUE;
+ }
+}
+
+/****************************************************************************/
+void
+remove_tap_listener_h245dg_calls(void)
+{
+ protect_thread_critical_region();
+ remove_tap_listener(&(the_tapinfo_struct.h245dg_dummy));
+ unprotect_thread_critical_region();
+
+ have_H245dg_tap_listener=FALSE;
+}
+
+/****************************************************************************/
+/****************************TAP for SDP PROTOCOL ***************************/
+/****************************************************************************/
+/* whenever a SDP packet is seen by the tap listener */
+static int
+SDPcalls_packet(void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *SDPinfo)
+{
+ voip_calls_tapinfo_t *tapinfo = &the_tapinfo_struct;
+ const sdp_packet_info *pi = SDPinfo;
+
+ /* There are protocols like MGCP/SIP where the SDP is called before the tap for the
+ MGCP/SIP packet, in those cases we assign the SPD summary to global lastSDPsummary
+ to use it later
+ */
+ g_free(sdp_summary);
+ sdp_frame_num = pinfo->fd->num;
+ /* Append to graph the SDP summary if the packet exists */
+ sdp_summary = g_strdup_printf("SDP (%s)", pi->summary_str);
+ append_to_frame_graph(tapinfo, pinfo->fd->num, sdp_summary, NULL);
+
+ tapinfo->redraw = TRUE;
+
+ return 1; /* refresh output */
+}
+
+
+/****************************************************************************/
+/* TAP INTERFACE */
+/****************************************************************************/
+static gboolean have_sdp_tap_listener=FALSE;
+/****************************************************************************/
+void
+sdp_calls_init_tap(void)
+{
+ GString *error_string;
+
+ if(have_sdp_tap_listener==FALSE)
+ {
+ /* don't register tap listener, if we have it already */
+ error_string = register_tap_listener("sdp", &(the_tapinfo_struct.sdp_dummy), NULL,
+ 0,
+ voip_calls_dlg_reset,
+ SDPcalls_packet,
+ voip_calls_dlg_draw
+ );
+
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+ have_sdp_tap_listener=TRUE;
+ }
+}
+
+/****************************************************************************/
+void
+remove_tap_listener_sdp_calls(void)
+{
+ protect_thread_critical_region();
+ remove_tap_listener(&(the_tapinfo_struct.sdp_dummy));
+ unprotect_thread_critical_region();
+
+ have_sdp_tap_listener=FALSE;
+}
+
+
+
+/****************************************************************************/
+/* ***************************TAP for MGCP **********************************/
+/****************************************************************************/
+
+/*
+ This function will look for a signal/event in the SignalReq/ObsEvent string
+ and return true if it is found
+*/
+static gboolean isSignal(const gchar *signal_str_p, const gchar *signalStr)
+{
+ gint i;
+ gchar **resultArray;
+
+ /* if there is no signalStr, just return false */
+ if (signalStr == NULL) return FALSE;
+
+ /* if are both "blank" return true */
+ if ( (*signal_str_p == '\0') && (*signalStr == '\0') ) return TRUE;
+
+ /* look for signal in signalStr */
+ resultArray = g_strsplit(signalStr, ",", 10);
+
+ for (i = 0; resultArray[i]; i++) {
+ g_strstrip(resultArray[i]);
+ if (strcmp(resultArray[i], signal_str_p) == 0) return TRUE;
+ }
+
+ g_strfreev(resultArray);
+
+ return FALSE;
+}
+
+/*
+ This function will get the Caller ID info and replace the current string
+ This is how it looks the caller Id: rg, ci(02/16/08/29, "3035550002","Ale Sipura 2")
+*/
+static void mgcpCallerID(gchar *signalStr, gchar **callerId)
+{
+ gchar **arrayStr;
+
+ /* if there is no signalStr, just return false */
+ if (signalStr == NULL) return;
+
+ arrayStr = g_strsplit(signalStr, "\"", 10);
+
+ if (arrayStr[0] == NULL) return;
+
+ /* look for the ci signal */
+ if (strstr(arrayStr[0], "ci(") && (arrayStr[1] != NULL) ) {
+ /* free the previous "From" field of the call, and assign the new */
+ g_free(*callerId);
+ *callerId = g_strdup(arrayStr[1]);
+ }
+ g_strfreev(arrayStr);
+
+ return;
+}
+
+
+/*
+ This function will get the Dialed Digits and replace the current string
+ This is how it looks the dialed digits 5,5,5,0,0,0,2,#,*
+*/
+static void mgcpDialedDigits(gchar *signalStr, gchar **dialedDigits)
+{
+ gchar *tmpStr;
+ gchar resultStr[50];
+ gint i,j;
+
+ /* if there is no signalStr, just return false */
+ if (signalStr == NULL) return;
+
+ tmpStr = g_strdup(signalStr);
+
+ for ( i = 0 ; tmpStr[i] ; i++) {
+ switch (tmpStr[i]) {
+ case '0' : case '1' : case '2' : case '3' : case '4' :
+ case '5' : case '6' : case '7' : case '8' : case '9' :
+ case '#' : case '*' :
+ break;
+ default:
+ tmpStr[i] = '?';
+ break;
+ }
+ }
+
+ for (i = 0, j = 0; tmpStr[i] && i<50; i++) {
+ if (tmpStr[i] != '?')
+ resultStr[j++] = tmpStr[i];
+ }
+ resultStr[j] = '\0';
+
+ if (*resultStr == '\0') return;
+
+ g_free(*dialedDigits);
+ *dialedDigits = g_strdup(resultStr);
+ g_free(tmpStr);
+
+ return;
+}
+
+
+
+/****************************************************************************/
+/* whenever a MGCP packet is seen by the tap listener */
+static int
+MGCPcalls_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *MGCPinfo)
+{
+ voip_calls_tapinfo_t *tapinfo = &the_tapinfo_struct;
+
+ voip_calls_info_t *tmp_listinfo;
+ voip_calls_info_t *callsinfo = NULL;
+ mgcp_calls_info_t *tmp_mgcpinfo = NULL;
+ GList *list;
+ GList *listGraph;
+ gchar *frame_label = NULL;
+ gchar *comment = NULL;
+ graph_analysis_item_t *gai;
+ gboolean new = FALSE;
+ gboolean fromEndpoint = FALSE; /* true for calls originated in Endpoints, false for calls from MGC */
+ gdouble diff_time;
+
+ const mgcp_info_t *pi = MGCPinfo;
+
+
+ if ((pi->mgcp_type == MGCP_REQUEST) && !pi->is_duplicate ){
+ /* check whether we already have a call with this Endpoint and it is active*/
+ list = g_list_first(tapinfo->callsinfo_list);
+ while (list)
+ {
+ tmp_listinfo=list->data;
+ if ((tmp_listinfo->protocol == VOIP_MGCP) && (tmp_listinfo->call_active_state == VOIP_ACTIVE)){
+ tmp_mgcpinfo = tmp_listinfo->prot_info;
+ if (pi->endpointId != NULL){
+ if (g_ascii_strcasecmp(tmp_mgcpinfo->endpointId,pi->endpointId) == 0){
+ /*
+ check first if it is an ended call. We can still match packets to this Endpoint 2 seconds
+ after the call has been released
+ */
+ diff_time = nstime_to_sec(&pinfo->fd->rel_ts) - nstime_to_sec(&tmp_listinfo->stop_fd->rel_ts);
+ if ( ((tmp_listinfo->call_state == VOIP_CANCELLED) ||
+ (tmp_listinfo->call_state == VOIP_COMPLETED) ||
+ (tmp_listinfo->call_state == VOIP_REJECTED)) &&
+ (diff_time > 2) )
+ {
+ tmp_listinfo->call_active_state = VOIP_INACTIVE;
+ } else {
+ callsinfo = (voip_calls_info_t*)(list->data);
+ break;
+ }
+ }
+ }
+ }
+ list = g_list_next (list);
+ }
+
+ /* there is no call with this Endpoint, lets see if this a new call or not */
+ if (callsinfo == NULL){
+ if ( (strcmp(pi->code, "NTFY") == 0) && isSignal("hd", pi->observedEvents) ){ /* off hook transition */
+ /* this is a new call from the Endpoint */
+ fromEndpoint = TRUE;
+ new = TRUE;
+ } else if (strcmp(pi->code, "CRCX") == 0){
+ /* this is a new call from the MGC */
+ fromEndpoint = FALSE;
+ new = TRUE;
+ }
+ if (!new) return 0;
+ }
+ } else if ( ((pi->mgcp_type == MGCP_RESPONSE) && pi->request_available) ||
+ ((pi->mgcp_type == MGCP_REQUEST) && pi->is_duplicate) ) {
+ /* if it is a response OR if it is a duplicated Request, lets look in the Graph to see
+ if there is a request that matches */
+ listGraph = g_list_first(tapinfo->graph_analysis->list);
+ while (listGraph)
+ {
+ gai = listGraph->data;
+ if (gai->fd->num == pi->req_num){
+ /* there is a request that match, so look the associated call with this call_num */
+ list = g_list_first(tapinfo->callsinfo_list);
+ while (list)
+ {
+ tmp_listinfo=list->data;
+ if (tmp_listinfo->protocol == VOIP_MGCP){
+ if (tmp_listinfo->call_num == gai->conv_num){
+ tmp_mgcpinfo = tmp_listinfo->prot_info;
+ callsinfo = (voip_calls_info_t*)(list->data);
+ break;
+ }
+ }
+ list = g_list_next (list);
+ }
+ if (callsinfo != NULL) break;
+ }
+ listGraph = g_list_next(listGraph);
+ }
+ /* if there is not a matching request, just return */
+ if (callsinfo == NULL) return 0;
+ } else return 0;
+
+ /* not in the list? then create a new entry */
+ if (callsinfo==NULL){
+ callsinfo = g_malloc0(sizeof(voip_calls_info_t));
+ callsinfo->call_active_state = VOIP_ACTIVE;
+ callsinfo->call_state = VOIP_CALL_SETUP;
+ if (fromEndpoint) {
+ callsinfo->from_identity=g_strdup(pi->endpointId);
+ callsinfo->to_identity=g_strdup("");
+ } else {
+ callsinfo->from_identity=g_strdup("");
+ callsinfo->to_identity=g_strdup(pi->endpointId);
+ }
+ COPY_ADDRESS(&(callsinfo->initial_speaker),&(pinfo->src));
+ callsinfo->selected=FALSE;
+ callsinfo->start_fd=pinfo->fd;
+ callsinfo->protocol=VOIP_MGCP;
+ callsinfo->prot_info=g_malloc(sizeof(mgcp_calls_info_t));
+ callsinfo->free_prot_info = g_free;
+ tmp_mgcpinfo=callsinfo->prot_info;
+ tmp_mgcpinfo->endpointId = g_strdup(pi->endpointId);
+ tmp_mgcpinfo->fromEndpoint = fromEndpoint;
+ callsinfo->npackets = 0;
+ callsinfo->call_num = tapinfo->ncalls++;
+ tapinfo->callsinfo_list = g_list_append(tapinfo->callsinfo_list, callsinfo);
+ }
+
+ g_assert(tmp_mgcpinfo != NULL);
+
+ /* change call state and add to graph */
+ switch (pi->mgcp_type)
+ {
+ case MGCP_REQUEST:
+ if ( (strcmp(pi->code, "NTFY") == 0) && (pi->observedEvents != NULL) ){
+ frame_label = g_strdup_printf("%s ObsEvt:%s",pi->code, pi->observedEvents);
+
+ if (tmp_mgcpinfo->fromEndpoint){
+ /* use the Dialed digits to fill the "To" for the call, but use the first NTFY */
+ if (callsinfo->to_identity[0] == '\0') mgcpDialedDigits(pi->observedEvents, &(callsinfo->to_identity));
+
+ /* from MGC and the user picked up, the call is connected */
+ } else if (isSignal("hd", pi->observedEvents))
+ callsinfo->call_state=VOIP_IN_CALL;
+
+ /* hung up signal */
+ if (isSignal("hu", pi->observedEvents)) {
+ if ((callsinfo->call_state == VOIP_CALL_SETUP) || (callsinfo->call_state == VOIP_RINGING)){
+ callsinfo->call_state = VOIP_CANCELLED;
+ } else {
+ callsinfo->call_state = VOIP_COMPLETED;
+ }
+ }
+
+ } else if (strcmp(pi->code, "RQNT") == 0) {
+ /* for calls from Endpoint: if there is a "no signal" RQNT and the call was RINGING, we assume this is the CONNECT */
+ if ( tmp_mgcpinfo->fromEndpoint && isSignal("", pi->signalReq) && (callsinfo->call_state == VOIP_RINGING) ) {
+ callsinfo->call_state = VOIP_IN_CALL;
+ }
+
+ /* if there is ringback or ring tone, change state to ringing */
+ if ( isSignal("rg", pi->signalReq) || isSignal("rt", pi->signalReq) ) {
+ callsinfo->call_state = VOIP_RINGING;
+ }
+
+ /* if there is a Busy or ReorderTone, and the call was Ringing or Setup the call is Rejected */
+ if ( (isSignal("ro", pi->signalReq) || isSignal("bz", pi->signalReq)) && ((callsinfo->call_state == VOIP_CALL_SETUP) || (callsinfo->call_state == VOIP_RINGING)) ) {
+ callsinfo->call_state = VOIP_REJECTED;
+ }
+
+ if (pi->signalReq != NULL)
+ frame_label = g_strdup_printf("%s%sSigReq:%s",pi->code, (pi->hasDigitMap == TRUE)?" DigitMap ":"", pi->signalReq);
+ else
+ frame_label = g_strdup_printf("%s%s",pi->code, (pi->hasDigitMap == TRUE)?" DigitMap ":"");
+
+ /* use the CallerID info to fill the "From" for the call */
+ if (!tmp_mgcpinfo->fromEndpoint) mgcpCallerID(pi->signalReq, &(callsinfo->from_identity));
+
+ } else if (strcmp(pi->code, "DLCX") == 0) {
+ /*
+ if there is a DLCX in a call To an Endpoint and the call was not connected, we use
+ the DLCX as the end of the call
+ */
+ if (!tmp_mgcpinfo->fromEndpoint){
+ if ((callsinfo->call_state == VOIP_CALL_SETUP) || (callsinfo->call_state == VOIP_RINGING)){
+ callsinfo->call_state = VOIP_CANCELLED;
+ }
+ }
+ }
+
+ if (frame_label == NULL) frame_label = g_strdup(pi->code);
+ break;
+ case MGCP_RESPONSE:
+ frame_label = g_strdup_printf("%u (%s)",pi->rspcode, pi->code);
+ break;
+ case MGCP_OTHERS:
+ /* XXX what to do? */
+ break;
+ }
+
+
+ comment = g_strdup_printf("MGCP %s %s%s", tmp_mgcpinfo->endpointId, (pi->mgcp_type == MGCP_REQUEST)?"Request":"Response", pi->is_duplicate?" Duplicate":"");
+
+ callsinfo->stop_fd = pinfo->fd;
+ ++(callsinfo->npackets);
+ /* increment the packets counter of all calls */
+ ++(tapinfo->npackets);
+
+ /* add to the graph */
+ add_to_graph(tapinfo, pinfo, frame_label, comment, callsinfo->call_num, &(pinfo->src), &(pinfo->dst), 1);
+ g_free(comment);
+ g_free(frame_label);
+
+ /* add SDP info if apply */
+ if ( (sdp_summary != NULL) && (sdp_frame_num == pinfo->fd->num) ){
+ append_to_frame_graph(tapinfo, pinfo->fd->num, sdp_summary, NULL);
+ g_free(sdp_summary);
+ sdp_summary = NULL;
+ }
+
+ tapinfo->redraw = TRUE;
+
+ return 1; /* refresh output */
+}
+
+
+/****************************************************************************/
+/* TAP INTERFACE */
+/****************************************************************************/
+static gboolean have_MGCP_tap_listener=FALSE;
+/****************************************************************************/
+void
+mgcp_calls_init_tap(void)
+{
+ GString *error_string;
+
+ if(have_MGCP_tap_listener==FALSE)
+ {
+ /*
+ * Don't register the tap listener if we have it already.
+ * We set TL_REQUIRES_PROTO_TREE to force a non-null "tree"
+ * in the MGCP dissector; otherwise, the dissector
+ * doesn't fill in the info passed to the tap's packet
+ * routine.
+ */
+ error_string = register_tap_listener("mgcp",
+ &(the_tapinfo_struct.mgcp_dummy),
+ NULL,
+ TL_REQUIRES_PROTO_TREE,
+ voip_calls_dlg_reset,
+ MGCPcalls_packet,
+ voip_calls_dlg_draw
+ );
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+ have_MGCP_tap_listener=TRUE;
+ }
+}
+
+/****************************************************************************/
+void
+remove_tap_listener_mgcp_calls(void)
+{
+ protect_thread_critical_region();
+ remove_tap_listener(&(the_tapinfo_struct.mgcp_dummy));
+ unprotect_thread_critical_region();
+
+ have_MGCP_tap_listener=FALSE;
+}
+
+
+/****************************************************************************/
+/****************************TAP for ACTRACE (AudioCodes trace)**************/
+/****************************************************************************/
+
+/* whenever a ACTRACE packet is seen by the tap listener */
+static int
+ACTRACEcalls_packet(void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *ACTRACEinfo)
+{
+ voip_calls_tapinfo_t *tapinfo = &the_tapinfo_struct;
+ const actrace_info_t *pi = ACTRACEinfo;
+ GList *list;
+ actrace_cas_calls_info_t *tmp_actrace_cas_info;
+ voip_calls_info_t *tmp_listinfo;
+ voip_calls_info_t *callsinfo = NULL;
+
+
+ actrace_frame_num = pinfo->fd->num;
+ actrace_trunk = pi->trunk;
+ actrace_direction = pi->direction;
+
+ if (pi->type == 1){ /* is CAS protocol */
+ address pstn_add;
+ gchar *comment = NULL;
+
+ callsinfo = NULL;
+ list = g_list_first(tapinfo->callsinfo_list);
+ while (list)
+ {
+ tmp_listinfo=list->data;
+ if ( tmp_listinfo->protocol == VOIP_AC_CAS ){
+ tmp_actrace_cas_info = tmp_listinfo->prot_info;
+ /* TODO: Also check the IP of the Blade, and if the call is complete (no active) */
+ if ( (tmp_actrace_cas_info->bchannel == pi->cas_bchannel) && (tmp_actrace_cas_info->trunk == actrace_trunk) ) {
+ callsinfo = (voip_calls_info_t*)(list->data);
+ break;
+ }
+ }
+ list = g_list_next (list);
+ }
+
+ SET_ADDRESS(&pstn_add, AT_STRINGZ, 5, "PSTN");
+
+ /* if it is a new call, add it to the list */
+ if (!callsinfo) {
+ callsinfo = g_malloc0(sizeof(voip_calls_info_t));
+ callsinfo->call_active_state = VOIP_ACTIVE;
+ callsinfo->call_state = VOIP_CALL_SETUP;
+ callsinfo->from_identity=g_strdup("N/A");
+ callsinfo->to_identity=g_strdup("N/A");
+ COPY_ADDRESS(&(callsinfo->initial_speaker),actrace_direction?&pstn_add:&(pinfo->src));
+ callsinfo->selected=FALSE;
+ callsinfo->start_fd=pinfo->fd;
+ callsinfo->protocol=VOIP_AC_CAS;
+ callsinfo->prot_info=g_malloc(sizeof(actrace_cas_calls_info_t));
+ callsinfo->free_prot_info = g_free;
+
+ tmp_actrace_cas_info=callsinfo->prot_info;
+ tmp_actrace_cas_info->bchannel=pi->cas_bchannel;
+ tmp_actrace_cas_info->trunk=actrace_trunk;
+ callsinfo->npackets = 0;
+ callsinfo->call_num = tapinfo->ncalls++;
+ tapinfo->callsinfo_list = g_list_append(tapinfo->callsinfo_list, callsinfo);
+ }
+
+ callsinfo->stop_fd = pinfo->fd;
+ ++(callsinfo->npackets);
+ /* increment the packets counter of all calls */
+ ++(tapinfo->npackets);
+
+ comment = g_strdup_printf("AC_CAS trunk:%u", actrace_trunk);
+
+ add_to_graph(tapinfo, pinfo, pi->cas_frame_label, comment, callsinfo->call_num,
+ actrace_direction?&pstn_add:&(pinfo->src),
+ actrace_direction?&(pinfo->src):&pstn_add,
+ 1 );
+
+ g_free(comment);
+ }
+
+ tapinfo->redraw = TRUE;
+
+ return 1; /* refresh output */
+}
+
+
+/****************************************************************************/
+/* TAP INTERFACE */
+/****************************************************************************/
+static gboolean have_actrace_tap_listener=FALSE;
+/****************************************************************************/
+void
+actrace_calls_init_tap(void)
+{
+ GString *error_string;
+
+ if(have_actrace_tap_listener==FALSE)
+ {
+ /* don't register tap listener, if we have it already */
+ error_string = register_tap_listener("actrace", &(the_tapinfo_struct.actrace_dummy), NULL,
+ 0,
+ voip_calls_dlg_reset,
+ ACTRACEcalls_packet,
+ voip_calls_dlg_draw
+ );
+
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+ have_actrace_tap_listener=TRUE;
+ }
+}
+
+/****************************************************************************/
+void
+remove_tap_listener_actrace_calls(void)
+{
+ protect_thread_critical_region();
+ remove_tap_listener(&(the_tapinfo_struct.actrace_dummy));
+ unprotect_thread_critical_region();
+
+ have_actrace_tap_listener=FALSE;
+}
+
+
+/****************************************************************************/
+/**************************** TAP for H248/MEGACO **********************************/
+/****************************************************************************/
+static gboolean have_h248_tap_listener = FALSE;
+static gboolean have_megaco_tap_listener = FALSE;
+
+#define gcp_is_req(type) ( type == GCP_CMD_ADD_REQ || type == GCP_CMD_MOVE_REQ || type == GCP_CMD_MOD_REQ || \
+ type == GCP_CMD_SUB_REQ || type == GCP_CMD_AUDITCAP_REQ || type == GCP_CMD_AUDITVAL_REQ || \
+ type == GCP_CMD_NOTIFY_REQ || type == GCP_CMD_SVCCHG_REQ || type == GCP_CMD_TOPOLOGY_REQ || \
+ type == GCP_CMD_CTX_ATTR_AUDIT_REQ )
+
+static int h248_calls_packet(void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *prot_info) {
+ voip_calls_tapinfo_t *tapinfo = &the_tapinfo_struct;
+ const gcp_cmd_t *cmd = prot_info;
+ GList *list;
+ voip_calls_info_t *callsinfo = NULL;
+ address *mgw;
+ address *mgc;
+ gchar mgw_addr[128];
+
+ if (cmd->ctx->id == NULL_CONTEXT || cmd->ctx->id == ALL_CONTEXTS ) {
+ return 0;
+ }
+
+ if ( gcp_is_req(cmd->type) ) {
+ mgw = &(pinfo->dst);
+ mgc = &(pinfo->src);
+ } else {
+ mgc = &(pinfo->dst);
+ mgw = &(pinfo->src);
+ }
+
+ address_to_str_buf(mgw, mgw_addr, 128);
+
+ /* check whether we already have this context in the list */
+ list = g_list_first(tapinfo->callsinfo_list);
+ while (list)
+ {
+ voip_calls_info_t* tmp_listinfo = list->data;
+
+ if (tmp_listinfo->protocol == TEL_H248){
+ if (tmp_listinfo->prot_info == cmd->ctx){
+ callsinfo = (voip_calls_info_t*)(list->data);
+ break;
+ }
+ }
+ list = g_list_next (list);
+ }
+
+ if (callsinfo==NULL){
+
+ callsinfo = g_malloc0(sizeof(voip_calls_info_t));
+ callsinfo->call_state = VOIP_NO_STATE;
+ callsinfo->call_active_state = VOIP_ACTIVE;
+ callsinfo->from_identity = g_strdup_printf("%s : %.8x", mgw_addr, cmd->ctx->id);
+ callsinfo->to_identity = g_strdup("");
+ callsinfo->prot_info = cmd->ctx;
+ callsinfo->free_prot_info = NULL;
+
+ callsinfo->npackets = 1;
+
+ COPY_ADDRESS(&(callsinfo->initial_speaker), mgc);
+
+ callsinfo->protocol = TEL_H248;
+ callsinfo->call_num = tapinfo->ncalls++;
+ callsinfo->start_fd = pinfo->fd;
+ callsinfo->stop_fd = pinfo->fd;
+
+ callsinfo->selected = FALSE;
+
+ tapinfo->callsinfo_list = g_list_append(tapinfo->callsinfo_list, callsinfo);
+
+ } else {
+ GString *s = g_string_new("");
+ gcp_terms_t *ctx_term;
+
+ g_free(callsinfo->from_identity);
+ callsinfo->from_identity = g_strdup_printf("%s : %.8x", mgw_addr, ((gcp_ctx_t*)callsinfo->prot_info)->id);
+
+ g_free(callsinfo->to_identity);
+
+ for (ctx_term = ((gcp_ctx_t*)callsinfo->prot_info)->terms.next;
+ ctx_term;
+ ctx_term = ctx_term->next ) {
+ if ( ctx_term->term && ctx_term->term->str) {
+ g_string_append_printf(s," %s",ctx_term->term->str);
+ }
+ }
+
+ callsinfo->to_identity = s->str;
+ g_string_free(s,FALSE);
+
+ callsinfo->stop_fd = pinfo->fd;
+ ++(callsinfo->npackets);
+ }
+
+ add_to_graph(tapinfo, pinfo, cmd->str ? cmd->str : "unknown Msg",
+ ep_strdup_printf("TrxId = %u, CtxId = %.8x",cmd->trx->id,cmd->ctx->id),
+ callsinfo->call_num, &(pinfo->src), &(pinfo->dst), 1);
+
+ ++(tapinfo->npackets);
+
+ tapinfo->redraw = TRUE;
+
+ return 1;
+}
+
+void h248_calls_init_tap(void)
+{
+ GString *error_string;
+
+
+ if(have_megaco_tap_listener==FALSE)
+ {
+ error_string = register_tap_listener("megaco", &(the_tapinfo_struct.megaco_dummy),
+ NULL,
+ 0,
+ voip_calls_dlg_reset,
+ h248_calls_packet,
+ voip_calls_dlg_draw);
+
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+
+ have_megaco_tap_listener=TRUE;
+ }
+
+ if(have_h248_tap_listener==FALSE)
+ {
+ error_string = register_tap_listener("h248", &(the_tapinfo_struct.h248_dummy),
+ NULL,
+ 0,
+ voip_calls_dlg_reset,
+ h248_calls_packet,
+ voip_calls_dlg_draw);
+
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+
+ have_h248_tap_listener=TRUE;
+ }
+}
+
+void
+remove_tap_listener_h248_calls(void)
+{
+ protect_thread_critical_region();
+ remove_tap_listener(&(the_tapinfo_struct.h248_dummy));
+ remove_tap_listener(&(the_tapinfo_struct.megaco_dummy));
+ unprotect_thread_critical_region();
+
+ have_megaco_tap_listener=FALSE;
+ have_h248_tap_listener=FALSE;
+}
+
+/****************************************************************************/
+/**************************** TAP for SCCP and SUA **********************************/
+/**************************** ( RANAP and BSSAP ) **********************************/
+/****************************************************************************/
+
+static gboolean have_sccp_tap_listener = FALSE;
+static gboolean have_sua_tap_listener = FALSE;
+
+static const voip_protocol sccp_proto_map[] = {
+ TEL_SCCP,
+ TEL_BSSMAP,
+ TEL_RANAP
+};
+#define SP2VP(ap) ((ap) < SCCP_PLOAD_NUM_PLOADS ? sccp_proto_map[(ap)] : TEL_SCCP)
+const value_string* sccp_payload_values;
+
+static int sccp_calls(packet_info *pinfo, const void *prot_info) {
+ voip_calls_tapinfo_t *tapinfo = &the_tapinfo_struct;
+ const sccp_msg_info_t* msg = prot_info;
+ sccp_assoc_info_t* assoc = msg->data.co.assoc;
+ GList *list;
+ voip_calls_info_t *callsinfo = NULL;
+ const gchar *label = NULL;
+ const gchar *comment = NULL;
+ /* check whether we already have this assoc in the list */
+
+ for(list = g_list_first(tapinfo->callsinfo_list) ; list ; list = g_list_next (list) ) {
+ if ( ((voip_calls_info_t*)(list->data))->prot_info == assoc ){
+ callsinfo = (voip_calls_info_t*)(list->data);
+ break;
+ }
+ }
+
+ if (callsinfo==NULL){
+ callsinfo = g_malloc0(sizeof(voip_calls_info_t));
+ callsinfo->call_state = VOIP_CALL_SETUP;
+ callsinfo->call_active_state = VOIP_ACTIVE;
+ if ( assoc->calling_party ) {
+ callsinfo->from_identity = g_strdup(assoc->calling_party);
+ } else {
+ callsinfo->from_identity = g_strdup("Unknown");
+ }
+
+ if ( assoc->called_party ) {
+ callsinfo->to_identity = g_strdup(assoc->called_party);
+ } else {
+ callsinfo->to_identity = g_strdup("Unknown");
+ }
+
+ callsinfo->prot_info = (void*)assoc;
+ callsinfo->free_prot_info = NULL;
+
+ callsinfo->npackets = 1;
+
+ COPY_ADDRESS(&(callsinfo->initial_speaker), &(pinfo->src));
+
+ callsinfo->protocol = SP2VP(assoc->payload);
+ /* Store frame data which holds time and frame number */
+ callsinfo->start_fd = pinfo->fd;
+ callsinfo->stop_fd = pinfo->fd;
+
+ callsinfo->selected = FALSE;
+ callsinfo->call_num = tapinfo->ncalls++;
+
+ tapinfo->callsinfo_list = g_list_append(tapinfo->callsinfo_list, callsinfo);
+ } else {
+
+ if ( assoc->calling_party ) {
+ g_free(callsinfo->from_identity);
+ callsinfo->from_identity = g_strdup(assoc->calling_party);
+ }
+
+ if ( assoc->called_party ) {
+ g_free(callsinfo->to_identity);
+ callsinfo->to_identity = g_strdup(assoc->called_party);
+ }
+
+ callsinfo->protocol = SP2VP(assoc->payload);
+ /* Store frame data which holds stop time and frame number */
+ callsinfo->stop_fd = pinfo->fd;
+ ++(callsinfo->npackets);
+
+ switch (msg->type) {
+ case SCCP_MSG_TYPE_CC:
+ callsinfo->call_state = VOIP_IN_CALL;
+ break;
+ case SCCP_MSG_TYPE_RLC:
+ callsinfo->call_state = VOIP_COMPLETED;
+ callsinfo->call_active_state = VOIP_INACTIVE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (msg->data.co.label) {
+ label = msg->data.co.label;
+ } else {
+ label = val_to_str(msg->type, sccp_payload_values, "Unknown(%d)");
+ }
+
+ if (msg->data.co.comment) {
+ comment = msg->data.co.comment;
+ } else {
+ comment = NULL;
+ }
+
+ add_to_graph(tapinfo, pinfo, label, comment, callsinfo->call_num, &(pinfo->src), &(pinfo->dst), 1);
+
+ ++(tapinfo->npackets);
+
+ tapinfo->redraw = TRUE;
+
+ return 1;
+}
+
+static int sccp_calls_packet(void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *prot_info) {
+ sccp_payload_values = sccp_message_type_acro_values;
+ return sccp_calls(pinfo, prot_info);
+}
+
+
+static int sua_calls_packet(void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *prot_info) {
+ sccp_payload_values = sua_co_class_type_acro_values;
+ return sccp_calls(pinfo, prot_info);
+}
+
+
+void sccp_calls_init_tap(void)
+{
+ GString *error_string;
+
+ if(have_sccp_tap_listener==FALSE)
+ {
+ error_string = register_tap_listener("sccp", &(the_tapinfo_struct.sccp_dummy),
+ NULL,
+ 0,
+ voip_calls_dlg_reset,
+ sccp_calls_packet,
+ voip_calls_dlg_draw);
+
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+
+ have_sccp_tap_listener=TRUE;
+ }
+
+ if(have_sua_tap_listener==FALSE)
+ {
+ error_string = register_tap_listener("sua", &(the_tapinfo_struct.sua_dummy),
+ NULL,
+ 0,
+ voip_calls_dlg_reset,
+ sua_calls_packet,
+ voip_calls_dlg_draw);
+
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+
+ have_sua_tap_listener=TRUE;
+ }
+
+}
+
+void
+remove_tap_listener_sccp_calls(void)
+{
+ protect_thread_critical_region();
+ remove_tap_listener(&(the_tapinfo_struct.sccp_dummy));
+ unprotect_thread_critical_region();
+
+ have_sccp_tap_listener=FALSE;
+ have_sua_tap_listener=FALSE;
+}
+
+
+/****************************************************************************/
+/****************************TAP for UNISTIM ********************************/
+/****************************************************************************/
+
+static int
+unistim_calls_packet(void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *unistim_info)
+{
+ voip_calls_tapinfo_t *tapinfo = &the_tapinfo_struct;
+ voip_calls_info_t *tmp_listinfo;
+ voip_calls_info_t *callsinfo = NULL;
+ unistim_info_t *tmp_unistim_info = NULL;
+ GList *list = NULL;
+ GString *g_tmp = NULL;
+ gchar *frame_label = NULL;
+ gchar *comment = NULL;
+
+ /* Fetch specific packet infos */
+ const unistim_info_t *pi = unistim_info;
+
+ /* Init gstring */
+ g_tmp = g_string_new(NULL);
+
+ /* Check to see if this is a dup */
+ list = g_list_first(tapinfo->callsinfo_list);
+
+ while(list)
+ {
+ tmp_listinfo = list->data;
+
+ if(tmp_listinfo->protocol == VOIP_UNISTIM){
+
+ tmp_unistim_info = tmp_listinfo->prot_info;
+
+ /* Search by termid if possible, otherwise use ni/it ip + port.. */
+ if(pi->termid != 0){
+ if(tmp_unistim_info->termid == pi->termid){
+ /* If the call has ended, then we can reuse it.. */
+ if(tmp_listinfo->call_state == VOIP_COMPLETED || tmp_listinfo->call_state == VOIP_UNKNOWN){
+ /* Do nothing */
+ } else {
+ callsinfo = (voip_calls_info_t*)(list->data);
+ break;
+ }
+ }
+ } else {
+ /* If no term id use ips / port to find entry */
+ if(ADDRESSES_EQUAL(&tmp_unistim_info->it_ip, &pinfo->dst) && ADDRESSES_EQUAL(&tmp_unistim_info->ni_ip,&pinfo->src) && (tmp_unistim_info->it_port == pinfo->destport)){
+ if(tmp_listinfo->call_state == VOIP_COMPLETED || tmp_listinfo->call_state == VOIP_UNKNOWN){
+ /* Do nothing previous call */
+ } else {
+ callsinfo = (voip_calls_info_t*)(list->data);
+ break;
+ }
+ }
+ else if(ADDRESSES_EQUAL(&tmp_unistim_info->it_ip, &pinfo->src) && ADDRESSES_EQUAL(&tmp_unistim_info->ni_ip,&pinfo->dst) && (tmp_unistim_info->it_port == pinfo->srcport)) {
+ if(tmp_listinfo->call_state == VOIP_COMPLETED || tmp_listinfo->call_state == VOIP_UNKNOWN){
+ /* Do nothing, it ain't our call.. */
+ } else {
+ callsinfo = (voip_calls_info_t*)(list->data);
+ break;
+ }
+ }
+ }
+ }
+
+ /* Otherwise, go to the next one.. */
+ list = g_list_next(list);
+ }
+
+ if(pi->payload_type == 2 || pi->payload_type == 1){
+
+ if(pi->key_state == 1 || pi->hook_state == 1){
+
+ /* If the user hits a button,
+ Session will be SETUP */
+
+ /* If new add to list */
+ if (callsinfo==NULL){
+
+ callsinfo = g_malloc0(sizeof(voip_calls_info_t));
+ callsinfo->call_active_state = VOIP_ACTIVE;
+ callsinfo->call_state = VOIP_CALL_SETUP;
+ callsinfo->from_identity=g_strdup_printf("%x",pi->termid);
+ callsinfo->to_identity=g_strdup("UNKNOWN");
+ COPY_ADDRESS(&(callsinfo->initial_speaker),&(pinfo->src));
+ callsinfo->selected=FALSE;
+
+ /* Set this on init of struct so in case the call doesn't complete, we'll have a ref. */
+ /* Otherwise if the call is completed we'll have the open/close streams to ref actual call duration */
+ /* Store frame data which holds time and frame number */
+ callsinfo->start_fd=pinfo->fd;
+
+ callsinfo->protocol=VOIP_UNISTIM;
+ callsinfo->prot_info=g_malloc(sizeof(unistim_info_t));
+
+ tmp_unistim_info = callsinfo->prot_info;
+
+ /* Clear tap struct */
+ tmp_unistim_info->rudp_type = 0;
+ tmp_unistim_info->payload_type = 0;
+ tmp_unistim_info->sequence = pi->sequence;
+ tmp_unistim_info->termid = pi->termid;
+ tmp_unistim_info->key_val = -1;
+ tmp_unistim_info->key_state = -1;
+ tmp_unistim_info->hook_state = -1;
+ tmp_unistim_info->stream_connect = -1;
+ tmp_unistim_info->trans_connect = -1;
+ tmp_unistim_info->set_termid = -1;
+ tmp_unistim_info->string_data = NULL;
+ tmp_unistim_info->key_buffer = NULL;
+
+ COPY_ADDRESS(&(tmp_unistim_info->it_ip),&(pi->it_ip));
+ COPY_ADDRESS(&(tmp_unistim_info->ni_ip),&(pi->ni_ip));
+ tmp_unistim_info->it_port = pi->it_port;
+
+ callsinfo->free_prot_info = g_free;
+ callsinfo->npackets = 0;
+ callsinfo->call_num = tapinfo->ncalls++;
+ tapinfo->callsinfo_list = g_list_append(tapinfo->callsinfo_list, callsinfo);
+
+ } else {
+
+ /* Set up call wide info struct */
+ tmp_unistim_info = callsinfo->prot_info;
+ tmp_unistim_info->sequence = pi->sequence;
+ }
+
+ /* Each packet COULD BE OUR LAST!!!! */
+ /* Store frame data which holds time and frame number */
+ callsinfo->stop_fd = pinfo->fd;
+
+ /* This is a valid packet so increment counter */
+ ++(callsinfo->npackets);
+
+ /* increment the packets counter of all calls */
+ ++(tapinfo->npackets);
+
+ /* Key was depressed.. update key buffer.. */
+ if(pi->key_val >= 0 && pi->key_val <= 11){
+
+ if(tmp_unistim_info->key_buffer != NULL){
+
+ /* assign to temp variable */
+ g_string_assign(g_tmp,tmp_unistim_info->key_buffer);
+
+ /* Manipulate the data */
+ if(pi->key_val == 10) {
+ tmp_unistim_info->key_buffer = g_strdup_printf("%s*",g_tmp->str);
+ } else if(pi->key_val == 11) {
+ tmp_unistim_info->key_buffer = g_strdup_printf("%s#",g_tmp->str);
+ } else {
+ tmp_unistim_info->key_buffer = g_strdup_printf("%s%d",g_tmp->str,pi->key_val);
+ }
+
+ } else {
+
+ /* Create new string */
+ if(pi->key_val == 10) {
+ tmp_unistim_info->key_buffer = g_strdup("*");
+ } else if(pi->key_val == 11) {
+ tmp_unistim_info->key_buffer = g_strdup("#");
+ } else {
+ tmp_unistim_info->key_buffer = g_strdup_printf("%d",pi->key_val);
+ }
+
+ }
+
+ /* Select for non-digit characters */
+ if(pi->key_val == 10) {
+ comment = g_strdup_printf("Key Input Sent: * (%d)", pi->sequence);
+ } else if(pi->key_val == 11) {
+ comment = g_strdup_printf("Key Input Sent: # (%d)", pi->sequence);
+ } else {
+ comment = g_strdup_printf("Key Input Sent: %d (%d)",pi->key_val, pi->sequence);
+ }
+ } else if(pi->key_val == 12) {
+ /* Set label and comment for graph */
+ comment = g_strdup_printf("Key Input Sent: UP (%d)", pi->sequence);
+ } else if(pi->key_val == 13) {
+ /* Set label and comment for graph */
+ comment = g_strdup_printf("Key Input Sent: DOWN (%d)", pi->sequence);
+ } else if(pi->key_val == 14) {
+ /* Set label and comment for graph */
+ comment = g_strdup_printf("Key Input Sent: RIGHT (%d)", pi->sequence);
+ } else if(pi->key_val == 15) {
+ if(pi->key_buffer != NULL){
+ /* Get data */
+ g_string_assign(g_tmp,pi->key_buffer);
+
+ /* Manipulate the data */
+ g_string_truncate(g_tmp,g_tmp->len-1);
+
+ /* Insert new data */
+ tmp_unistim_info->key_buffer = g_strdup(g_tmp->str);
+ }
+
+ /* Set label and comment for graph */
+ comment = g_strdup_printf("Key Input Sent: LEFT (%d)", pi->sequence);
+ } else if(pi->key_val == 20) {
+ /* User pressed the soft key 0 probably dial */
+ comment = g_strdup_printf("Key Input Sent: S0 (%d)", pi->sequence);
+ } else if(pi->key_val == 21) {
+ /* User pressed the soft key 1 */
+ comment = g_strdup_printf("Key Input Sent: S1 (%d)", pi->sequence);
+ } else if(pi->key_val == 22) {
+ /* User pressed the soft key 2 */
+ /* On cs2k phones, soft key 2 is backspace. */
+ if(pi->key_buffer != NULL) {
+
+ /* Get data */
+ g_string_assign(g_tmp,pi->key_buffer);
+
+ /* Manipulate the data */
+ g_string_truncate(g_tmp,g_tmp->len-1);
+
+ /* Insert new data */
+ tmp_unistim_info->key_buffer = g_strdup(g_tmp->str);
+ }
+
+ /* add label and comment */
+ comment = g_strdup_printf("Key Input Sent: S2 (%d)", pi->sequence);
+ } else if(pi->key_val == 28) {
+ /* User pressed something */
+ comment = g_strdup_printf("Key Input Sent: Release (%d)", pi->sequence);
+ } else if(pi->key_val == 23) {
+ /* User pressed the soft key 3 */
+ /* Cancel on cs2k so clear buffer */
+ /* On mcs its config which will clear the buffer too */
+ tmp_unistim_info->key_buffer = g_strdup("\n");
+
+ /* User pressed something, set labels*/
+ comment = g_strdup_printf("Key Input Sent: S3 (%d)", pi->sequence);
+ } else if(pi->key_val == 27) {
+ /* User pressed something */
+ comment = g_strdup_printf("Key Input Sent: Hold (%d)", pi->sequence);
+ } else if(pi->key_val == 29) {
+ /* User pressed something */
+ comment = g_strdup_printf("Key Input Sent: Mute (%d)", pi->sequence);
+ } else if(pi->key_val == 30) {
+ /* User pressed something */
+ comment = g_strdup_printf("Key Input Sent: Headset (%d)", pi->sequence);
+ } else if(pi->key_val == 31) {
+ /* Handsfree button */
+ comment = g_strdup_printf("Key Input Sent: Handsfree (%d)", pi->sequence);
+ } else if(pi->key_val >= 32 && pi->key_val <= 56) {
+ /* Prog. Key X */
+ comment = g_strdup_printf("Key Input Sent: Prog%d (%d)", (pi->key_val & 31), pi->sequence);
+ }
+
+ if(pi->key_val != -1) {
+
+ frame_label = "KEY INPUT";
+
+ if (comment == NULL)
+ /* Ouch! What do you do!? */
+ /* User pressed something */
+ comment = g_strdup_printf("Key Input Sent: UNKNOWN - %d (%d)", pi->key_val, pi->sequence);
+
+ /* add to the graph */
+ add_to_graph(tapinfo, pinfo, frame_label, comment, callsinfo->call_num, &(pinfo->src), &(pinfo->dst), 1);
+
+ g_free(comment);
+ }
+
+ if(pi->hook_state == 1) {
+
+ /* Phone is off hook */
+ frame_label = "OFF HOOK";
+ comment = g_strdup_printf("Off Hook (%d)", pi->sequence);
+
+ /* add to the graph */
+ add_to_graph(tapinfo, pinfo, frame_label, comment, callsinfo->call_num, &(pinfo->src), &(pinfo->dst), 1);
+
+ g_free(comment);
+ } else if(pi->hook_state == 0) {
+
+ /* Phone is on hook */
+ frame_label = "ON HOOK";
+ comment = g_strdup_printf("On Hook (%d)", pi->sequence);
+
+ /* add to the graph */
+ add_to_graph(tapinfo, pinfo, frame_label, comment, callsinfo->call_num, &(pinfo->src), &(pinfo->dst), 1);
+
+ g_free(comment);
+ }
+ }
+
+ /* Open stream was sent from server */
+ if(pi->stream_connect == 1 && callsinfo != NULL) {
+
+ /* Open stream */
+ /* Signifies the start of the call so set start_sec & start_usec */
+ /* Frame data holds the time info */
+ callsinfo->start_fd=pinfo->fd;
+
+ /* Local packets too */
+ ++(callsinfo->npackets);
+
+ /* increment the packets counter of all calls */
+ ++(tapinfo->npackets);
+
+ /* ?? means we're not quite sure if this is accurate. Since Unistim isn't a true
+ Call control protocol, we can only guess at the destination by messing with
+ key buffers. */
+ if(tmp_unistim_info->key_buffer != NULL){
+ callsinfo->to_identity = g_strdup_printf("?? %s",tmp_unistim_info->key_buffer);
+ }
+
+ /* change sequence number for ACK detection */
+ tmp_unistim_info->sequence = pi->sequence;
+
+ /* State changes too */
+ callsinfo->call_active_state = VOIP_ACTIVE;
+ callsinfo->call_state = VOIP_IN_CALL;
+
+ /* Add graph data */
+ frame_label = "STREAM OPENED";
+ comment = g_strdup_printf("Stream Opened (%d)",pi->sequence);
+
+ /* add to the graph */
+ add_to_graph(tapinfo, pinfo, frame_label, comment, callsinfo->call_num, &(pinfo->src), &(pinfo->dst), 1);
+
+ } else if(pi->stream_connect == 1 && callsinfo == NULL) {
+
+ /* Research indicates some nortel products initiate stream first
+ * without keypresses. therefore creating this solely on a keypress is
+ * ineffective.
+ * Sometimes calls start immediately with open stream.
+ */
+ callsinfo = g_malloc0(sizeof(voip_calls_info_t));
+ callsinfo->call_active_state = VOIP_ACTIVE;
+ callsinfo->call_state = VOIP_CALL_SETUP;
+ callsinfo->from_identity=g_strdup("UNKNOWN");
+ callsinfo->to_identity=g_strdup("UNKNOWN");
+ COPY_ADDRESS(&(callsinfo->initial_speaker),&(pinfo->src));
+ callsinfo->selected=FALSE;
+
+ /* Set this on init of struct so in case the call doesn't complete, we'll have a ref. */
+ /* Otherwise if the call is completed we'll have the open/close streams to ref actual call duration */
+ callsinfo->start_fd=pinfo->fd;
+
+ callsinfo->protocol=VOIP_UNISTIM;
+ callsinfo->prot_info=g_malloc(sizeof(unistim_info_t));
+
+ tmp_unistim_info = callsinfo->prot_info;
+
+ /* Clear tap struct */
+ tmp_unistim_info->rudp_type = 0;
+ tmp_unistim_info->payload_type = 0;
+ tmp_unistim_info->sequence = pi->sequence;
+ tmp_unistim_info->termid = 0;
+ tmp_unistim_info->key_val = -1;
+ tmp_unistim_info->key_state = -1;
+ tmp_unistim_info->hook_state = -1;
+ tmp_unistim_info->stream_connect = -1;
+ tmp_unistim_info->trans_connect = -1;
+ tmp_unistim_info->set_termid = -1;
+ tmp_unistim_info->string_data = NULL;
+ tmp_unistim_info->key_buffer = NULL;
+
+ COPY_ADDRESS(&(tmp_unistim_info->it_ip),&(pi->it_ip));
+ COPY_ADDRESS(&(tmp_unistim_info->ni_ip),&(pi->ni_ip));
+ tmp_unistim_info->it_port = pi->it_port;
+
+ callsinfo->free_prot_info = g_free;
+ callsinfo->npackets = 0;
+ callsinfo->call_num = tapinfo->ncalls++;
+ tapinfo->callsinfo_list = g_list_append(tapinfo->callsinfo_list, callsinfo);
+
+ /* Open stream */
+ /* Signifies the start of the call so set start_sec & start_usec */
+ /* frame_data holds the time info */
+ callsinfo->start_fd=pinfo->fd;
+
+ /* Local packets too */
+ ++(callsinfo->npackets);
+
+ /* increment the packets counter of all calls */
+ ++(tapinfo->npackets);
+
+ /* ?? means we're not quite sure if this is accurate. Since Unistim isn't a true
+ Call control protocol, we can only guess at the destination by messing with
+ key buffers. */
+ if(tmp_unistim_info->key_buffer != NULL){
+ callsinfo->to_identity = g_strdup_printf("?? %s",tmp_unistim_info->key_buffer);
+ }
+
+ /* change sequence number for ACK detection */
+ tmp_unistim_info->sequence = pi->sequence;
+
+ /* State changes too */
+ callsinfo->call_active_state = VOIP_ACTIVE;
+ callsinfo->call_state = VOIP_IN_CALL;
+
+ /* Add graph data */
+ frame_label = "STREAM OPENED";
+ comment = g_strdup_printf("Stream Opened (%d)",pi->sequence);
+
+ /* add to the graph */
+ add_to_graph(tapinfo, pinfo, frame_label, comment, callsinfo->call_num, &(pinfo->src), &(pinfo->dst), 1);
+
+ } else if(pi->stream_connect == 0 && callsinfo != NULL) {
+ /* Close Stream */
+
+ /* Set stop seconds + usec */
+ /* frame_data holds the time info */
+ callsinfo->stop_fd = pinfo->fd;
+
+ tmp_unistim_info->sequence = pi->sequence;
+
+ if(callsinfo->call_state == VOIP_IN_CALL){
+ callsinfo->call_active_state = VOIP_INACTIVE;
+ callsinfo->call_state = VOIP_COMPLETED;
+ } else {
+ callsinfo->call_state = VOIP_UNKNOWN;
+ callsinfo->call_active_state = VOIP_INACTIVE;
+ }
+
+ frame_label = "STREAM CLOSED";
+ comment = g_strdup_printf("Stream Closed (%d)",pi->sequence);
+
+ /* add to the graph */
+ add_to_graph(tapinfo, pinfo, frame_label, comment, callsinfo->call_num, &(pinfo->src), &(pinfo->dst), 1);
+
+ } else
+ comment = NULL;
+
+ } else if(pi->rudp_type == 1 && callsinfo != NULL) {
+ /* ACK */
+ /* Only show acks for processed seq #s */
+ if(tmp_unistim_info->sequence == pi->sequence) {
+
+ frame_label = "ACK";
+ comment = g_strdup_printf("ACK for sequence %d",pi->sequence);
+
+ /* add to the graph */
+ add_to_graph(tapinfo, pinfo, frame_label, comment, callsinfo->call_num, &(pinfo->src), &(pinfo->dst), 1);
+
+ }
+
+ } else if(pi->rudp_type == 0 && callsinfo != NULL) {
+
+ /* NAK */
+ frame_label = "NAK";
+ comment = g_strdup_printf("NAK for sequence %d",pi->sequence);
+
+ /* add to the graph */
+ add_to_graph(tapinfo, pinfo, frame_label, comment, callsinfo->call_num, &(pinfo->src), &(pinfo->dst), 1);
+
+ }
+
+ /* free data */
+ g_free(comment);
+
+ tapinfo->redraw = TRUE;
+
+ return 1;
+}
+
+/****************************************************************************/
+/* TAP INTERFACE */
+/****************************************************************************/
+static gboolean have_unistim_tap_listener=FALSE;
+/****************************************************************************/
+void
+unistim_calls_init_tap(void){
+
+ GString *error_string;
+
+ if(have_unistim_tap_listener==FALSE) {
+
+ error_string = register_tap_listener("unistim", &(the_tapinfo_struct.unistim_dummy),
+ NULL,
+ 0,
+ voip_calls_dlg_reset,
+ unistim_calls_packet,
+ voip_calls_dlg_draw
+ );
+
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+
+ have_unistim_tap_listener=TRUE;
+ }
+}
+
+/****************************************************************************/
+void
+remove_tap_listener_unistim_calls(void)
+{
+ protect_thread_critical_region();
+ remove_tap_listener(&(the_tapinfo_struct.unistim_dummy));
+ unprotect_thread_critical_region();
+
+ have_unistim_tap_listener=FALSE;
+}
+
+/****************************************************************************/
+/* ***************************TAP for SKINNY **********************************/
+/****************************************************************************/
+
+/* Telecaster to tap-voip call state mapping */
+static const voip_call_state skinny_tap_voip_state[] = {
+ VOIP_NO_STATE,
+ VOIP_CALL_SETUP,
+ VOIP_COMPLETED,
+ VOIP_RINGING,
+ VOIP_RINGING,
+ VOIP_IN_CALL,
+ VOIP_REJECTED,
+ VOIP_REJECTED,
+ VOIP_IN_CALL,
+ VOIP_IN_CALL,
+ VOIP_COMPLETED,
+ VOIP_COMPLETED,
+ VOIP_CALL_SETUP,
+ VOIP_UNKNOWN,
+ VOIP_REJECTED
+};
+
+static int
+skinny_calls_packet(void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *skinny_info)
+{
+ voip_calls_tapinfo_t *tapinfo = &the_tapinfo_struct;
+ GList* list;
+ voip_calls_info_t *callsinfo = NULL;
+ address* phone;
+ const skinny_info_t *si = skinny_info;
+ skinny_calls_info_t *tmp_skinnyinfo;
+ gchar *comment;
+
+ if (si == NULL || (si->callId == 0 && si->passThruId == 0))
+ return 0;
+ /* check whether we already have this context in the list */
+ list = g_list_first(tapinfo->callsinfo_list);
+ while (list)
+ {
+ voip_calls_info_t* tmp_listinfo = list->data;
+ if (tmp_listinfo->protocol == VOIP_SKINNY){
+ tmp_skinnyinfo = tmp_listinfo->prot_info;
+ if (tmp_skinnyinfo->callId == si->callId ||
+ tmp_skinnyinfo->callId == si->passThruId){
+ callsinfo = (voip_calls_info_t*)(list->data);
+ break;
+ }
+ }
+ list = g_list_next (list);
+ }
+
+ if (si->messId >= 256)
+ phone = &(pinfo->dst);
+ else
+ phone = &(pinfo->src);
+
+ if (callsinfo==NULL){
+ callsinfo = g_malloc0(sizeof(voip_calls_info_t));
+ callsinfo->call_state = VOIP_NO_STATE;
+ callsinfo->call_active_state = VOIP_ACTIVE;
+ /* callsinfo->from_identity = g_strdup_printf("%s : %.8x", "Skinny", 1); */
+ callsinfo->from_identity = g_strdup("");
+ callsinfo->to_identity = g_strdup("");
+ callsinfo->prot_info = g_malloc(sizeof(skinny_calls_info_t));
+ callsinfo->free_prot_info = g_free;
+ tmp_skinnyinfo = callsinfo->prot_info;
+ tmp_skinnyinfo->callId = si->callId ? si->callId : si->passThruId;
+ callsinfo->npackets = 1;
+
+ COPY_ADDRESS(&(callsinfo->initial_speaker), phone);
+
+ callsinfo->protocol = VOIP_SKINNY;
+ callsinfo->call_num = tapinfo->ncalls++;
+ callsinfo->start_fd = pinfo->fd;
+ callsinfo->stop_fd = pinfo->fd;
+
+ callsinfo->selected = FALSE;
+ tapinfo->callsinfo_list = g_list_append(tapinfo->callsinfo_list, callsinfo);
+ } else {
+ if (si->callingParty) {
+ g_free(callsinfo->from_identity);
+ callsinfo->from_identity = g_strdup(si->callingParty);
+ }
+ if (si->calledParty) {
+ g_free(callsinfo->to_identity);
+ callsinfo->to_identity = g_strdup(si->calledParty);
+ }
+ if ((si->callState > 0) && (si->callState < (sizeof(skinny_tap_voip_state)/sizeof(skinny_tap_voip_state[0]))))
+ callsinfo->call_state = skinny_tap_voip_state[si->callState];
+
+ callsinfo->stop_fd = pinfo->fd;
+ ++(callsinfo->npackets);
+ }
+
+ if (si->callId) {
+ if (si->passThruId)
+ comment = g_strdup_printf("CallId = %u, PTId = %u", si->callId, si->passThruId);
+ else
+ comment = g_strdup_printf("CallId = %u, LineId = %u", si->callId, si->lineId);
+ } else {
+ if (si->passThruId)
+ comment = g_strdup_printf("PTId = %u", si->passThruId);
+ else
+ comment = NULL;
+ }
+
+ add_to_graph(tapinfo, pinfo, si->messageName, comment,
+ callsinfo->call_num, &(pinfo->src), &(pinfo->dst), 1);
+ g_free(comment);
+
+ return 1;
+}
+
+
+/****************************************************************************/
+/* TAP INTERFACE */
+/****************************************************************************/
+static gboolean have_skinny_tap_listener=FALSE;
+/****************************************************************************/
+void
+skinny_calls_init_tap(void)
+{
+ GString *error_string;
+
+ if(have_skinny_tap_listener==FALSE)
+ {
+ /*
+ * Don't register the tap listener if we have it already.
+ * We set TL_REQUIRES_PROTO_TREE to force a non-null "tree"
+ * in the SKINNY dissector; otherwise, the dissector
+ * doesn't fill in the info passed to the tap's packet
+ * routine.
+ */
+ error_string = register_tap_listener("skinny",
+ &(the_tapinfo_struct.skinny_dummy),
+ NULL,
+ TL_REQUIRES_PROTO_TREE,
+ voip_calls_dlg_reset,
+ skinny_calls_packet,
+ voip_calls_dlg_draw
+ );
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+ have_skinny_tap_listener=TRUE;
+ }
+}
+
+/****************************************************************************/
+void
+remove_tap_listener_skinny_calls(void)
+{
+ protect_thread_critical_region();
+ remove_tap_listener(&(the_tapinfo_struct.skinny_dummy));
+ unprotect_thread_critical_region();
+
+ have_skinny_tap_listener=FALSE;
+}
+
+/****************************************************************************/
+/* ***************************TAP for IAX2 **********************************/
+/****************************************************************************/
+
+/* IAX2 to tap-voip call state mapping */
+static const voip_call_state tap_iax_voip_state[] = {
+ VOIP_NO_STATE,
+ VOIP_CALL_SETUP, /*NEW*/
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_COMPLETED, /*HANGUP*/
+ VOIP_REJECTED, /*REJECT*/
+ VOIP_RINGING, /*ACCEPT*/
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_CALL_SETUP, /*DIAL*/
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE,
+ VOIP_NO_STATE
+};
+
+static void free_iax2_info(gpointer p) {
+ iax2_info_t *ii = p;
+
+ g_free(ii);
+}
+
+
+/****************************************************************************/
+/* whenever a IAX2 packet is seen by the tap listener */
+static int
+iax2_calls_packet( void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *iax2_info)
+{
+ voip_calls_tapinfo_t *tapinfo = &the_tapinfo_struct;
+ GList* list;
+ voip_calls_info_t *callsinfo = NULL;
+ address* phone;
+ const iax2_info_t *ii = iax2_info;
+ iax2_info_t *tmp_iax2info;
+ gchar * comment;
+
+ if (ii == NULL || ii->ptype != IAX2_FULL_PACKET || (ii->scallno == 0 && ii->dcallno == 0))
+ return 0;
+ /* check whether we already have this context in the list */
+ list = g_list_first(tapinfo->callsinfo_list);
+ while (list)
+ {
+ voip_calls_info_t* tmp_listinfo = list->data;
+ if (tmp_listinfo->protocol == VOIP_IAX2){
+ tmp_iax2info = tmp_listinfo->prot_info;
+ if (tmp_iax2info->scallno == ii->scallno ||
+ tmp_iax2info->scallno == ii->dcallno){
+ callsinfo = (voip_calls_info_t*)(list->data);
+ break;
+ }
+ }
+ list = g_list_next (list);
+ }
+ phone = &(pinfo->src);
+
+
+ if (callsinfo==NULL){
+ /* We only care about real calls, i.e., no registration stuff */
+ if (ii->ftype != AST_FRAME_IAX || ii->csub != IAX_COMMAND_NEW)
+ return 0;
+ callsinfo = g_malloc0(sizeof(voip_calls_info_t));
+ callsinfo->call_state = VOIP_NO_STATE;
+ callsinfo->call_active_state = VOIP_ACTIVE;
+ callsinfo->prot_info=g_malloc(sizeof(iax2_info_t));
+ callsinfo->free_prot_info = free_iax2_info;
+ tmp_iax2info = callsinfo->prot_info;
+
+ tmp_iax2info->scallno = ii->scallno;
+ if (tmp_iax2info->scallno == 0) tmp_iax2info->scallno = ii->dcallno;
+ tmp_iax2info->callState = tap_iax_voip_state[ii->callState];
+
+ callsinfo->npackets = 1;
+
+ COPY_ADDRESS(&(callsinfo->initial_speaker), phone);
+ callsinfo->from_identity = g_strdup(ii->callingParty);
+ callsinfo->to_identity = g_strdup(ii->calledParty);
+
+ callsinfo->protocol = VOIP_IAX2;
+ callsinfo->call_num = tapinfo->ncalls++;
+ callsinfo->start_fd=pinfo->fd;
+ callsinfo->stop_fd = pinfo->fd;
+
+ callsinfo->selected = FALSE;
+ tapinfo->callsinfo_list = g_list_append(tapinfo->callsinfo_list, callsinfo);
+
+ } else {
+ if ((ii->callState > 0) && (ii->callState < (sizeof(tap_iax_voip_state)/sizeof(tap_iax_voip_state[0]))))
+ callsinfo->call_state = tap_iax_voip_state[ii->callState];
+
+ callsinfo->stop_fd = pinfo->fd;
+ ++(callsinfo->npackets);
+ }
+
+ comment = "";
+
+ add_to_graph(tapinfo, pinfo, ii->messageName, comment,
+ callsinfo->call_num, &(pinfo->src), &(pinfo->dst), 1);
+
+ return 1;
+
+}
+
+
+/****************************************************************************/
+/* TAP INTERFACE */
+/****************************************************************************/
+static gboolean have_iax2_tap_listener=FALSE;
+/****************************************************************************/
+void
+iax2_calls_init_tap(void)
+{
+ GString *error_string;
+
+ if(have_iax2_tap_listener==FALSE)
+ {
+ /*
+ * Don't register the tap listener if we have it already.
+ * We set TL_REQUIRES_PROTO_TREE to force a non-null "tree"
+ * in the IAX2 dissector; otherwise, the dissector
+ * doesn't fill in the info passed to the tap's packet
+ * routine.
+ * XXX - that appears to be true of the MGCP and SKINNY
+ * dissectors, but, unless I've missed something, it doesn't
+ * appear to be true of the IAX2 dissector.
+ */
+ error_string = register_tap_listener("IAX2",
+ &(the_tapinfo_struct.iax2_dummy),
+ NULL,
+ TL_REQUIRES_PROTO_TREE,
+ voip_calls_dlg_reset,
+ iax2_calls_packet,
+ voip_calls_dlg_draw
+ );
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+ have_iax2_tap_listener=TRUE;
+ }
+}
+
+/****************************************************************************/
+void
+remove_tap_listener_iax2_calls(void)
+{
+ protect_thread_critical_region();
+ remove_tap_listener(&(the_tapinfo_struct.iax2_dummy));
+ unprotect_thread_critical_region();
+
+ have_iax2_tap_listener=FALSE;
+}
+
+/****************************************************************************/
+/* ***************************TAP for OTHER PROTOCOL **********************************/
+/****************************************************************************/
+
+static int
+VoIPcalls_packet(void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *VoIPinfo)
+{
+ voip_calls_tapinfo_t *tapinfo = &the_tapinfo_struct;
+ voip_calls_info_t *callsinfo = NULL;
+ voip_calls_info_t *tmp_listinfo;
+ GList *list = NULL;
+ const voip_packet_info_t *pi = VoIPinfo;
+
+ if (pi->call_id)
+ list = g_list_first(tapinfo->callsinfo_list);
+ while (list) {
+ tmp_listinfo = list->data;
+ if ( tmp_listinfo->protocol == VOIP_COMMON ) {
+ if (!strcmp(pi->call_id, tmp_listinfo->call_id)) {
+ callsinfo = (voip_calls_info_t*)(list->data);
+ break;
+ }
+ }
+ list = g_list_next(list);
+ }
+
+ if (callsinfo == NULL) {
+ callsinfo = g_malloc0(sizeof(voip_calls_info_t));
+ callsinfo->call_active_state = pi->call_active_state;
+ callsinfo->call_state = pi->call_state;
+ callsinfo->call_id=g_strdup((pi->call_id)?pi->call_id:"");
+ callsinfo->from_identity = g_strdup((pi->from_identity)?pi->from_identity:"");
+ callsinfo->to_identity = g_strdup((pi->to_identity)?pi->to_identity:"");
+ COPY_ADDRESS(&(callsinfo->initial_speaker),&(pinfo->src));
+ callsinfo->selected=FALSE;
+ callsinfo->start_fd=pinfo->fd;
+ callsinfo->protocol=VOIP_COMMON;
+ callsinfo->protocol_name=g_strdup((pi->protocol_name)?pi->protocol_name:"");
+ callsinfo->call_comment=g_strdup((pi->call_comment)?pi->call_comment:"");
+ callsinfo->prot_info=NULL;
+ callsinfo->free_prot_info = NULL;
+
+ callsinfo->call_num = tapinfo->ncalls++;
+ callsinfo->npackets = 0;
+
+ tapinfo->callsinfo_list = g_list_append(tapinfo->callsinfo_list, callsinfo);
+ }
+
+ if (callsinfo != NULL) {
+ callsinfo->call_active_state = pi->call_active_state;
+ if ((callsinfo->call_state != VOIP_COMPLETED) && (pi->call_state == VOIP_COMPLETED))
+ tapinfo->completed_calls++;
+ if (pi->call_state != VOIP_NO_STATE)
+ callsinfo->call_state = pi->call_state;
+ if (pi->call_comment) {
+ g_free(callsinfo->call_comment);
+ callsinfo->call_comment=g_strdup(pi->call_comment);
+ }
+ callsinfo->stop_fd = pinfo->fd;
+ ++(callsinfo->npackets);
+ ++(tapinfo->npackets);
+ }
+
+ /* add to the graph */
+ add_to_graph(tapinfo, pinfo, (pi->frame_label)?pi->frame_label:"VoIP msg", pi->frame_comment, callsinfo->call_num, &(pinfo->src), &(pinfo->dst), 1);
+
+ tapinfo->redraw = TRUE;
+
+ return 1;
+}
+/****************************************************************************/
+static gboolean have_voip_tap_listener=FALSE;
+
+void
+VoIPcalls_init_tap(void)
+{
+ GString *error_string;
+
+ if(have_voip_tap_listener==FALSE)
+ {
+ error_string = register_tap_listener("voip", &(the_tapinfo_struct.voip_dummy),
+ NULL,
+ 0,
+ voip_calls_dlg_reset,
+ VoIPcalls_packet,
+ voip_calls_dlg_draw
+ );
+
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+ have_voip_tap_listener=TRUE;
+ }
+}
+/****************************************************************************/
+void
+remove_tap_listener_voip_calls(void)
+{
+ protect_thread_critical_region();
+ remove_tap_listener(&(the_tapinfo_struct.voip_dummy));
+ unprotect_thread_critical_region();
+
+ have_voip_tap_listener=FALSE;
+}
+
+
+/****************************************************************************/
+/* ***************************TAP for OTHER PROTOCOL **********************************/
+/****************************************************************************/
+
+/****************************************************************************/
+/* whenever a prot_ packet is seen by the tap listener */
+/*
+static int
+prot_calls_packet(void *ptr _U_, packet_info *pinfo, epan_dissect_t *edt _U_, const void *prot_info _U_)
+{
+ voip_calls_tapinfo_t *tapinfo = &the_tapinfo_struct;
+ if (callsinfo!=NULL){
+ callsinfo->stop_abs = pinfo->fd->abs_ts;
+ callsinfo->stop_rel = pinfo->fd->rel_ts;
+ callsinfo->last_frame_num=pinfo->fd->num;
+ ++(callsinfo->npackets);
+ ++(tapinfo->npackets);
+ }
+
+ tapinfo->redraw = TRUE;
+
+ return 1;
+}
+*/
+/****************************************************************************/
+/*
+static gboolean have_prot__tap_listener=FALSE;
+
+void
+prot_calls_init_tap(void)
+{
+ GString *error_string;
+
+ if(have_prot__tap_listener==FALSE)
+ {
+ error_string = register_tap_listener("prot_", &(the_tapinfo_struct.prot__dummy),
+ NULL,
+ 0,
+ voip_calls_dlg_reset,
+ prot__calls_packet,
+ voip_calls_dlg_draw
+ );
+
+ if (error_string != NULL) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+ have_prot__tap_listener=TRUE;
+ }
+}
+*/
+/****************************************************************************/
+/*
+void
+remove_tap_listener_prot__calls(void)
+{
+ protect_thread_critical_region();
+ remove_tap_listener(&(the_tapinfo_struct.prot__dummy));
+ unprotect_thread_critical_region();
+
+ have_prot__tap_listener=FALSE;
+}
+*/
diff --git a/ui/gtk/voip_calls.h b/ui/gtk/voip_calls.h
new file mode 100644
index 0000000000..d6ff33ca43
--- /dev/null
+++ b/ui/gtk/voip_calls.h
@@ -0,0 +1,272 @@
+/* voip_calls.h
+ * VoIP calls summary addition for Wireshark
+ *
+ * $Id$
+ *
+ * Copyright 2004, Ericsson , Spain
+ * By Francisco Alcoba <francisco.alcoba@ericsson.com>
+ *
+ * based on h323_calls.h
+ * Copyright 2004, Iskratel, Ltd, Kranj
+ * By Miha Jemec <m.jemec@iskratel.si>
+ *
+ * H323, RTP and Graph Support
+ * By Alejandro Vaquero, alejandro.vaquero@verso.com
+ * Copyright 2005, Verso Technologies Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __VOIP_CALLS_H__
+#define __VOIP_CALLS_H__
+
+#include <glib.h>
+#include <stdio.h>
+#include <epan/address.h>
+#include <epan/guid-utils.h>
+#include <epan/tap-voip.h>
+
+/****************************************************************************/
+extern const char *voip_call_state_name[8];
+
+typedef enum _voip_protocol {
+ VOIP_SIP,
+ VOIP_ISUP,
+ VOIP_H323,
+ VOIP_MGCP,
+ VOIP_AC_ISDN,
+ VOIP_AC_CAS,
+ MEDIA_T38,
+ TEL_H248,
+ TEL_SCCP,
+ TEL_BSSMAP,
+ TEL_RANAP,
+ VOIP_UNISTIM,
+ VOIP_SKINNY,
+ VOIP_IAX2,
+ VOIP_COMMON
+} voip_protocol;
+
+extern const char *voip_protocol_name[];
+
+/* defines specific SIP data */
+
+typedef enum _sip_call_state {
+ SIP_INVITE_SENT,
+ SIP_200_REC,
+ SIP_CANCEL_SENT
+} sip_call_state;
+
+typedef struct _sip_calls_info {
+ gchar *call_identifier;
+ guint32 invite_cseq;
+ sip_call_state sip_state;
+} sip_calls_info_t;
+
+/* defines specific ISUP data */
+typedef struct _isup_calls_info {
+ guint16 cic;
+ guint32 opc, dpc;
+ guint8 ni;
+} isup_calls_info_t;
+
+/* defines specific H245 data */
+typedef struct _h245_address {
+ address h245_address;
+ guint16 h245_port;
+} h245_address_t;
+
+/* defines specific H323 data */
+typedef struct _h323_calls_info {
+ e_guid_t *guid; /* Call ID to identify a H225 */
+ GList* h245_list; /* list of H245 Address and ports for tunneling off calls*/
+ address h225SetupAddr; /* we use the SETUP H225 IP to determine if packets are forward or reverse */
+ gboolean is_h245;
+ gboolean is_faststart_Setup; /* if faststart field is included in Setup*/
+ gboolean is_faststart_Proc; /* if faststart field is included in Proce, Alerting, Progress or Connect*/
+ gboolean is_h245Tunneling;
+ gint32 q931_crv;
+ gint32 q931_crv2;
+ guint requestSeqNum;
+} h323_calls_info_t;
+
+/* defines specific MGCP data */
+typedef struct _mgcp_calls_info {
+ gchar *endpointId;
+ gboolean fromEndpoint; /* true if the call was originated from the Endpoint, false for calls from MGC */
+} mgcp_calls_info_t;
+
+/* defines specific ACTRACE ISDN data */
+typedef struct _actrace_isdn_calls_info {
+ gint32 crv;
+ int trunk;
+} actrace_isdn_calls_info_t;
+
+/* defines specific ACTRACE CAS data */
+typedef struct _actrace_cas_calls_info {
+ gint32 bchannel;
+ int trunk;
+} actrace_cas_calls_info_t;
+
+/* defines specific SKINNY data */
+typedef struct _skinny_calls_info {
+ guint32 callId;
+} skinny_calls_info_t;
+
+/* defines a voip call */
+typedef struct _voip_calls_info {
+ voip_call_state call_state;
+ voip_call_active_state call_active_state;
+ gchar *call_id;
+ gchar *from_identity;
+ gchar *to_identity;
+ gpointer prot_info;
+ void(*free_prot_info)(gpointer);
+ address initial_speaker;
+ guint32 npackets;
+ voip_protocol protocol;
+ gchar *protocol_name;
+ gchar *call_comment;
+ guint16 call_num;
+ /* The frame_data struct holds the frame number and timing information needed. */
+ frame_data *start_fd;
+ frame_data *stop_fd;
+ gboolean selected;
+
+} voip_calls_info_t;
+
+/* structure that holds the information about all detected calls */
+/* struct holding all information of the tap */
+
+typedef struct _voip_calls_tapinfo {
+ int ncalls; /* number of call */
+ GList* callsinfo_list; /* list with all calls */
+ int npackets; /* total number of packets of all calls */
+ voip_calls_info_t* filter_calls_fwd; /* used as filter in some tap modes */
+ guint32 launch_count; /* number of times the tap has been run */
+ int start_packets;
+ int completed_calls;
+ int rejected_calls;
+ graph_analysis_info_t* graph_analysis;
+ gboolean redraw;
+ /*
+ * Now add dummy variables, one for each tap listener.
+ * Their address will be used to distinguish between them.
+ */
+ int sip_dummy;
+ int sdp_dummy;
+ int h225_dummy;
+ int h245dg_dummy;
+ int mtp3_dummy;
+ int m3ua_dummy;
+ int isup_dummy;
+ int q931_dummy;
+ int mgcp_dummy;
+ int actrace_dummy;
+ int t38_dummy;
+ int h248_dummy;
+ int sccp_dummy;
+ int sua_dummy;
+ int megaco_dummy;
+ int unistim_dummy;
+ int skinny_dummy;
+ int iax2_dummy;
+ int voip_dummy;
+} voip_calls_tapinfo_t;
+
+
+
+/* structure that holds the information about all RTP streams associated with the calls */
+/* struct holding all information of the RTP tap */
+typedef struct _voip_rtp_tapinfo {
+ int nstreams; /* number of rtp streams */
+ GList* list; /* list with the rtp streams */
+ int rtp_dummy;
+ int rtp_event_dummy;
+} voip_rtp_tapinfo_t;
+
+/****************************************************************************/
+/* INTERFACE */
+
+/*
+* Registers the voip_calls tap listeners (if not already done).
+* From that point on, the calls list will be updated with every redissection.
+* This function is also the entry point for the initialization routine of the tap system.
+* So whenever voip_calls.c is added to the list of WIRESHARK_TAP_SRCs, the tap will be registered on startup.
+* If not, it will be registered on demand by the voip_calls functions that need it.
+*/
+void sip_calls_init_tap(void);
+void isup_calls_init_tap(void);
+void mtp3_calls_init_tap(void);
+void h225_calls_init_tap(void);
+void h245dg_calls_init_tap(void);
+void q931_calls_init_tap(void);
+void sdp_calls_init_tap(void);
+void rtp_init_tap(void);
+void rtp_event_init_tap(void);
+void mgcp_calls_init_tap(void);
+void actrace_calls_init_tap(void);
+void t38_init_tap(void);
+void h248_calls_init_tap(void);
+void sccp_calls_init_tap(void);
+void unistim_calls_init_tap(void);
+void skinny_calls_init_tap(void);
+void iax2_calls_init_tap(void);
+void VoIPcalls_init_tap(void);
+
+/*
+* Removes the voip_calls tap listener (if not already done)
+* From that point on, the voip calls list won't be updated any more.
+*/
+void remove_tap_listener_sip_calls(void);
+void remove_tap_listener_isup_calls(void);
+void remove_tap_listener_mtp3_calls(void);
+void remove_tap_listener_h225_calls(void);
+void remove_tap_listener_h245dg_calls(void);
+void remove_tap_listener_q931_calls(void);
+void remove_tap_listener_sdp_calls(void);
+void remove_tap_listener_rtp(void);
+void remove_tap_listener_rtp_event(void);
+void remove_tap_listener_mgcp_calls(void);
+void remove_tap_listener_actrace_calls(void);
+void remove_tap_listener_t38(void);
+void remove_tap_listener_h248_calls(void);
+void remove_tap_listener_sccp_calls(void);
+void remove_tap_listener_unistim_calls(void);
+void remove_tap_listener_skinny_calls(void);
+void remove_tap_listener_iax2_calls(void);
+void remove_tap_listener_voip_calls(void);
+
+/*
+* Retrieves a constant reference to the unique info structure of the voip_calls tap listener.
+* The user should not modify the data pointed to.
+*/
+voip_calls_tapinfo_t* voip_calls_get_info(void);
+
+/*
+* Cleans up memory of voip calls tap.
+*/
+void isup_calls_reset(voip_calls_tapinfo_t *tapinfo);
+void mtp3_calls_reset(voip_calls_tapinfo_t *tapinfo);
+void q931_calls_reset(voip_calls_tapinfo_t *tapinfo);
+void voip_calls_reset(voip_calls_tapinfo_t *tapinfo);
+
+void graph_analysis_data_init(void);
+
+#endif /* __VOIP_CALLS_H__ */
diff --git a/ui/gtk/voip_calls_dlg.c b/ui/gtk/voip_calls_dlg.c
new file mode 100644
index 0000000000..d037b06d32
--- /dev/null
+++ b/ui/gtk/voip_calls_dlg.c
@@ -0,0 +1,888 @@
+/* voip_calls_dlg.c
+ * VoIP calls summary addition for Wireshark
+ *
+ * $Id$
+ *
+ * Copyright 2004, Ericsson , Spain
+ * By Francisco Alcoba <francisco.alcoba@ericsson.com>
+ *
+ * based on h323_calls_dlg.c
+ * Copyright 2004, Iskratel, Ltd, Kranj
+ * By Miha Jemec <m.jemec@iskratel.si>
+ *
+ * H323, RTP and Graph Support
+ * By Alejandro Vaquero, alejandro.vaquero@verso.com
+ * Copyright 2005, Verso Technologies Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/epan.h>
+#include <epan/packet.h>
+#include "epan/filesystem.h"
+#include <epan/tap.h>
+#include <epan/stat_cmd_args.h>
+#include <epan/to_str.h>
+#include <epan/address.h>
+#include <epan/addr_resolv.h>
+#include <epan/dissectors/packet-h248.h>
+
+#include "../globals.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/graph_analysis.h"
+#include "ui/gtk/voip_calls_dlg.h"
+#include "ui/gtk/voip_calls.h"
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/stock_icons.h"
+
+#include "simple_dialog.h"
+
+#ifdef HAVE_LIBPORTAUDIO
+#include "ui/gtk/rtp_analysis.h"
+#include "ui/gtk/rtp_player.h"
+#endif /* HAVE_LIBPORTAUDIO */
+
+#include "ui/gtk/old-gtk-compat.h"
+
+/****************************************************************************/
+/* pointer to the one and only dialog window */
+static GtkWidget *voip_calls_dlg = NULL;
+
+static GtkListStore *list_store = NULL;
+static GtkTreeIter list_iter;
+static GtkWidget *list = NULL;
+
+static GtkWidget *top_label = NULL;
+static GtkWidget *status_label = NULL;
+
+/*static GtkWidet *bt_unselect = NULL;*/
+static GtkWidget *bt_filter = NULL;
+static GtkWidget *bt_graph = NULL;
+#ifdef HAVE_LIBPORTAUDIO
+static GtkWidget *bt_player = NULL;
+#endif /* HAVE_LIBPORTAUDIO */
+
+static guint32 calls_nb = 0; /* number of displayed calls */
+static guint32 calls_ns = 0; /* number of selected calls */
+
+static graph_analysis_data_t *graph_analysis_data = NULL;
+
+enum
+{
+ CALL_COL_START_TIME,
+ CALL_COL_STOP_TIME,
+ CALL_COL_INITIAL_SPEAKER,
+ CALL_COL_FROM,
+ CALL_COL_TO,
+ CALL_COL_PROTOCOL,
+ CALL_COL_PACKETS,
+ CALL_COL_STATE,
+ CALL_COL_COMMENTS,
+ CALL_COL_DATA,
+ NUM_COLS /* The number of columns */
+};
+
+
+/****************************************************************************/
+static void
+voip_calls_remove_tap_listener(void)
+{
+ /* Remove the calls tap listener */
+ remove_tap_listener_sip_calls();
+ remove_tap_listener_isup_calls();
+ remove_tap_listener_mtp3_calls();
+ remove_tap_listener_h225_calls();
+ remove_tap_listener_h245dg_calls();
+ remove_tap_listener_q931_calls();
+ remove_tap_listener_h248_calls();
+ remove_tap_listener_sccp_calls();
+ remove_tap_listener_sdp_calls();
+ remove_tap_listener_rtp();
+ if (find_tap_id("unistim")) { /* The plugin may be missing */
+ remove_tap_listener_unistim_calls();
+ }
+ if (find_tap_id("voip")) {
+ remove_tap_listener_voip_calls();
+ }
+ remove_tap_listener_rtp_event();
+ remove_tap_listener_mgcp_calls();
+ remove_tap_listener_actrace_calls();
+ remove_tap_listener_skinny_calls();
+ remove_tap_listener_iax2_calls();
+ remove_tap_listener_t38();
+}
+
+/****************************************************************************/
+/* CALLBACKS */
+/****************************************************************************/
+static void
+voip_calls_on_destroy(GObject *object _U_, gpointer user_data _U_)
+{
+ /* remove_tap_listeners */
+ voip_calls_remove_tap_listener();
+
+ /* Clean up memory used by calls tap */
+ voip_calls_dlg_reset(NULL);
+
+ /* Note that we no longer have a "VoIP Calls" dialog box. */
+ voip_calls_dlg = NULL;
+
+ /* Clean up graph analysis memory */
+ g_free(graph_analysis_data);
+ graph_analysis_data = NULL;
+}
+
+/****************************************************************************/
+static void
+voip_calls_on_unselect(GtkButton *button _U_, gpointer user_data _U_)
+{
+ /*gtk_widget_set_sensitive(bt_unselect, FALSE);*/
+ gtk_widget_set_sensitive(bt_filter, FALSE);
+ gtk_widget_set_sensitive(bt_graph, FALSE);
+#ifdef HAVE_LIBPORTAUDIO
+ gtk_widget_set_sensitive(bt_player, FALSE);
+#endif /* HAVE_LIBPORTAUDIO */
+}
+
+/****************************************************************************/
+static void
+voip_calls_on_filter(GtkButton *button _U_, gpointer user_data _U_)
+{
+ gchar *filter_string;
+ GString *filter_string_fwd;
+ const gchar *filter_prepend;
+ gboolean is_first = TRUE;
+ GList* lista;
+ GList* listb;
+ voip_calls_info_t *listinfo;
+ graph_analysis_item_t *gai;
+ size_t filter_length;
+ size_t max_filter_length = 2048; /* What's this based on ? */
+ int pos;
+
+ const sip_calls_info_t *sipinfo;
+ const isup_calls_info_t *isupinfo;
+ const h323_calls_info_t *h323info;
+ const h245_address_t *h245_add = NULL;
+ const gcp_ctx_t* ctx;
+
+ filter_string = g_strdup(gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget)));
+ filter_length = strlen(filter_string);
+ pos = (int)filter_length; /* we assume the filter won't be more than 2^31-1 octets long */
+ g_strstrip(filter_string);
+
+ if (strlen(filter_string) > 0)
+ filter_prepend = " or ";
+ else
+ filter_prepend = "";
+ g_free(filter_string);
+
+ filter_string_fwd = g_string_new(filter_prepend);
+
+ /* look in the Graph and get all the frame_num for this call */
+ g_string_append_printf(filter_string_fwd, "(");
+
+ /* Build a new filter based on frame numbers */
+ lista = g_list_first(voip_calls_get_info()->callsinfo_list);
+ while (lista) {
+ listinfo = lista->data;
+ if (listinfo->selected) {
+ listb = g_list_first(voip_calls_get_info()->graph_analysis->list);
+ while (listb) {
+ gai = listb->data;
+ if (gai->conv_num == listinfo->call_num) {
+ g_string_append_printf(filter_string_fwd, "%sframe.number == %u", is_first?"":" or ", gai->fd->num);
+ is_first = FALSE;
+ }
+ listb = g_list_next(listb);
+ }
+ }
+ lista = g_list_next(lista);
+ }
+
+ g_string_append_printf(filter_string_fwd, ")");
+ filter_length += filter_string_fwd->len;
+
+ if (filter_length < max_filter_length) {
+ gtk_editable_insert_text(GTK_EDITABLE(main_display_filter_widget), filter_string_fwd->str, -1, &pos);
+ } else {
+ g_string_free(filter_string_fwd, TRUE);
+ filter_string_fwd = g_string_new(filter_prepend);
+
+ g_string_append_printf(filter_string_fwd, "(");
+ is_first = TRUE;
+ /* Build a new filter based on protocol fields */
+ lista = g_list_first(voip_calls_get_info()->callsinfo_list);
+ while (lista) {
+ listinfo = lista->data;
+ if (listinfo->selected) {
+ if (!is_first)
+ g_string_append_printf(filter_string_fwd, " or ");
+ switch (listinfo->protocol) {
+ case VOIP_SIP:
+ sipinfo = listinfo->prot_info;
+ g_string_append_printf(filter_string_fwd,
+ "(sip.Call-ID == \"%s\")",
+ sipinfo->call_identifier
+ );
+ break;
+ case VOIP_ISUP:
+ isupinfo = listinfo->prot_info;
+ g_string_append_printf(filter_string_fwd,
+ "(isup.cic == %i and frame.number >= %i and frame.number <= %i and mtp3.network_indicator == %i and ((mtp3.dpc == %i) and (mtp3.opc == %i)) or ((mtp3.dpc == %i) and (mtp3.opc == %i)))",
+ isupinfo->cic, listinfo->start_fd->num,
+ listinfo->stop_fd->num,
+ isupinfo->ni, isupinfo->dpc, isupinfo->opc,
+ isupinfo->opc, isupinfo->dpc
+ );
+ break;
+ case VOIP_H323:
+ h323info = listinfo->prot_info;
+ g_string_append_printf(filter_string_fwd,
+ "((h225.guid == %s || q931.call_ref == %x:%x || q931.call_ref == %x:%x)",
+ guid_to_str(&h323info->guid[0]),
+ (guint8) (h323info->q931_crv & 0x00ff),
+ (guint8)((h323info->q931_crv & 0xff00)>>8),
+ (guint8) (h323info->q931_crv2 & 0x00ff),
+ (guint8)((h323info->q931_crv2 & 0xff00)>>8));
+ listb = g_list_first(h323info->h245_list);
+ while (listb) {
+ h245_add = listb->data;
+ g_string_append_printf(filter_string_fwd,
+ " || (ip.addr == %s && tcp.port == %d && h245)",
+ ip_to_str((guint8 *)(h245_add->h245_address.data)), h245_add->h245_port);
+ listb = g_list_next(listb);
+ }
+ g_string_append_printf(filter_string_fwd, ")");
+ break;
+ case TEL_H248:
+ ctx = listinfo->prot_info;
+ g_string_append_printf(filter_string_fwd,
+ "(h248.ctx == 0x%x)", ctx->id);
+ break;
+ default:
+ /* placeholder to assure valid display filter expression */
+ g_string_append_printf(filter_string_fwd,
+ "(frame)");
+ break;
+ }
+ is_first = FALSE;
+ }
+ lista = g_list_next(lista);
+ }
+
+ g_string_append_printf(filter_string_fwd, ")");
+ gtk_editable_insert_text(GTK_EDITABLE(main_display_filter_widget), filter_string_fwd->str, -1, &pos);
+ }
+
+ g_string_free(filter_string_fwd, TRUE);
+}
+
+/****************************************************************************/
+static void
+voip_calls_on_select_all(GtkButton *button _U_, gpointer user_data _U_)
+{
+ GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
+ gtk_tree_selection_select_all(selection);
+}
+
+/****************************************************************************/
+static void
+on_graph_bt_clicked(GtkButton *button _U_, gpointer user_data _U_)
+{
+ graph_analysis_item_t *gai;
+ GList* lista;
+ GList* listb;
+ voip_calls_info_t *listinfo;
+
+ /* reset the "display" parameter in graph analysis */
+ listb = g_list_first(voip_calls_get_info()->graph_analysis->list);
+ while (listb) {
+ gai = listb->data;
+ gai->display = FALSE;
+ listb = g_list_next(listb);
+ }
+
+ /* set the display for selected calls */
+ lista = g_list_first(voip_calls_get_info()->callsinfo_list);
+ while (lista) {
+ listinfo = lista->data;
+ if (listinfo->selected) {
+ listb = g_list_first(voip_calls_get_info()->graph_analysis->list);
+ while (listb) {
+ gai = listb->data;
+ if (gai->conv_num == listinfo->call_num) {
+ gai->display = TRUE;
+ }
+ listb = g_list_next(listb);
+ }
+ }
+ lista = g_list_next(lista);
+ }
+
+ /* create or refresh the graph windows */
+ if (graph_analysis_data->dlg.window == NULL)
+ graph_analysis_create(graph_analysis_data); /* create the window */
+ else
+ graph_analysis_update(graph_analysis_data); /* refresh it */
+}
+
+/****************************************************************************/
+#ifdef HAVE_LIBPORTAUDIO
+static void
+on_player_bt_clicked(GtkButton *button _U_, gpointer user_data _U_)
+{
+ rtp_player_init(voip_calls_get_info());
+}
+#endif /* HAVE_LIBPORTAUDIO */
+
+/****************************************************************************/
+/* when the user selects a row in the calls list */
+static gboolean
+voip_calls_mark_selected(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter, gpointer data)
+{
+ GtkTreeSelection *selection = (GtkTreeSelection *)data;
+ voip_calls_info_t* strinfo;
+
+ gtk_tree_model_get(model, iter, CALL_COL_DATA, &strinfo, -1);
+ strinfo->selected = gtk_tree_selection_iter_is_selected(selection, iter);
+
+ return FALSE;
+}
+
+static void
+voip_calls_on_select_row_cb(GtkTreeSelection *selection, gpointer data _U_)
+{
+ gchar label_text[80];
+
+ gtk_tree_model_foreach(GTK_TREE_MODEL(list_store), voip_calls_mark_selected, selection);
+
+ calls_ns = gtk_tree_selection_count_selected_rows(selection);
+
+ g_snprintf(label_text, 80,
+ "Detected %u VoIP %s. Selected %u %s.",
+ calls_nb,
+ plurality(calls_nb, "Call", "Calls"),
+ calls_ns,
+ plurality(calls_ns, "Call", "Calls"));
+ gtk_label_set_text(GTK_LABEL(top_label), label_text);
+
+ gtk_widget_set_sensitive(bt_filter, calls_ns ? TRUE : FALSE);
+ gtk_widget_set_sensitive(bt_graph, calls_ns ? TRUE : FALSE);
+#ifdef HAVE_LIBPORTAUDIO
+ gtk_widget_set_sensitive(bt_player, calls_ns ? TRUE : FALSE);
+#endif /* HAVE_LIBPORTAUDIO */
+}
+
+/****************************************************************************/
+/* Tree view constructors */
+/****************************************************************************/
+/* append a line to list */
+static void
+add_to_list_store(voip_calls_info_t* strinfo)
+{
+ gchar field[NUM_COLS][50];
+ isup_calls_info_t *isupinfo;
+ h323_calls_info_t *h323info;
+ gboolean flag = FALSE;
+
+ g_snprintf(field[CALL_COL_INITIAL_SPEAKER], 30, "%s", get_addr_name(&(strinfo->initial_speaker)));
+ g_snprintf(field[CALL_COL_FROM], 50, "%s", strinfo->from_identity);
+ g_snprintf(field[CALL_COL_TO], 50, "%s", strinfo->to_identity);
+ g_snprintf(field[CALL_COL_PROTOCOL], 15, "%s",
+ ((strinfo->protocol==VOIP_COMMON)&&strinfo->protocol_name)?
+ strinfo->protocol_name:voip_protocol_name[strinfo->protocol]);
+ g_snprintf(field[CALL_COL_STATE], 15, "%s", voip_call_state_name[strinfo->call_state]);
+
+ /* Add comments based on the protocol */
+ switch (strinfo->protocol) {
+ case VOIP_ISUP:
+ isupinfo = strinfo->prot_info;
+ g_snprintf(field[CALL_COL_COMMENTS],30, "%i-%i -> %i-%i", isupinfo->ni, isupinfo->opc,
+ isupinfo->ni, isupinfo->dpc);
+ break;
+ case VOIP_H323:
+ h323info = strinfo->prot_info;
+ if (strinfo->call_state == VOIP_CALL_SETUP)
+ flag = h323info->is_faststart_Setup;
+ else
+ if ((h323info->is_faststart_Setup == TRUE) && (h323info->is_faststart_Proc == TRUE))
+ flag = TRUE;
+ g_snprintf(field[CALL_COL_COMMENTS],35, "Tunneling: %s Fast Start: %s",
+ (h323info->is_h245Tunneling==TRUE?"ON":"OFF"),
+ (flag==TRUE?"ON":"OFF"));
+ break;
+ case VOIP_COMMON:
+ field[CALL_COL_COMMENTS][0]='\0';
+ if (strinfo->call_comment)
+ g_snprintf(field[CALL_COL_COMMENTS],30, "%s", strinfo->call_comment);
+ break;
+ default:
+ field[CALL_COL_COMMENTS][0]='\0';
+ }
+
+ /* Acquire an iterator */
+ gtk_list_store_append(list_store, &list_iter);
+
+ /* Fill the new row */
+ gtk_list_store_set(list_store, &list_iter,
+ CALL_COL_START_TIME, nstime_to_sec(&strinfo->start_fd->rel_ts),
+ CALL_COL_STOP_TIME, nstime_to_sec(&strinfo->stop_fd->rel_ts),
+ CALL_COL_INITIAL_SPEAKER, &field[CALL_COL_INITIAL_SPEAKER][0],
+ CALL_COL_FROM, &field[CALL_COL_FROM][0],
+ CALL_COL_TO, &field[CALL_COL_TO][0],
+ CALL_COL_PROTOCOL, &field[CALL_COL_PROTOCOL][0],
+ CALL_COL_PACKETS, strinfo->npackets,
+ CALL_COL_STATE, &field[CALL_COL_STATE][0],
+ CALL_COL_COMMENTS, &field[CALL_COL_COMMENTS][0],
+ CALL_COL_DATA, strinfo,
+ -1);
+
+ calls_nb += 1;
+}
+
+/****************************************************************************/
+/* Create list view */
+static void
+create_list_view(void)
+{
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSortable *sortable;
+ GtkTreeView *list_view;
+ GtkTreeSelection *selection;
+
+ /* Create the store */
+ list_store = gtk_list_store_new(NUM_COLS, /* Total number of columns XXX */
+ G_TYPE_DOUBLE, /* Start Time */
+ G_TYPE_DOUBLE, /* Stop Time */
+ G_TYPE_STRING, /* Initial Speaker */
+ G_TYPE_STRING, /* From */
+ G_TYPE_STRING, /* To */
+ G_TYPE_STRING, /* Protocol */
+ G_TYPE_UINT, /* Packets */
+ G_TYPE_STRING, /* State */
+ G_TYPE_STRING, /* Comments */
+ G_TYPE_POINTER /* Data */
+ );
+
+ /* Create a view */
+ list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store));
+
+ /* The view now holds a reference. We can get rid of our own reference */
+ g_object_unref(G_OBJECT(list_store));
+
+ list_view = GTK_TREE_VIEW(list);
+ sortable = GTK_TREE_SORTABLE(list_store);
+
+ /* Speed up the list display */
+ gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
+
+ /* Setup the sortable columns */
+ gtk_tree_sortable_set_sort_column_id(sortable, CALL_COL_START_TIME, GTK_SORT_ASCENDING);
+ gtk_tree_view_set_headers_clickable(list_view, FALSE);
+
+ /* Start Time */
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL); /* right align */
+ g_object_set(G_OBJECT(renderer), "xpad", 10, NULL);
+ column = gtk_tree_view_column_new_with_attributes("Start Time", renderer,
+ "text", CALL_COL_START_TIME,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, CALL_COL_START_TIME);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+ gtk_tree_view_column_set_fixed_width(column, 100);
+ /* Add the column to the view. */
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Stop Time */
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL); /* right align */
+ g_object_set(G_OBJECT(renderer), "xpad", 10, NULL);
+ column = gtk_tree_view_column_new_with_attributes("Stop Time", renderer,
+ "text", CALL_COL_STOP_TIME,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, CALL_COL_STOP_TIME);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+ gtk_tree_view_column_set_fixed_width(column, 100);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Initial Speaker */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Initial Speaker", renderer,
+ "text", CALL_COL_INITIAL_SPEAKER,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, CALL_COL_INITIAL_SPEAKER);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 80);
+ gtk_tree_view_column_set_fixed_width(column, 120);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* From */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("From", renderer,
+ "text", CALL_COL_FROM,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, CALL_COL_FROM);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 130);
+ gtk_tree_view_column_set_fixed_width(column, 140);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* To */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("To", renderer,
+ "text", CALL_COL_TO,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, CALL_COL_TO);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 130);
+ gtk_tree_view_column_set_fixed_width(column, 140);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Protocol */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Protocol", renderer,
+ "text", CALL_COL_PROTOCOL,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, CALL_COL_PROTOCOL);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 50);
+ gtk_tree_view_column_set_fixed_width(column, 80);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Packets */
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL); /* right align numbers */
+ g_object_set(G_OBJECT(renderer), "xpad", 10, NULL);
+ column = gtk_tree_view_column_new_with_attributes("Packets", renderer,
+ "text", CALL_COL_PACKETS,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, CALL_COL_PACKETS);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 70);
+ gtk_tree_view_column_set_fixed_width(column, 80);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* State */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("State", renderer,
+ "text", CALL_COL_STATE,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, CALL_COL_STATE);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 60);
+ gtk_tree_view_column_set_fixed_width(column, 80);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Comments */
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes("Comments", renderer,
+ "text", CALL_COL_COMMENTS,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, CALL_COL_COMMENTS);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
+ gtk_tree_view_column_set_min_width(column, 100);
+ gtk_tree_view_column_set_fixed_width(column, 140);
+ gtk_tree_view_append_column(list_view, column);
+
+ /* Now enable the sorting of each column */
+ gtk_tree_view_set_rules_hint(list_view, TRUE);
+ gtk_tree_view_set_headers_clickable(list_view, TRUE);
+
+ /* Setup the selection handler */
+ selection = gtk_tree_view_get_selection(list_view);
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
+
+ g_signal_connect(G_OBJECT(selection), "changed", /* select_row */
+ G_CALLBACK(voip_calls_on_select_row_cb),
+ NULL);
+
+}
+
+/****************************************************************************/
+/* INTERFACE */
+/****************************************************************************/
+static void
+voip_calls_dlg_create(void)
+{
+ GtkWidget *voip_calls_dlg_w;
+ GtkWidget *main_vb;
+ GtkWidget *scrolledwindow;
+ GtkWidget *hbuttonbox;
+ GtkWidget *bt_close;
+ GtkWidget *bt_select_all;
+ const gchar *title_name_ptr;
+ gchar *win_name;
+
+ title_name_ptr = cf_get_display_name(&cfile);
+ win_name = g_strdup_printf("%s - VoIP Calls", title_name_ptr);
+ voip_calls_dlg_w = dlg_window_new(win_name); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent(GTK_WINDOW(voip_calls_dlg_w), TRUE);
+
+ gtk_window_set_default_size(GTK_WINDOW(voip_calls_dlg_w), 1000, 350);
+
+ main_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(voip_calls_dlg_w), main_vb);
+ gtk_container_set_border_width(GTK_CONTAINER (main_vb), 12);
+
+ top_label = gtk_label_new("Detected 0 VoIP Calls. Selected 0 Calls.");
+ gtk_box_pack_start(GTK_BOX (main_vb), top_label, FALSE, FALSE, 8);
+
+ scrolledwindow = scrolled_window_new(NULL, NULL);
+ gtk_box_pack_start(GTK_BOX (main_vb), scrolledwindow, TRUE, TRUE, 0);
+
+ create_list_view();
+ gtk_container_add(GTK_CONTAINER(scrolledwindow), list);
+ gtk_widget_show(voip_calls_dlg_w);
+
+ status_label = gtk_label_new("Total: Calls: 0 Start packets: 0 Completed calls: 0 Rejected calls: 0");
+ gtk_box_pack_start(GTK_BOX(main_vb), status_label, FALSE, FALSE, 8);
+
+ /* button row */
+ hbuttonbox = gtk_hbutton_box_new();
+ gtk_box_pack_start(GTK_BOX(main_vb), hbuttonbox, FALSE, FALSE, 0);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_SPREAD);
+ gtk_box_set_spacing(GTK_BOX(hbuttonbox), 30);
+
+ /*bt_unselect = gtk_button_new_with_label ("Unselect");
+ gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_unselect);
+ gtk_widget_set_tooltip_text (bt_unselect, "Unselect this conversation");*/
+
+ bt_filter = gtk_button_new_from_stock(WIRESHARK_STOCK_PREPARE_FILTER);
+ gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_filter);
+ gtk_widget_set_tooltip_text(bt_filter, "Prepare a display filter of the selected conversation");
+
+ bt_graph = gtk_button_new_from_stock(WIRESHARK_STOCK_VOIP_FLOW);
+ gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_graph);
+ gtk_widget_show(bt_graph);
+ g_signal_connect(bt_graph, "clicked", G_CALLBACK(on_graph_bt_clicked), NULL);
+ gtk_widget_set_tooltip_text(bt_graph, "Show a flow graph of the selected calls.");
+
+#ifdef HAVE_LIBPORTAUDIO
+ bt_player = gtk_button_new_from_stock(WIRESHARK_STOCK_AUDIO_PLAYER);
+ gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_player);
+ gtk_widget_show(bt_player);
+ g_signal_connect(bt_player, "clicked", G_CALLBACK(on_player_bt_clicked), NULL);
+ gtk_widget_set_tooltip_text(bt_player, "Launch the RTP player to listen the selected calls.");
+#endif /* HAVE_LIBPORTAUDIO */
+
+ bt_select_all = gtk_button_new_from_stock(GTK_STOCK_SELECT_ALL);
+ gtk_container_add(GTK_CONTAINER(hbuttonbox), bt_select_all);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(bt_select_all, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(bt_select_all, GTK_CAN_DEFAULT);
+#endif
+ gtk_widget_set_tooltip_text(bt_select_all, "Select all the calls");
+
+ bt_close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+ gtk_container_add(GTK_CONTAINER (hbuttonbox), bt_close);
+#if GTK_CHECK_VERSION(2,18,0)
+ gtk_widget_set_can_default(bt_close, TRUE);
+#else
+ GTK_WIDGET_SET_FLAGS(bt_close, GTK_CAN_DEFAULT);
+#endif
+ gtk_widget_set_tooltip_text(bt_close, "Close this dialog");
+
+ /*g_signal_connect(bt_unselect, "clicked", G_CALLBACK(voip_calls_on_unselect), NULL);*/
+ g_signal_connect(bt_filter, "clicked", G_CALLBACK(voip_calls_on_filter), NULL);
+
+ window_set_cancel_button(voip_calls_dlg_w, bt_close, window_cancel_button_cb);
+
+ g_signal_connect(voip_calls_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(voip_calls_dlg_w, "destroy", G_CALLBACK(voip_calls_on_destroy), NULL);
+ g_signal_connect(bt_select_all, "clicked", G_CALLBACK(voip_calls_on_select_all), NULL);
+
+ gtk_widget_show_all(voip_calls_dlg_w);
+ window_present(voip_calls_dlg_w);
+
+ voip_calls_on_unselect(NULL, NULL);
+
+ voip_calls_dlg = voip_calls_dlg_w;
+
+ g_free(win_name);
+}
+
+
+/****************************************************************************/
+/* PUBLIC */
+/****************************************************************************/
+
+/****************************************************************************/
+/* update the contents of the list view */
+/* list: pointer to list of voip_calls_info_t* */
+void
+voip_calls_dlg_update(GList *listx)
+{
+ gchar label_text[256];
+ if (voip_calls_dlg != NULL) {
+ calls_nb = 0;
+ calls_ns = 0;
+ gtk_list_store_clear(list_store);
+
+ g_snprintf(label_text, sizeof(label_text),
+ "Total: Calls: %u Start packets: %u Completed calls: %u Rejected calls: %u",
+ g_list_length(voip_calls_get_info()->callsinfo_list),
+ voip_calls_get_info()->start_packets,
+ voip_calls_get_info()->completed_calls,
+ voip_calls_get_info()->rejected_calls);
+ gtk_label_set_text(GTK_LABEL(status_label), label_text);
+
+ listx = g_list_first(listx);
+ while (listx) {
+ add_to_list_store((voip_calls_info_t*)(listx->data));
+ listx = g_list_next(listx);
+ }
+
+ g_snprintf(label_text, sizeof(label_text),
+ "Detected %u VoIP %s. Selected %u %s.",
+ calls_nb,
+ plurality(calls_nb, "Call", "Calls"),
+ calls_ns,
+ plurality(calls_ns, "Call", "Calls"));
+ gtk_label_set_text(GTK_LABEL(top_label), label_text);
+ }
+}
+
+/****************************************************************************/
+/* draw function for tap listeners to keep the window up to date */
+void
+voip_calls_dlg_draw(void *ptr _U_)
+{
+ if (voip_calls_get_info()->redraw) {
+ voip_calls_dlg_update(voip_calls_get_info()->callsinfo_list);
+ voip_calls_get_info()->redraw = FALSE;
+ }
+}
+
+/****************************************************************************/
+/* reset function for tap listeners to clear window, if necessary */
+void
+voip_calls_dlg_reset(void *ptr _U_)
+{
+ /* Clean up memory used by calls tap */
+ voip_calls_reset(voip_calls_get_info());
+
+ /* close the graph window if open */
+ if (graph_analysis_data && graph_analysis_data->dlg.window != NULL) {
+ window_cancel_button_cb(NULL, graph_analysis_data->dlg.window);
+ graph_analysis_data->dlg.window = NULL;
+ }
+}
+
+/****************************************************************************/
+/* init function for tap */
+static void
+voip_calls_init_tap(const char *dummy _U_, void* userdata _U_)
+{
+ if (graph_analysis_data == NULL) {
+ graph_analysis_data_init();
+ /* init the Graph Analysys */
+ graph_analysis_data = graph_analysis_init();
+ graph_analysis_data->graph_info = voip_calls_get_info()->graph_analysis;
+ }
+
+ /* Clean up memory used by calls tap */
+ voip_calls_reset(voip_calls_get_info());
+
+ /* Register the tap listener */
+ sip_calls_init_tap();
+ mtp3_calls_init_tap();
+ isup_calls_init_tap();
+ h225_calls_init_tap();
+ h245dg_calls_init_tap();
+ q931_calls_init_tap();
+ h248_calls_init_tap();
+ sccp_calls_init_tap();
+ sdp_calls_init_tap();
+ /* We don't register this tap, if we don't have the unistim plugin loaded.*/
+ if (find_tap_id("unistim")) {
+ unistim_calls_init_tap();
+ }
+ if (find_tap_id("voip")) {
+ VoIPcalls_init_tap();
+ }
+ rtp_init_tap();
+ rtp_event_init_tap();
+ mgcp_calls_init_tap();
+ actrace_calls_init_tap();
+ skinny_calls_init_tap();
+ iax2_calls_init_tap();
+ t38_init_tap();
+
+ /* create dialog box if necessary */
+ if (voip_calls_dlg == NULL) {
+ voip_calls_dlg_create();
+ } else {
+ /* There's already a dialog box; reactivate it. */
+ reactivate_window(voip_calls_dlg);
+ }
+
+ voip_calls_get_info()->redraw = TRUE;
+ voip_calls_dlg_draw(NULL);
+ voip_calls_get_info()->redraw = TRUE;
+
+ /* Scan for VoIP calls calls (redissect all packets) */
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(voip_calls_dlg));
+ /* Tap listener will be removed and cleaned up in voip_calls_on_destroy */
+}
+
+
+/****************************************************************************/
+/* entry point when called via the GTK menu */
+void
+voip_calls_launch(GtkAction *action _U_, gpointer user_data _U_)
+{
+ voip_calls_init_tap("", NULL);
+}
+
+/****************************************************************************/
+void
+register_tap_listener_voip_calls_dlg(void)
+{
+ register_stat_cmd_arg("voip,calls", voip_calls_init_tap, NULL);
+}
+
diff --git a/ui/gtk/voip_calls_dlg.h b/ui/gtk/voip_calls_dlg.h
new file mode 100644
index 0000000000..2b80493c57
--- /dev/null
+++ b/ui/gtk/voip_calls_dlg.h
@@ -0,0 +1,52 @@
+/* voip_calls_dlg.h
+ * VoIP conversations addition for Wireshark
+ *
+ * $Id$
+ *
+ * Copyright 2004, Ericsson , Spain
+ * By Francisco Alcoba <francisco.alcoba@ericsson.com>
+ *
+ * based on h323_conversations_dlg.h
+ * Copyright 2004, Iskratel, Ltd, Kranj
+ * By Miha Jemec <m.jemec@iskratel.si>
+ *
+ * H323, RTP and Graph Support
+ * By Alejandro Vaquero, alejandro.vaquero@verso.com
+ * Copyright 2005, Verso Technologies Inc.
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __VOIP_CALLS_DLG_H__
+#define __VOIP_CALLS_DLG_H__
+
+#include <gtk/gtk.h>
+
+/**
+ * Update the contents of the dialog box clist with that of list.
+ *
+ * @param list pointer to list of rtp_stream_info_t*
+ */
+void voip_calls_dlg_update(GList *list);
+
+/* functions for tap_listeners in voip_calls.c */
+void voip_calls_dlg_draw(void *ptr);
+void voip_calls_dlg_reset(void *ptr);
+
+#endif /* __VOIP_CALLS_DLG_H__ */
diff --git a/ui/gtk/webbrowser.c b/ui/gtk/webbrowser.c
new file mode 100644
index 0000000000..6ffaf61d8a
--- /dev/null
+++ b/ui/gtk/webbrowser.c
@@ -0,0 +1,520 @@
+/* The GIMP -- an image manipulation program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * Web Browser Plug-in
+ * Copyright (C) 2003 Henrik Brix Andersen <brix@gimp.org>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Wireshark - this file is copied from "The GIMP" V2.0.2
+ * You will find the original file in the gimp distribution zip under:
+ * \plug-ins\common\webbrowser.c
+ *
+ * It was modified to suit the Wireshark environment (#if 0)!
+ *
+ * For the UNIX+X11 launcher, see this blog post:
+ *
+ * http://blogs.gnome.org/timj/2006/11/24/24112006-how-to-start-a-web-browser/
+ *
+ * for a discussion of how Beast launches a browser, a link that shows
+ * the rather complicated code it uses, and some information on why it
+ * goes through all that pain. See also Kevin Krammer's comment, which
+ * notes that the problem might be that the GNOME, KDE, and XFCE
+ * launcher programs always cause the window to be opened in the background,
+ * regardless of whether an instance of the app is running or not (the
+ * app gets launched - in the background - if it's not already running,
+ * and is told to open a new window/tab if it's already running), while
+ * launchers such as sensible-browser, which xdg-open falls back to,
+ * launch the app in the foreground if it's not already running, leading
+ * to the "first window is in the foreground, subsequent windows are in
+ * the background" behavior in non-GNOME/KDE/XFCE environments.
+ *
+ * Perhaps the right strategy is to:
+ *
+ * Check whether we're in a GNOME/KDE/XFCE session and, if
+ * we are, try xdg-open, as it works around, among other things,
+ * some kfmclient bugs, and run it synchronously (that will fail
+ * if we detect a GNOME/KDE/XFCE session but the launcher is
+ * missing, but so it goes). If we don't have xdg-open, try
+ * the appropriate launcher for the environment, but ignore
+ * the return code from kfmclient, as it might be bogus (that's
+ * the bug xdg-open works around).
+ *
+ * Otherwise, try the "broken/unpredictable browser launchers",
+ * but run them in the background and leave them running, and
+ * ignore the exit code, and then try x-www-browser, and then
+ * try directly launching a user-specified browser. (Beast tries
+ * a bunch of browsers, with the user not being allowed to
+ * specify which one they want.)
+ *
+ * On the other hand, see bug 2699, in which xdg-open is itself buggy.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <string.h> /* strlen, strstr */
+
+#include <gtk/gtk.h>
+
+#include <epan/filesystem.h>
+#include <epan/prefs.h>
+
+#include "../simple_dialog.h"
+
+#include "ui/gtk/webbrowser.h"
+
+#if defined(G_OS_WIN32)
+/* Win32 - use Windows shell services to start a browser */
+#include <windows.h>
+/* We're using Unicode */
+#include <tchar.h>
+#include <wsutil/unicode-utils.h>
+/* if WIN32_LEAN_AND_MEAN is defined, shellapi.h is needed too */
+#include <shellapi.h>
+#elif defined (HAVE_OS_X_FRAMEWORKS)
+/* Mac OS X - use Launch Services to start a browser */
+#include <CoreFoundation/CoreFoundation.h>
+#include <ApplicationServices/ApplicationServices.h>
+#elif defined(HAVE_XDG_OPEN)
+/* UNIX+X11 desktop with Portland Group stuff - use xdg-open to start a browser */
+#else
+/* Everything else - launch the browser ourselves */
+#define MUST_LAUNCH_BROWSER_OURSELVES
+#endif
+
+#ifdef MUST_LAUNCH_BROWSER_OURSELVES
+static gchar* strreplace (const gchar *string,
+ const gchar *delimiter,
+ const gchar *replacement);
+#endif
+
+gboolean
+browser_needs_pref(void)
+{
+#ifdef MUST_LAUNCH_BROWSER_OURSELVES
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+
+gboolean
+browser_open_url (const gchar *url)
+{
+#if defined(G_OS_WIN32)
+
+ return ((gint) ShellExecute (HWND_DESKTOP, _T("open"), utf_8to16(url), NULL, NULL, SW_SHOWNORMAL) > 32);
+
+#elif defined(HAVE_OS_X_FRAMEWORKS)
+
+ CFStringRef url_CFString;
+ CFURLRef url_CFURL;
+ OSStatus status;
+
+ /*
+ * XXX - if URLs passed to "browser_open_url()" contain non-ASCII
+ * characters, we'd have to choose an appropriate value from the
+ * CFStringEncodings enum.
+ */
+ url_CFString = CFStringCreateWithCString(NULL, url, kCFStringEncodingASCII);
+ if (url_CFString == NULL)
+ return (FALSE);
+ url_CFURL = CFURLCreateWithString(NULL, url_CFString, NULL);
+ CFRelease(url_CFString);
+ if (url_CFURL == NULL) {
+ /*
+ * XXX - this could mean that the url_CFString wasn't a valid URL,
+ * or that memory allocation failed. We can't determine which,
+ * except perhaps by providing our own allocator and somehow
+ * flagging allocation failures.
+ */
+ return (FALSE);
+ }
+ /*
+ * XXX - this is a Launch Services result code, and we should probably
+ * display a dialog box if it's not 0, describing what the error was.
+ * Then again, we should probably do the same for the ShellExecute call,
+ * unless that call itself happens to pop up a dialog box for all errors.
+ */
+ status = LSOpenCFURLRef(url_CFURL, NULL);
+ CFRelease(url_CFURL);
+ return (status == 0);
+
+#elif defined(HAVE_XDG_OPEN)
+
+ GError *error = NULL;
+ gchar *argv[3];
+ gboolean retval;
+
+ g_return_val_if_fail (url != NULL, FALSE);
+
+ argv[0] = "xdg-open";
+ argv[1] = (char *)url; /* Grr - g_spawn_async() shouldn't modify this */
+ argv[2] = NULL;
+
+ /*
+ * XXX - use g_spawn_on_screen() so the browser window shows up on
+ * the same screen?
+ */
+ retval = g_spawn_async (NULL, argv, NULL,
+ G_SPAWN_SEARCH_PATH,
+ NULL, NULL,
+ NULL, &error);
+
+ if (! retval)
+ {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "%sCould not execute xdg-open: %s\n\n\"%s\"",
+ simple_dialog_primary_start(), simple_dialog_primary_end(),
+ error->message);
+ g_error_free (error);
+ }
+
+ return retval;
+
+#elif defined(MUST_LAUNCH_BROWSER_OURSELVES)
+
+ GError *error = NULL;
+ gchar *browser;
+ gchar *argument;
+ gchar *cmd;
+ gchar **argv;
+ gboolean retval;
+
+ g_return_val_if_fail (url != NULL, FALSE);
+
+ /* browser = gimp_gimprc_query ("web-browser");*/
+ browser = g_strdup(prefs.gui_webbrowser);
+
+ if (browser == NULL || ! strlen (browser))
+ {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Web browser not specified.\n"
+ "Please correct the web browser setting in the Preferences dialog.");
+ g_free (browser);
+ return FALSE;
+ }
+
+ /* quote the url since it might contains special chars */
+ argument = g_shell_quote (url);
+
+ /* replace %s with URL */
+ if (strstr (browser, "%s"))
+ cmd = strreplace (browser, "%s", argument);
+ else
+ cmd = g_strconcat (browser, " ", argument, NULL);
+
+ g_free (argument);
+
+ /* parse the cmd line */
+ if (! g_shell_parse_argv (cmd, NULL, &argv, &error))
+ {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "%sCould not parse web browser command: \"%s\"%s\n\n\"%s\"\n\n%s",
+ simple_dialog_primary_start(), browser, simple_dialog_primary_end(),
+ error->message,
+ "Please correct the web browser setting in the Preferences dialog.");
+ g_error_free (error);
+ return FALSE;
+ }
+
+ /*
+ * XXX - use g_spawn_on_screen() so the browser window shows up on
+ * the same screen?
+ */
+ retval = g_spawn_async (NULL, argv, NULL,
+ G_SPAWN_SEARCH_PATH,
+ NULL, NULL,
+ NULL, &error);
+
+ if (! retval)
+ {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "%sCould not execute web browser: \"%s\"%s\n\n\"%s\"\n\n%s",
+ simple_dialog_primary_start(), browser, simple_dialog_primary_end(),
+ error->message,
+ "Please correct the web browser setting in the Preferences dialog.");
+ g_error_free (error);
+ }
+
+ g_free (browser);
+ g_free (cmd);
+ g_strfreev (argv);
+
+ return retval;
+#endif
+}
+
+/** Convert local absolute path to uri.
+ *
+ * @param filename to (absolute pathed) filename to convert
+ * @return a newly allocated uri, you must g_free it later
+ */
+gchar *
+filename2uri(const gchar *filename)
+{
+ int i = 0;
+ gchar *file_tmp;
+ GString *filestr;
+
+
+ filestr = g_string_sized_new(200);
+
+ /* this escaping is somewhat slow but should working fine */
+ for(i=0; filename[i]; i++) {
+ switch(filename[i]) {
+ case(' '):
+ g_string_append(filestr, "%20");
+ break;
+ case('%'):
+ g_string_append(filestr, "%%");
+ break;
+ case('\\'):
+ g_string_append_c(filestr, '/');
+ break;
+ /* XXX - which other chars need to be escaped? */
+ default:
+ g_string_append_c(filestr, filename[i]);
+ }
+ }
+
+
+ /* prepend URI header "file:" appropriate for the system */
+#ifdef G_OS_WIN32
+ /* XXX - how do we handle UNC names (e.g. //servername/sharename/dir1/dir2/capture-file.cap) */
+ g_string_prepend(filestr, "file:///");
+#else
+ g_string_prepend(filestr, "file://");
+#endif
+
+ file_tmp = filestr->str;
+
+ g_string_free(filestr, FALSE /* don't free segment data */);
+
+ return file_tmp;
+}
+
+gboolean
+filemanager_open_directory (const gchar *path)
+{
+#if defined(G_OS_WIN32)
+ /* ShellExecute(...,"explore",...) needs path to be explicitly a directory;
+ Otherwise 'explore' will fail if a file exists with a basename matching
+ the provided directory path.
+ (eg: wireshak-gtk2.exe exists in the same directory as a wireshark-gtk2
+ directory entry).
+ */
+ gint ret;
+ gchar *xpath;
+ xpath = g_strconcat(path,
+ g_str_has_suffix(path, "\\") ? "" : "\\",
+ NULL);
+ ret = (gint) ShellExecute (HWND_DESKTOP, _T("explore"), utf_8to16(xpath), NULL, NULL, SW_SHOWNORMAL);
+ g_free(xpath);
+ return (ret > 32);
+
+#elif defined(HAVE_OS_X_FRAMEWORKS)
+
+ CFStringRef path_CFString;
+ CFURLRef path_CFURL;
+ OSStatus status;
+
+ path_CFString = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8);
+ if (path_CFString == NULL)
+ return (FALSE);
+ path_CFURL = CFURLCreateWithFileSystemPath(NULL, path_CFString,
+ kCFURLPOSIXPathStyle, true);
+ CFRelease(path_CFString);
+ if (path_CFURL == NULL) {
+ /*
+ * XXX - does this always mean that that memory allocation failed?
+ */
+ return (FALSE);
+ }
+ /*
+ * XXX - this is a Launch Services result code, and we should probably
+ * display a dialog box if it's not 0, describing what the error was.
+ * Then again, we should probably do the same for the ShellExecute call,
+ * unless that call itself happens to pop up a dialog box for all errors.
+ */
+ status = LSOpenCFURLRef(path_CFURL, NULL);
+ CFRelease(path_CFURL);
+ return (status == 0);
+
+#elif defined(HAVE_XDG_OPEN)
+
+ GError *error = NULL;
+ gchar *argv[3];
+ gboolean retval;
+
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ argv[0] = "xdg-open";
+ argv[1] = (char *)path; /* Grr - g_spawn_async() shouldn't modify this */
+ argv[2] = NULL;
+
+ /*
+ * XXX - use g_spawn_on_screen() so the file managaer window shows up on
+ * the same screen?
+ */
+ retval = g_spawn_async (NULL, argv, NULL,
+ G_SPAWN_SEARCH_PATH,
+ NULL, NULL,
+ NULL, &error);
+
+ if (! retval)
+ {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "%sCould not execute xdg-open: %s\n\n\"%s\"",
+ simple_dialog_primary_start(), simple_dialog_primary_end(),
+ error->message);
+ g_error_free (error);
+ }
+
+ return retval;
+
+#elif defined(MUST_LAUNCH_BROWSER_OURSELVES)
+
+ GError *error = NULL;
+ gchar *browser;
+ gchar *argument;
+ gchar *cmd;
+ gchar **argv;
+ gboolean retval;
+
+ g_return_val_if_fail (path != NULL, FALSE);
+
+ /* browser = gimp_gimprc_query ("web-browser");*/
+ browser = g_strdup(prefs.gui_webbrowser);
+
+ if (browser == NULL || ! strlen (browser))
+ {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "Web browser not specified.\n"
+ "Please correct the web browser setting in the Preferences dialog.");
+ g_free (browser);
+ return FALSE;
+ }
+
+ /* conver the path to a URI */
+ argument = filename2uri (path);
+
+ /* replace %s with URL */
+ if (strstr (browser, "%s"))
+ cmd = strreplace (browser, "%s", argument);
+ else
+ cmd = g_strconcat (browser, " ", argument, NULL);
+
+ g_free (argument);
+
+ /* parse the cmd line */
+ if (! g_shell_parse_argv (cmd, NULL, &argv, &error))
+ {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "%sCould not parse web browser command: \"%s\"%s\n\n\"%s\"\n\n%s",
+ simple_dialog_primary_start(), browser, simple_dialog_primary_end(),
+ error->message,
+ "Please correct the web browser setting in the Preferences dialog.");
+ g_error_free (error);
+ return FALSE;
+ }
+
+ /*
+ * XXX - use g_spawn_on_screen() so the browser window shows up on
+ * the same screen?
+ */
+ retval = g_spawn_async (NULL, argv, NULL,
+ G_SPAWN_SEARCH_PATH,
+ NULL, NULL,
+ NULL, &error);
+
+ if (! retval)
+ {
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK,
+ "%sCould not execute web browser: \"%s\"%s\n\n\"%s\"\n\n%s",
+ simple_dialog_primary_start(), browser, simple_dialog_primary_end(),
+ error->message,
+ "Please correct the web browser setting in the Preferences dialog.");
+ g_error_free (error);
+ }
+
+ g_free (browser);
+ g_free (cmd);
+ g_strfreev (argv);
+
+ return retval;
+#endif
+}
+
+#ifdef MUST_LAUNCH_BROWSER_OURSELVES
+
+static gchar*
+strreplace (const gchar *string,
+ const gchar *delimiter,
+ const gchar *replacement)
+{
+ gchar *ret;
+ gchar **tmp;
+
+ g_return_val_if_fail (string != NULL, NULL);
+ g_return_val_if_fail (delimiter != NULL, NULL);
+ g_return_val_if_fail (replacement != NULL, NULL);
+
+ tmp = g_strsplit (string, delimiter, 0);
+ ret = g_strjoinv (replacement, tmp);
+ g_strfreev (tmp);
+
+ return ret;
+}
+
+#endif /* MUST_LAUNCH_BROWSER_OURSELVES */
+
+
+/* browse a file relative to the data dir */
+void
+browser_open_data_file(const gchar *filename)
+{
+ gchar *file_path;
+ gchar *uri;
+
+ /* build filename */
+#ifdef G_OS_WIN32
+ if((strlen(filename) > 2) && (filename[1] == ':'))
+ file_path = g_strdup(filename);
+#else
+ /* XXX: is this correct for MacOS/Linux ? */
+ if((strlen(filename) > 1) && (filename[0] == '/'))
+ file_path = g_strdup(filename);
+#endif
+ else
+
+ file_path = g_strdup_printf("%s/%s", get_datafile_dir(), filename);
+
+ /* XXX - check, if the file is really existing, otherwise display a simple_dialog about the problem */
+
+ /* convert filename to uri */
+ uri = filename2uri(file_path);
+
+ /* show the uri */
+ browser_open_url (uri);
+
+ g_free(file_path);
+ g_free(uri);
+}
diff --git a/ui/gtk/webbrowser.h b/ui/gtk/webbrowser.h
new file mode 100644
index 0000000000..09a70aa7a0
--- /dev/null
+++ b/ui/gtk/webbrowser.h
@@ -0,0 +1,44 @@
+/* webbrowser.h
+ * Web browser activation functions
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __WEBBROWSER_H__
+#define __WEBBROWSER_H__
+
+extern gboolean browser_needs_pref(void);
+
+extern gboolean browser_open_url (const gchar *url);
+
+extern gboolean filemanager_open_directory (const gchar *path);
+
+/* browse a file relative to the data dir */
+extern void browser_open_data_file (const gchar *filename);
+
+/** Convert local absolute path to uri.
+ *
+ * @param filename to (absolute pathed) filename to convert
+ * @return a newly allocated uri, you must g_free it later
+ */
+extern gchar *filename2uri(const gchar *filename);
+
+#endif /* __WEBBROWSER_H__ */
diff --git a/ui/gtk/wlan_stat_dlg.c b/ui/gtk/wlan_stat_dlg.c
new file mode 100644
index 0000000000..ca61f793e5
--- /dev/null
+++ b/ui/gtk/wlan_stat_dlg.c
@@ -0,0 +1,1970 @@
+/* wlan_stat_dlg.c
+ * Copyright 2008 Stig Bjorlykke <stig@bjorlykke.org>
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/addr_resolv.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-ieee80211.h>
+#include <epan/strutil.h>
+
+#include "../simple_dialog.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gtkglobals.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/filter_utils.h"
+#include "ui/gtk/gui_stat_menu.h"
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/recent.h"
+#include "ui/gtk/help_dlg.h"
+#include "ui/gtk/main.h"
+#include "ui/gtk/utf8_entities.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+enum {
+ BSSID_COLUMN,
+ CHANNEL_COLUMN,
+ SSID_COLUMN,
+ PERCENT_COLUMN,
+ BEACONS_COLUMN,
+ DATA_COLUMN,
+ PROBE_REQ_COLUMN,
+ PROBE_RESP_COLUMN,
+ AUTH_COLUMN,
+ DEAUTH_COLUMN,
+ OTHER_COLUMN,
+ PROTECTION_COLUMN,
+ PERCENT_VALUE_COLUMN,
+ TABLE_COLUMN,
+ NUM_COLUMNS
+};
+
+static const gchar *titles[] = { "BSSID", "Ch.", "SSID", "% Packets", "Beacons", "Data Packets",
+ "Probe Req", "Probe Resp", "Auth", "Deauth", "Other", "Protection" };
+
+enum {
+ ADDRESS_COLUMN,
+ PERCENT_2_COLUMN,
+ DATA_SENT_COLUMN,
+ DATA_REC_COLUMN,
+ PROBE_REQ_2_COLUMN,
+ PROBE_RESP_2_COLUMN,
+ AUTH_2_COLUMN,
+ DEAUTH_2_COLUMN,
+ OTHER_2_COLUMN,
+ COMMENT_COLUMN,
+ PERCENT_VALUE_2_COLUMN,
+ DETAILS_COLUMN,
+ NUM_DETAIL_COLUMNS
+};
+
+static const gchar *detail_titles[] = { "Address", "% Packets", "Data Sent", "Data Received",
+ "Probe Req", "Probe Resp", "Auth", "Deauth", "Other", "Comment" };
+
+typedef struct wlan_details_ep {
+ struct wlan_details_ep *next;
+ address addr;
+ guint32 probe_req;
+ guint32 probe_rsp;
+ guint32 auth;
+ guint32 deauth;
+ guint32 data_sent;
+ guint32 data_received;
+ guint32 other;
+ guint32 number_of_packets;
+ GtkTreeIter iter;
+ gboolean iter_valid;
+} wlan_details_ep_t;
+
+typedef struct wlan_ep {
+ struct wlan_ep* next;
+ address bssid;
+ struct _wlan_stats stats;
+ guint32 type[256];
+ guint32 number_of_packets;
+ GtkTreeIter iter;
+ gboolean iter_valid;
+ gboolean probe_req_searched;
+ gboolean is_broadcast;
+ struct wlan_details_ep *details;
+} wlan_ep_t;
+
+static GtkWidget *wlanstat_dlg_w = NULL;
+static GtkWidget *wlanstat_pane = NULL;
+static GtkWidget *wlanstat_name_lb = NULL;
+static address broadcast;
+
+/* used to keep track of the statistics for an entire program interface */
+typedef struct _wlan_stat_t {
+ GtkTreeView *table;
+ GtkTreeView *details;
+ GtkWidget *menu;
+ GtkWidget *details_menu;
+ guint32 number_of_packets;
+ guint32 num_entries;
+ guint32 num_details;
+ gboolean resolve_names;
+ gboolean use_dfilter;
+ gboolean show_only_existing;
+ wlan_ep_t* ep_list;
+} wlanstat_t;
+
+static void
+dealloc_wlan_details_ep (wlan_details_ep_t *details)
+{
+ wlan_details_ep_t *tmp;
+
+ while (details) {
+ tmp = details;
+ details = details->next;
+ g_free (tmp);
+ }
+}
+
+static void
+wlanstat_reset (void *phs)
+{
+ wlanstat_t* wlan_stat = (wlanstat_t *)phs;
+ wlan_ep_t* list = wlan_stat->ep_list;
+ wlan_ep_t* tmp = NULL;
+ char title[256];
+ GString *error_string;
+ GtkListStore *store;
+ const char *filter = NULL;
+
+ if (wlanstat_dlg_w != NULL) {
+ g_snprintf (title, sizeof(title), "Wireshark: WLAN Traffic Statistics: %s",
+ cf_get_display_name(&cfile));
+ gtk_window_set_title(GTK_WINDOW(wlanstat_dlg_w), title);
+ }
+
+ if (wlan_stat->use_dfilter) {
+ filter = gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
+ }
+
+ error_string = set_tap_dfilter (wlan_stat, filter);
+ if (error_string) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free(error_string, TRUE);
+ return;
+ }
+
+ if (wlan_stat->use_dfilter) {
+ if (filter && strlen(filter)) {
+ g_snprintf(title, sizeof(title), "Network Overview - Filter: %s", filter);
+ } else {
+ g_snprintf(title, sizeof(title), "Network Overview - No Filter");
+ }
+ } else {
+ g_snprintf(title, sizeof(title), "Network Overview");
+ }
+ gtk_frame_set_label(GTK_FRAME(wlanstat_name_lb), title);
+
+ /* remove all entries from the list */
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(wlan_stat->table));
+ gtk_list_store_clear(store);
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(wlan_stat->details));
+ gtk_list_store_clear(store);
+
+ if (!list)
+ return;
+
+ while (list) {
+ tmp = list;
+ dealloc_wlan_details_ep (tmp->details);
+ list = tmp->next;
+ g_free (tmp);
+ }
+
+ wlan_stat->ep_list = NULL;
+ wlan_stat->number_of_packets = 0;
+}
+
+static void
+invalidate_detail_iters (wlanstat_t *hs)
+{
+ wlan_ep_t *ep = hs->ep_list;
+ wlan_details_ep_t *d_ep;
+
+ while (ep) {
+ d_ep = ep->details;
+ while (d_ep) {
+ d_ep->iter_valid = FALSE;
+ d_ep = d_ep->next;
+ }
+ ep = ep->next;
+ }
+}
+
+static wlan_ep_t*
+alloc_wlan_ep (struct _wlan_hdr *si, packet_info *pinfo _U_)
+{
+ wlan_ep_t* ep;
+
+ if (!si)
+ return NULL;
+
+ ep = g_malloc (sizeof(wlan_ep_t));
+
+ SE_COPY_ADDRESS (&ep->bssid, &si->bssid);
+ ep->stats.channel = si->stats.channel;
+ memcpy (ep->stats.ssid, si->stats.ssid, MAX_SSID_LEN);
+ ep->stats.ssid_len = si->stats.ssid_len;
+ g_strlcpy (ep->stats.protection, si->stats.protection, MAX_PROTECT_LEN);
+ memset(&ep->type, 0, sizeof (int) * 256);
+ ep->number_of_packets = 0;
+ ep->details = NULL;
+ ep->iter_valid = FALSE;
+ ep->probe_req_searched = FALSE;
+ ep->is_broadcast = FALSE;
+ ep->next = NULL;
+
+ return ep;
+}
+
+static wlan_details_ep_t*
+alloc_wlan_details_ep (address *addr)
+{
+ wlan_details_ep_t* d_ep;
+
+ if (!addr)
+ return NULL;
+
+ if (!(d_ep = g_malloc (sizeof(wlan_details_ep_t))))
+ return NULL;
+
+ SE_COPY_ADDRESS (&d_ep->addr, addr);
+ d_ep->probe_req = 0;
+ d_ep->probe_rsp = 0;
+ d_ep->auth = 0;
+ d_ep->deauth = 0;
+ d_ep->data_sent = 0;
+ d_ep->data_received = 0;
+ d_ep->other = 0;
+ d_ep->number_of_packets = 0;
+ d_ep->iter_valid = FALSE;
+ d_ep->next = NULL;
+
+ return d_ep;
+}
+
+static wlan_details_ep_t *
+get_details_ep (wlan_ep_t *te, address *addr)
+{
+ wlan_details_ep_t *tmp, *d_te = NULL;
+
+ if (!te->details) {
+ te->details = alloc_wlan_details_ep (addr);
+ d_te = te->details;
+ } else {
+ for (tmp = te->details; tmp; tmp = tmp->next) {
+ if (!CMP_ADDRESS (&tmp->addr, addr)) {
+ d_te = tmp;
+ break;
+ }
+ }
+
+ if (!d_te) {
+ if ((d_te = alloc_wlan_details_ep (addr)) != NULL) {
+ d_te->next = te->details;
+ te->details = d_te;
+ }
+ }
+ }
+
+ g_assert (d_te != NULL);
+
+ return d_te;
+}
+
+static void
+wlanstat_packet_details (wlan_ep_t *te, guint32 type, address *addr, gboolean src)
+{
+ wlan_details_ep_t *d_te = get_details_ep (te, addr);
+
+ switch (type) {
+ case 0x04:
+ d_te->probe_req++;
+ break;
+ case 0x05:
+ d_te->probe_rsp++;
+ break;
+ case 0x08:
+ /* No counting for beacons */
+ break;
+ case 0x0B:
+ d_te->auth++;
+ break;
+ case 0x0C:
+ d_te->deauth++;
+ break;
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x28:
+ case 0x29:
+ case 0x2A:
+ case 0x2B:
+ if (src) {
+ d_te->data_sent++;
+ } else {
+ d_te->data_received++;
+ }
+ break;
+ default:
+ d_te->other++;
+ break;
+ }
+
+ if (type != 0x08) {
+ /* Do not count beacons in details */
+ d_te->number_of_packets++;
+ }
+}
+
+static gboolean
+is_broadcast(address *addr)
+{
+#if 0
+ /* doesn't work if MAC resolution is disable */
+ return strcmp(get_addr_name(addr), "Broadcast") == 0;
+#endif
+ return ADDRESSES_EQUAL(&broadcast, addr);
+}
+
+static gboolean
+ssid_equal(struct _wlan_stats *st1, struct _wlan_stats *st2 )
+{
+ return st1->ssid_len == st2->ssid_len && memcmp (st1->ssid, st2->ssid, st1->ssid_len) == 0;
+}
+
+static int
+wlanstat_packet (void *phs, packet_info *pinfo, epan_dissect_t *edt _U_, const void *phi)
+{
+
+ wlanstat_t *hs = (wlanstat_t *)phs;
+ wlan_ep_t *tmp = NULL, *te = NULL;
+ struct _wlan_hdr *si = (struct _wlan_hdr *) phi;
+
+ if (!hs)
+ return (0);
+
+ hs->number_of_packets++;
+ if (!hs->ep_list) {
+ hs->ep_list = alloc_wlan_ep (si, pinfo);
+ te = hs->ep_list;
+ te->is_broadcast = is_broadcast(&si->bssid);
+ } else {
+ for (tmp = hs->ep_list; tmp; tmp = tmp->next) {
+ if (((si->type == 0x04 && (
+ (tmp->stats.ssid_len == 0 && si->stats.ssid_len == 0 && tmp->is_broadcast)
+ || (si->stats.ssid_len != 0 && ssid_equal(&tmp->stats, &si->stats))
+ )))
+ ||
+ (si->type != 0x04 && !CMP_ADDRESS (&tmp->bssid, &si->bssid))) {
+ te = tmp;
+ break;
+ }
+ }
+
+ if (!te) {
+ te = alloc_wlan_ep (si, pinfo);
+ te->is_broadcast = is_broadcast(&si->bssid);
+ te->next = hs->ep_list;
+ hs->ep_list = te;
+ }
+
+ if (!te->probe_req_searched && (si->type != 0x04) && (te->type[0x04] == 0) &&
+ (si->stats.ssid_len > 1 || si->stats.ssid[0] != 0)) {
+ /*
+ * We have found a matching entry without Probe Requests.
+ * Search the rest of the entries for a corresponding entry
+ * matching the SSID and BSSID == Broadcast.
+ *
+ * This is because we can have a hidden SSID or Probe Request
+ * before we have a Beacon, Association Request, etc.
+ */
+ wlan_ep_t *prev = NULL;
+ GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(hs->table));
+ te->probe_req_searched = TRUE;
+ for (tmp = hs->ep_list; tmp; tmp = tmp->next) {
+ if (tmp != te && tmp->is_broadcast && ssid_equal (&si->stats, &tmp->stats)) {
+ /*
+ * Found a matching entry. Merge with the previous
+ * found entry and remove from list.
+ */
+ te->type[0x04] += tmp->type[0x04];
+ te->number_of_packets += tmp->number_of_packets;
+
+ if (tmp->details && tmp->details->next) {
+ /* Adjust received probe requests */
+ wlan_details_ep_t *d_te;
+ d_te = get_details_ep (te, &tmp->details->addr);
+ d_te->probe_req += tmp->type[0x04];
+ d_te->number_of_packets += tmp->type[0x04];
+ d_te = get_details_ep (te, &tmp->details->next->addr);
+ d_te->probe_req += tmp->type[0x04];
+ d_te->number_of_packets += tmp->type[0x04];
+ }
+ if (prev) {
+ prev->next = tmp->next;
+ } else {
+ hs->ep_list = tmp->next;
+ }
+ dealloc_wlan_details_ep (tmp->details);
+ if (tmp->iter_valid) {
+ gtk_list_store_remove(store, &tmp->iter);
+ }
+ g_free (tmp);
+ break;
+ }
+ prev = tmp;
+ }
+ }
+ }
+
+ if(!te)
+ return (0);
+
+ if (te->stats.channel == 0 && si->stats.channel != 0) {
+ te->stats.channel = si->stats.channel;
+ }
+ if (te->stats.ssid[0] == 0 && si->stats.ssid_len != 0) {
+ memcpy (te->stats.ssid, si->stats.ssid, MAX_SSID_LEN);
+ te->stats.ssid_len = si->stats.ssid_len;
+ }
+ if (te->stats.protection[0] == 0 && si->stats.protection[0] != 0) {
+ g_strlcpy (te->stats.protection, si->stats.protection, MAX_PROTECT_LEN);
+ }
+ te->type[si->type]++;
+ te->number_of_packets++;
+
+ wlanstat_packet_details (te, si->type, &si->src, TRUE); /* Register source */
+ wlanstat_packet_details (te, si->type, &si->dst, FALSE); /* Register destination */
+
+ return (1);
+}
+
+static void
+wlanstat_draw_details(wlanstat_t *hs, wlan_ep_t *wlan_ep, gboolean clear)
+{
+ wlan_details_ep_t *tmp = NULL;
+ char addr[256], comment[256], percent[256];
+ gboolean broadcast_flag, basestation_flag;
+ float f;
+ GtkListStore *store;
+
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(hs->details));
+ if (clear) {
+ gtk_list_store_clear(store);
+ invalidate_detail_iters(hs);
+ }
+ hs->num_details = 0;
+
+ for(tmp = wlan_ep->details; tmp; tmp=tmp->next) {
+ broadcast_flag = is_broadcast(&tmp->addr);
+ basestation_flag = !broadcast_flag && !CMP_ADDRESS(&tmp->addr, &wlan_ep->bssid);
+
+ if ((wlan_ep->number_of_packets - wlan_ep->type[0x08]) > 0) {
+ f = (float)(((float)tmp->number_of_packets * 100.0) / (wlan_ep->number_of_packets - wlan_ep->type[0x08]));
+ } else {
+ f = 0.0f;
+ }
+
+ if (hs->resolve_names) {
+ g_strlcpy (addr, get_addr_name(&tmp->addr), sizeof(addr));
+ } else {
+ g_strlcpy (addr, ep_address_to_str(&tmp->addr), sizeof(addr));
+ }
+ if (basestation_flag) {
+ g_strlcpy (comment, "Base station", sizeof(comment));
+ } else {
+ g_strlcpy (comment, " ", sizeof(comment));
+ }
+ g_snprintf (percent, sizeof(percent), "%.2f %%", f);
+
+ if (!tmp->iter_valid) {
+ gtk_list_store_append(store, &tmp->iter);
+ tmp->iter_valid = TRUE;
+ }
+ gtk_list_store_set(store, &tmp->iter,
+ ADDRESS_COLUMN, addr,
+ PERCENT_2_COLUMN, percent,
+ DATA_SENT_COLUMN, tmp->data_sent,
+ DATA_REC_COLUMN, tmp->data_received,
+ PROBE_REQ_2_COLUMN, tmp->probe_req,
+ PROBE_RESP_2_COLUMN, tmp->probe_rsp,
+ AUTH_2_COLUMN, tmp->auth,
+ DEAUTH_2_COLUMN, tmp->deauth,
+ OTHER_2_COLUMN, tmp->other,
+ COMMENT_COLUMN, comment,
+ PERCENT_VALUE_2_COLUMN, f,
+ DETAILS_COLUMN, tmp,
+ -1);
+
+ hs->num_details++;
+ }
+}
+
+static void
+wlanstat_draw(void *phs)
+{
+ wlanstat_t *hs = (wlanstat_t *)phs;
+ wlan_ep_t* list = hs->ep_list, *tmp = 0;
+ guint32 data = 0, other = 0;
+ char bssid[256], channel[256], ssid[256], percent[256];
+ float f;
+ GtkListStore *store;
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(hs->table));
+ hs->num_entries = 0;
+
+ for(tmp = list; tmp; tmp=tmp->next) {
+
+ if (hs->show_only_existing && tmp->is_broadcast) {
+ if (tmp->iter_valid) {
+ gtk_list_store_remove(store, &tmp->iter);
+ tmp->iter_valid = FALSE;
+ }
+ continue;
+ }
+
+ data = tmp->type[0x20] + tmp->type[0x21] + tmp->type[0x22] + tmp->type[0x23] +
+ tmp->type[0x28] + tmp->type[0x29] + tmp->type[0x2A] + tmp->type[0x2B];
+ other = tmp->number_of_packets - data - tmp->type[0x08] - tmp->type[0x04] -
+ tmp->type[0x05] - tmp->type[0x0B] - tmp->type[0x0C];
+ f = (float)(((float)tmp->number_of_packets * 100.0) / hs->number_of_packets);
+
+ if (hs->resolve_names) {
+ g_strlcpy (bssid, get_addr_name(&tmp->bssid), sizeof(bssid));
+ } else {
+ g_strlcpy (bssid, ep_address_to_str(&tmp->bssid), sizeof(bssid));
+ }
+ if (tmp->stats.channel) {
+ g_snprintf (channel, sizeof(channel), "%u", tmp->stats.channel);
+ } else {
+ channel[0] = '\0';
+ }
+ if (tmp->stats.ssid_len == 0) {
+ g_strlcpy (ssid, "<Broadcast>", sizeof(ssid));
+ } else if (tmp->stats.ssid_len == 1 && tmp->stats.ssid[0] == 0) {
+ g_strlcpy (ssid, "<Hidden>", sizeof(ssid));
+ } else {
+ g_strlcpy (ssid, format_text(tmp->stats.ssid, tmp->stats.ssid_len), sizeof(ssid));
+ }
+ g_snprintf (percent, sizeof(percent), "%.2f %%", f);
+
+ if (!tmp->iter_valid) {
+ gtk_list_store_append(store, &tmp->iter);
+ tmp->iter_valid = TRUE;
+ }
+ gtk_list_store_set (store, &tmp->iter,
+ BSSID_COLUMN, bssid,
+ CHANNEL_COLUMN, channel,
+ SSID_COLUMN, ssid,
+ PERCENT_COLUMN, percent,
+ BEACONS_COLUMN, tmp->type[0x08],
+ DATA_COLUMN, data,
+ PROBE_REQ_COLUMN, tmp->type[0x04],
+ PROBE_RESP_COLUMN, tmp->type[0x05],
+ AUTH_COLUMN, tmp->type[0x0B],
+ DEAUTH_COLUMN, tmp->type[0x0C],
+ OTHER_COLUMN, other,
+ PROTECTION_COLUMN, tmp->stats.protection,
+ PERCENT_VALUE_COLUMN, f,
+ TABLE_COLUMN, tmp,
+ -1);
+
+ hs->num_entries++;
+ }
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(hs->table));
+ if (gtk_tree_selection_get_selected (sel, &model, &iter)) {
+ wlan_ep_t *ep;
+
+ gtk_tree_model_get (model, &iter, TABLE_COLUMN, &ep, -1);
+ wlanstat_draw_details (hs, ep, FALSE);
+ }
+}
+
+/* What to do when a list item is selected/unselected */
+static void
+wlan_select_cb(GtkTreeSelection *sel, gpointer data)
+{
+ wlanstat_t *hs = (wlanstat_t *)data;
+ wlan_ep_t *ep;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected (sel, &model, &iter)) {
+ gtk_tree_model_get (model, &iter, TABLE_COLUMN, &ep, -1);
+ wlanstat_draw_details (hs, ep, TRUE);
+ }
+}
+
+
+static void
+wlan_resolve_toggle_dest(GtkWidget *widget, gpointer data)
+{
+ wlanstat_t *hs = (wlanstat_t *)data;
+
+ hs->resolve_names = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
+
+ wlanstat_draw(hs);
+}
+
+static void
+wlan_filter_toggle_dest(GtkWidget *widget, gpointer data)
+{
+ wlanstat_t *hs = (wlanstat_t *)data;
+
+ hs->use_dfilter = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(wlanstat_dlg_w));
+}
+
+static void
+wlan_existing_toggle_dest(GtkWidget *widget, gpointer data)
+{
+ wlanstat_t *hs = (wlanstat_t *)data;
+
+ hs->show_only_existing = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
+
+ wlanstat_draw(hs);
+}
+
+static gboolean
+csv_handle(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter,
+ gpointer data)
+{
+ GString *CSV_str = (GString *)data;
+ gchar *table_text;
+ gint table_value;
+ int i;
+
+ for (i=0; i<=PROTECTION_COLUMN; i++) {
+ if (i == BSSID_COLUMN || i == CHANNEL_COLUMN || i == SSID_COLUMN ||
+ i == PERCENT_COLUMN || i == PROTECTION_COLUMN) {
+ gtk_tree_model_get(model, iter, i, &table_text, -1);
+ g_string_append_printf(CSV_str, "\"%s\"", table_text);
+ g_free(table_text);
+ } else {
+ gtk_tree_model_get(model, iter, i, &table_value, -1);
+ g_string_append_printf(CSV_str, "\"%u\"", table_value);
+ }
+ if (i != PROTECTION_COLUMN)
+ g_string_append(CSV_str,",");
+ }
+ g_string_append(CSV_str,"\n");
+
+ return FALSE;
+}
+
+static void
+wlan_copy_as_csv(GtkWindow *win _U_, gpointer data)
+{
+ int i;
+ GString *CSV_str = g_string_new("");
+ GtkClipboard *cb;
+ GtkTreeView *tree_view = GTK_TREE_VIEW(data);
+ GtkListStore *store;
+
+ /* Add the column headers to the CSV data */
+ for (i=0; i<=PROTECTION_COLUMN; i++) {
+ g_string_append_printf(CSV_str, "\"%s\"", titles[i]);
+ if (i != PROTECTION_COLUMN)
+ g_string_append(CSV_str, ",");
+ }
+ g_string_append(CSV_str,"\n");
+
+ /* Add the column values to the CSV data */
+ store = GTK_LIST_STORE(gtk_tree_view_get_model(tree_view));
+ gtk_tree_model_foreach(GTK_TREE_MODEL(store), csv_handle, CSV_str);
+
+ /* Now that we have the CSV data, copy it into the default clipboard */
+ cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+ gtk_clipboard_set_text(cb, CSV_str->str, -1);
+ g_string_free(CSV_str, TRUE);
+}
+
+static void
+win_destroy_cb (GtkWindow *win _U_, gpointer data)
+{
+ wlanstat_t *hs = (wlanstat_t *)data;
+
+ protect_thread_critical_region ();
+ remove_tap_listener (hs);
+ unprotect_thread_critical_region ();
+
+ if (wlanstat_dlg_w != NULL) {
+ window_destroy(wlanstat_dlg_w);
+ wlanstat_dlg_w = NULL;
+ }
+ wlanstat_reset (hs);
+ g_free (hs);
+
+ recent.gui_geometry_wlan_stats_pane =
+ gtk_paned_get_position(GTK_PANED(wlanstat_pane));
+}
+
+/* Filter value */
+#define VALUE_BSSID_ONLY 0
+#define VALUE_SSID_ONLY 1
+#define VALUE_BSSID_AND_SSID 2
+#define VALUE_BSSID_OR_SSID 3
+
+static void
+wlan_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callback_action)
+{
+ int value;
+ wlanstat_t *hs=(wlanstat_t *)callback_data;
+ char *str = NULL;
+ wlan_ep_t *ep;
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(hs->table));
+ gtk_tree_selection_get_selected (sel, &model, &iter);
+ gtk_tree_model_get (model, &iter, TABLE_COLUMN, &ep, -1);
+
+ value = FILTER_EXTRA(callback_action);
+
+ switch (value) {
+ case VALUE_BSSID_ONLY:
+ str = g_strdup_printf("wlan.bssid==%s", ep_address_to_str(&ep->bssid));
+ break;
+ case VALUE_SSID_ONLY:
+ str = g_strdup_printf("wlan_mgt.ssid==\"%s\"", format_text(ep->stats.ssid, ep->stats.ssid_len));
+ break;
+ case VALUE_BSSID_AND_SSID:
+ str = g_strdup_printf("wlan.bssid==%s && wlan_mgt.ssid==\"%s\"",
+ ep_address_to_str(&ep->bssid), format_text(ep->stats.ssid, ep->stats.ssid_len));
+ break;
+ case VALUE_BSSID_OR_SSID:
+ str = g_strdup_printf("wlan.bssid==%s || wlan_mgt.ssid==\"%s\"",
+ ep_address_to_str(&ep->bssid), format_text(ep->stats.ssid, ep->stats.ssid_len));
+ break;
+ }
+
+ apply_selected_filter (callback_action, str);
+
+ g_free (str);
+}
+
+static void
+wlan_details_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callback_action)
+{
+ wlanstat_t *hs=(wlanstat_t *)callback_data;
+ char *str = NULL;
+ wlan_details_ep_t *ep;
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(hs->details));
+ gtk_tree_selection_get_selected (sel, &model, &iter);
+ gtk_tree_model_get (model, &iter, DETAILS_COLUMN, &ep, -1);
+
+ str = g_strdup_printf("wlan.addr==%s", ep_address_to_str(&ep->addr));
+
+ apply_selected_filter (callback_action, str);
+
+ g_free (str);
+}
+
+static gboolean
+wlan_show_popup_menu_cb(void *widg _U_, GdkEvent *event, wlanstat_t *et)
+{
+ GdkEventButton *bevent = (GdkEventButton *)event;
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ /* To qoute the "Gdk Event Structures" doc:
+ * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
+ if(event->type==GDK_BUTTON_PRESS && bevent->button==3){
+ /* If this is a right click on one of our columns, popup the context menu */
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(et->table));
+ if (gtk_tree_selection_get_selected (sel, &model, &iter)) {
+ gtk_menu_popup(GTK_MENU(et->menu), NULL, NULL, NULL, NULL,
+ bevent->button, bevent->time);
+ }
+ }
+
+ return FALSE;
+}
+
+/* Apply as Filter/Selected */
+static void
+wlan_select_filter_as_selected_BSSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_SELECTED, VALUE_BSSID_ONLY));
+}
+
+static void
+wlan_select_filter_as_selected_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_SELECTED, VALUE_SSID_ONLY));
+}
+
+static void
+wlan_select_filter_as_selected_BSSID_and_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_SELECTED, VALUE_BSSID_AND_SSID));
+}
+
+static void
+wlan_select_filter_as_selected_BSSID_or_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_SELECTED, VALUE_BSSID_OR_SSID));
+}
+
+/* Apply as Filter/Not Selected */
+static void
+wlan_select_filter_as_not_selected_BSSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_NOT_SELECTED, VALUE_BSSID_ONLY));
+}
+
+static void
+wlan_select_filter_as_not_selected_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_NOT_SELECTED, VALUE_SSID_ONLY));
+}
+
+static void
+wlan_select_filter_as_not_selected_BSSID_and_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_NOT_SELECTED, VALUE_BSSID_AND_SSID));
+}
+
+static void
+wlan_select_filter_as_not_selected_BSSID_or_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_NOT_SELECTED, VALUE_BSSID_OR_SSID));
+}
+
+/* /Apply as Filter/... and Selected */
+static void
+wlan_select_filter_and_selected_BSSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_SELECTED, VALUE_BSSID_ONLY));
+}
+
+static void
+wlan_select_filter_and_selected_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_SELECTED, VALUE_SSID_ONLY));
+}
+
+static void
+wlan_select_filter_and_selected_BSSID_and_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_SELECTED, VALUE_BSSID_AND_SSID));
+}
+
+static void
+wlan_select_filter_and_selected_BSSID_or_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_SELECTED, VALUE_BSSID_OR_SSID));
+}
+
+/* /Apply as Filter/... or Selected */
+static void
+wlan_select_filter_or_selected_BSSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_SELECTED, VALUE_BSSID_ONLY));
+}
+
+static void
+wlan_select_filter_or_selected_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_SELECTED, VALUE_SSID_ONLY));
+}
+
+static void
+wlan_select_filter_or_selected_BSSID_and_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_SELECTED, VALUE_BSSID_AND_SSID));
+}
+
+static void
+wlan_select_filter_or_selected_BSSID_or_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_SELECTED, VALUE_BSSID_OR_SSID));
+}
+
+/* /Apply as Filter/... and not Selected */
+static void
+wlan_select_filter_and_not_selected_BSSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, VALUE_BSSID_ONLY));
+}
+
+static void
+wlan_select_filter_and_not_selected_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, VALUE_SSID_ONLY));
+}
+
+static void
+wlan_select_filter_and_not_selected_BSSID_and_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, VALUE_BSSID_AND_SSID));
+}
+
+static void
+wlan_select_filter_and_not_selected_BSSID_or_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, VALUE_BSSID_OR_SSID));
+}
+
+/* /Apply as Filter/... or not Selected */
+static void
+wlan_select_filter_or_not_selected_BSSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, VALUE_BSSID_ONLY));
+}
+
+static void
+wlan_select_filter_or_not_selected_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, VALUE_SSID_ONLY));
+}
+
+static void
+wlan_select_filter_or_not_selected_BSSID_and_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, VALUE_BSSID_AND_SSID));
+}
+
+static void
+wlan_select_filter_or_not_selected_BSSID_or_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, VALUE_BSSID_OR_SSID));
+}
+
+/* Prepare */
+/* Prepare a Filter/Selected */
+static void
+wlan_prepare_filter_as_selected_BSSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_SELECTED, VALUE_BSSID_ONLY));
+}
+
+static void
+wlan_prepare_filter_as_selected_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_SELECTED, VALUE_SSID_ONLY));
+}
+
+static void
+wlan_prepare_filter_as_selected_BSSID_and_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_SELECTED, VALUE_BSSID_AND_SSID));
+}
+
+static void
+wlan_prepare_filter_as_selected_BSSID_or_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_SELECTED, VALUE_BSSID_OR_SSID));
+}
+
+/* Prepare a Filter/Not Selected */
+static void
+wlan_prepare_filter_as_not_selected_BSSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, VALUE_BSSID_ONLY));
+}
+
+static void
+wlan_prepare_filter_as_not_selected_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, VALUE_SSID_ONLY));
+}
+
+static void
+wlan_prepare_filter_as_not_selected_BSSID_and_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, VALUE_BSSID_AND_SSID));
+}
+
+static void
+wlan_prepare_filter_as_not_selected_BSSID_or_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, VALUE_BSSID_OR_SSID));
+}
+
+/* /Prepare a Filter/... and Selected */
+static void
+wlan_prepare_filter_and_selected_BSSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_SELECTED, VALUE_BSSID_ONLY));
+}
+
+static void
+wlan_prepare_filter_and_selected_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_SELECTED, VALUE_SSID_ONLY));
+}
+
+static void
+wlan_prepare_filter_and_selected_BSSID_and_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_SELECTED, VALUE_BSSID_AND_SSID));
+}
+
+static void
+wlan_prepare_filter_and_selected_BSSID_or_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_SELECTED, VALUE_BSSID_OR_SSID));
+}
+
+/* /Prepare a Filter/... or Selected */
+static void
+wlan_prepare_filter_or_selected_BSSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_SELECTED, VALUE_BSSID_ONLY));
+}
+
+static void
+wlan_prepare_filter_or_selected_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_SELECTED, VALUE_SSID_ONLY));
+}
+
+static void
+wlan_prepare_filter_or_selected_BSSID_and_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_SELECTED, VALUE_BSSID_AND_SSID));
+}
+
+static void
+wlan_prepare_filter_or_selected_BSSID_or_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_SELECTED, VALUE_BSSID_OR_SSID));
+}
+
+/* /Prepare a Filter/... and not Selected */
+static void
+wlan_prepare_filter_and_not_selected_BSSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, VALUE_BSSID_ONLY));
+}
+
+static void
+wlan_prepare_filter_and_not_selected_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, VALUE_SSID_ONLY));
+}
+
+static void
+wlan_prepare_filter_and_not_selected_BSSID_and_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, VALUE_BSSID_AND_SSID));
+}
+
+static void
+wlan_prepare_filter_and_not_selected_BSSID_or_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, VALUE_BSSID_OR_SSID));
+}
+
+/* /Prepare a Filter/... or not Selected */
+static void
+wlan_prepare_filter_or_not_selected_BSSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, VALUE_BSSID_ONLY));
+}
+
+static void
+wlan_prepare_filter_or_not_selected_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, VALUE_SSID_ONLY));
+}
+
+static void
+wlan_prepare_filter_or_not_selected_BSSID_and_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, VALUE_BSSID_AND_SSID));
+}
+static void
+wlan_prepare_filter_or_not_selected_BSSID_or_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, VALUE_BSSID_OR_SSID));
+}
+
+/* /Find frame/Find Frame/ */
+static void
+wlan_find_frame_BSSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_FIND_FRAME(ACTYPE_SELECTED, VALUE_BSSID_ONLY));
+}
+static void
+wlan_find_frame_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_FIND_FRAME(ACTYPE_SELECTED, VALUE_SSID_ONLY));
+}
+static void
+wlan_find_frame_BSSID_and_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_FIND_FRAME(ACTYPE_SELECTED, VALUE_BSSID_AND_SSID));
+}
+static void
+wlan_find_frame_BSSID_or_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_FIND_FRAME(ACTYPE_SELECTED, VALUE_BSSID_OR_SSID));
+}
+
+/* /Find frame/Find Next/ */
+static void
+wlan_find_frame_next_BSSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_FIND_NEXT(ACTYPE_SELECTED, VALUE_BSSID_ONLY));
+}
+static void
+wlan_find_frame_next_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_FIND_NEXT(ACTYPE_SELECTED, VALUE_SSID_ONLY));
+}
+static void
+wlan_find_frame_next_BSSID_and_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_FIND_NEXT(ACTYPE_SELECTED, VALUE_BSSID_AND_SSID));
+}
+static void
+wlan_find_frame_next_BSSID_or_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_FIND_NEXT(ACTYPE_SELECTED, VALUE_BSSID_OR_SSID));
+}
+/* /Find frame/Find Previous/ */
+static void
+wlan_find_frame_previous_BSSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, VALUE_BSSID_ONLY));
+}
+static void
+wlan_find_frame_previous_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, VALUE_SSID_ONLY));
+}
+static void
+wlan_find_frame_previous_BSSID_and_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, VALUE_BSSID_AND_SSID));
+}
+static void
+wlan_find_frame_previous_BSSID_or_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_FIND_PREVIOUS(ACTYPE_SELECTED, VALUE_BSSID_OR_SSID));
+}
+
+/* /Colorize/ */
+static void
+wlan_colorize_BSSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, VALUE_BSSID_ONLY));
+}
+static void
+wlan_colorize_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, VALUE_SSID_ONLY));
+}
+static void
+wlan_colorize_BSSID_and_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, VALUE_BSSID_AND_SSID));
+}
+static void
+wlan_colorize_BSSID_or_SSID_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_select_filter_cb( widget , user_data, CALLBACK_COLORIZE(ACTYPE_SELECTED, VALUE_BSSID_OR_SSID));
+}
+
+
+static const char *ui_desc_wlan_stat_filter_popup =
+"<ui>\n"
+" <popup name='WlanStatFilterPopup' action='PopupAction'>\n"
+" <menu name= 'ApplyAsFilter' action='/Apply as Filter'>\n"
+" <menu name= 'ApplyAsFilterSelected' action='/Apply as Filter/Selected'>\n"
+" <menuitem action='/Apply as Filter/Selected/BSSID'/>\n"
+" <menuitem action='/Apply as Filter/Selected/SSID'/>\n"
+" <menuitem action='/Apply as Filter/Selected/BSSID and SSID'/>\n"
+" <menuitem action='/Apply as Filter/Selected/BSSID or SSID'/>\n"
+" </menu>\n"
+" <menu name= 'ApplyAsFilterNotSelected' action='/Apply as Filter/Not Selected'>\n"
+" <menuitem action='/Apply as Filter/Not Selected/BSSID'/>\n"
+" <menuitem action='/Apply as Filter/Not Selected/SSID'/>\n"
+" <menuitem action='/Apply as Filter/Not Selected/BSSID and SSID'/>\n"
+" <menuitem action='/Apply as Filter/Not Selected/BSSID or SSID'/>\n"
+" </menu>\n"
+" <menu name= 'ApplyAsFilterAndSelected' action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected'>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/BSSID'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/SSID'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/BSSID and SSID'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/BSSID or SSID'/>\n"
+" </menu>\n"
+" <menu name= 'ApplyAsFilterOrSelected' action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected'>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/BSSID'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/SSID'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/BSSID and SSID'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/BSSID or SSID'/>\n"
+" </menu>\n"
+" <menu name= 'ApplyAsFilterAndNotSelected' action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected'>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/BSSID'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/SSID'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/BSSID and SSID'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/BSSID or SSID'/>\n"
+" </menu>\n"
+" <menu name= 'ApplyAsFilterOrNotSelected' action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected'>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/BSSID'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/SSID'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/BSSID and SSID'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/BSSID or SSID'/>\n"
+" </menu>\n"
+" </menu>\n"
+" <menu name= 'PrepareAFilter' action='/Prepare a Filter'>\n"
+" <menu name= 'PrepareAFilterSelected' action='/Prepare a Filter/Selected'>\n"
+" <menuitem action='/Prepare a Filter/Selected/BSSID'/>\n"
+" <menuitem action='/Prepare a Filter/Selected/SSID'/>\n"
+" <menuitem action='/Prepare a Filter/Selected/BSSID and SSID'/>\n"
+" <menuitem action='/Prepare a Filter/Selected/BSSID or SSID'/>\n"
+" </menu>\n"
+" <menu name= 'PrepareAFilterNotSelected' action='/Prepare a Filter/Not Selected'>\n"
+" <menuitem action='/Prepare a Filter/Not Selected/BSSID'/>\n"
+" <menuitem action='/Prepare a Filter/Not Selected/SSID'/>\n"
+" <menuitem action='/Prepare a Filter/Not Selected/BSSID and SSID'/>\n"
+" <menuitem action='/Prepare a Filter/Not Selected/BSSID or SSID'/>\n"
+" </menu>\n"
+" <menu name= 'PrepareAFilterAndSelected' action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected'>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/BSSID'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/SSID'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/BSSID and SSID'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/BSSID or SSID'/>\n"
+" </menu>\n"
+" <menu name= 'PrepareAFilterOrSelected' action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected'>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/BSSID'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/SSID'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/BSSID and SSID'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/BSSID or SSID'/>\n"
+" </menu>\n"
+" <menu name= 'PrepareAFilterAndNotSelected' action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected'>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/BSSID'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/SSID'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/BSSID and SSID'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/BSSID or SSID'/>\n"
+" </menu>\n"
+" <menu name= 'PrepareAFilterOrNotSelected' action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected'>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/BSSID'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/SSID'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/BSSID and SSID'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/BSSID or SSID'/>\n"
+" </menu>\n"
+" </menu>\n"
+" <menu name= 'FindFrame' action='/Find Frame'>\n"
+" <menu name= 'FindFrameFindFrame' action='/Find Frame/Find Frame'>\n"
+" <menuitem action='/Find Frame/Find Frame/BSSID'/>\n"
+" <menuitem action='/Find Frame/Find Frame/SSID'/>\n"
+" <menuitem action='/Find Frame/Find Frame/BSSID and SSID'/>\n"
+" <menuitem action='/Find Frame/Find Frame/BSSID or SSID'/>\n"
+" </menu>\n"
+" <menu name= 'FindFrameNext' action='/Find Frame/Find Next'>\n"
+" <menuitem action='/Find Frame/Find Next/BSSID'/>\n"
+" <menuitem action='/Find Frame/Find Next/SSID'/>\n"
+" <menuitem action='/Find Frame/Find Next/BSSID and SSID'/>\n"
+" <menuitem action='/Find Frame/Find Next/BSSID or SSID'/>\n"
+" </menu>\n"
+" <menu name= 'FindFramePrevious' action='/Find Frame/Find Previous'>\n"
+" <menuitem action='/Find Frame/Find Previous/BSSID'/>\n"
+" <menuitem action='/Find Frame/Find Previous/SSID'/>\n"
+" <menuitem action='/Find Frame/Find Previous/BSSID and SSID'/>\n"
+" <menuitem action='/Find Frame/Find Previous/BSSID or SSID'/>\n"
+" </menu>\n"
+" </menu>\n"
+" <menu name= 'Colorize' action='/Colorize'>\n"
+" <menuitem action='/Colorize/BSSID'/>\n"
+" <menuitem action='/Colorize/SSID'/>\n"
+" <menuitem action='/Colorize/BSSID and SSID'/>\n"
+" <menuitem action='/Colorize/BSSID or SSID'/>\n"
+" </menu>\n"
+" </popup>\n"
+"</ui>\n";
+
+/*
+ * GtkActionEntry
+ * typedef struct {
+ * const gchar *name;
+ * const gchar *stock_id;
+ * const gchar *label;
+ * const gchar *accelerator;
+ * const gchar *tooltip;
+ * GCallback callback;
+ * } GtkActionEntry;
+ * const gchar *name; The name of the action.
+ * const gchar *stock_id; The stock id for the action, or the name of an icon from the icon theme.
+ * const gchar *label; The label for the action. This field should typically be marked for translation,
+ * see gtk_action_group_set_translation_domain().
+ * If label is NULL, the label of the stock item with id stock_id is used.
+ * const gchar *accelerator; The accelerator for the action, in the format understood by gtk_accelerator_parse().
+ * const gchar *tooltip; The tooltip for the action. This field should typically be marked for translation,
+ * see gtk_action_group_set_translation_domain().
+ * GCallback callback; The function to call when the action is activated.
+ *
+ */
+static const GtkActionEntry wlans_stat_popup_entries[] = {
+ /* Top level */
+ { "/Apply as Filter", NULL, "Apply as Filter", NULL, NULL, NULL },
+ { "/Prepare a Filter", NULL, "Prepare a Filter", NULL, NULL, NULL },
+ { "/Find Frame", NULL, "Find Frame", NULL, NULL, NULL },
+ { "/Colorize", NULL, "Colorize", NULL, NULL, NULL },
+
+ /* Apply as */
+ { "/Apply as Filter/Selected", NULL, "Selected" , NULL, NULL, NULL },
+ { "/Apply as Filter/Not Selected", NULL, "Not Selected", NULL, NULL, NULL },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, NULL, NULL },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, NULL, NULL },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, NULL, NULL },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, NULL, NULL },
+
+ /* Apply as Selected */
+ { "/Apply as Filter/Selected/BSSID", NULL, "BSSID", NULL, "BSSID", G_CALLBACK(wlan_select_filter_as_selected_BSSID_cb)},
+ { "/Apply as Filter/Selected/SSID", NULL, "SSID", NULL, "SSID", G_CALLBACK(wlan_select_filter_as_selected_SSID_cb)},
+ { "/Apply as Filter/Selected/BSSID and SSID", NULL, "BSSID and SSID", NULL, "BSSID and SSID", G_CALLBACK(wlan_select_filter_as_selected_BSSID_and_SSID_cb)},
+ { "/Apply as Filter/Selected/BSSID or SSID", NULL, "BSSID or SSID", NULL, "BSSID or SSID", G_CALLBACK(wlan_select_filter_as_selected_BSSID_or_SSID_cb)},
+
+ /* Apply as Not Selected */
+ { "/Apply as Filter/Not Selected/BSSID", NULL, "BSSID", NULL, "BSSID", G_CALLBACK(wlan_select_filter_as_not_selected_BSSID_cb)},
+ { "/Apply as Filter/Not Selected/SSID", NULL, "SSID", NULL, "SSID", G_CALLBACK(wlan_select_filter_as_not_selected_SSID_cb)},
+ { "/Apply as Filter/Not Selected/BSSID and SSID", NULL, "BSSID and SSID", NULL, "BSSID and SSID", G_CALLBACK(wlan_select_filter_as_not_selected_BSSID_and_SSID_cb)},
+ { "/Apply as Filter/Not Selected/BSSID or SSID", NULL, "BSSID or SSID", NULL, "BSSID or SSID", G_CALLBACK(wlan_select_filter_as_not_selected_BSSID_or_SSID_cb)},
+
+ /* Apply as and Selected */
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/BSSID", NULL, "BSSID", NULL, "BSSID", G_CALLBACK(wlan_select_filter_and_selected_BSSID_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/SSID", NULL, "SSID", NULL, "SSID", G_CALLBACK(wlan_select_filter_and_selected_SSID_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/BSSID and SSID", NULL, "BSSID and SSID", NULL, "BSSID and SSID", G_CALLBACK(wlan_select_filter_and_selected_BSSID_and_SSID_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/BSSID or SSID", NULL, "BSSID or SSID", NULL, "BSSID or SSID", G_CALLBACK(wlan_select_filter_and_selected_BSSID_or_SSID_cb)},
+
+ /* Apply as or Selected */
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/BSSID", NULL, "BSSID", NULL, "BSSID", G_CALLBACK(wlan_select_filter_or_selected_BSSID_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/SSID", NULL, "SSID", NULL, "SSID", G_CALLBACK(wlan_select_filter_or_selected_SSID_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/BSSID and SSID", NULL, "BSSID and SSID", NULL, "BSSID and SSID", G_CALLBACK(wlan_select_filter_or_selected_BSSID_and_SSID_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/BSSID or SSID", NULL, "BSSID or SSID", NULL, "BSSID or SSID", G_CALLBACK(wlan_select_filter_or_selected_BSSID_or_SSID_cb)},
+
+ /* /Apply as Filter/... and not Selected */
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/BSSID", NULL, "BSSID", NULL, "BSSID", G_CALLBACK(wlan_select_filter_and_not_selected_BSSID_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/SSID", NULL, "SSID", NULL, "SSID", G_CALLBACK(wlan_select_filter_and_not_selected_SSID_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/BSSID and SSID", NULL, "BSSID and SSID", NULL, "BSSID and SSID", G_CALLBACK(wlan_select_filter_and_not_selected_BSSID_and_SSID_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/BSSID or SSID", NULL, "BSSID or SSID", NULL, "BSSID or SSID", G_CALLBACK(wlan_select_filter_and_not_selected_BSSID_or_SSID_cb)},
+
+ /* /Apply as Filter/... or not Selected */
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/BSSID", NULL, "BSSID", NULL, "BSSID", G_CALLBACK(wlan_select_filter_or_not_selected_BSSID_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/SSID", NULL, "SSID", NULL, "SSID", G_CALLBACK(wlan_select_filter_or_not_selected_SSID_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/BSSID and SSID", NULL, "BSSID and SSID", NULL, "BSSID and SSID", G_CALLBACK(wlan_select_filter_or_not_selected_BSSID_and_SSID_cb)},
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/BSSID or SSID", NULL, "BSSID or SSID", NULL, "BSSID or SSID", G_CALLBACK(wlan_select_filter_or_not_selected_BSSID_or_SSID_cb)},
+
+ /* Prepare a */
+ { "/Prepare a Filter/Selected", NULL, "Selected" , NULL, NULL, NULL },
+ { "/Prepare a Filter/Not Selected", NULL, "Not Selected", NULL, NULL, NULL },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, NULL, NULL },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, NULL, NULL },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, NULL, NULL },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, NULL, NULL },
+
+ /* Prepare a Selected */
+ { "/Prepare a Filter/Selected/BSSID", NULL, "BSSID", NULL, "BSSID", G_CALLBACK(wlan_prepare_filter_as_selected_BSSID_cb)},
+ { "/Prepare a Filter/Selected/SSID", NULL, "SSID", NULL, "SSID", G_CALLBACK(wlan_prepare_filter_as_selected_SSID_cb)},
+ { "/Prepare a Filter/Selected/BSSID and SSID",NULL, "BSSID and SSID", NULL, "BSSID and SSID", G_CALLBACK(wlan_prepare_filter_as_selected_BSSID_and_SSID_cb)},
+ { "/Prepare a Filter/Selected/BSSID or SSID", NULL, "BSSID or SSID", NULL, "BSSID or SSID", G_CALLBACK(wlan_prepare_filter_as_selected_BSSID_or_SSID_cb)},
+
+ /* Prepare a Not Selected */
+ { "/Prepare a Filter/Not Selected/BSSID", NULL, "BSSID", NULL, "BSSID", G_CALLBACK(wlan_prepare_filter_as_not_selected_BSSID_cb)},
+ { "/Prepare a Filter/Not Selected/SSID", NULL, "SSID", NULL, "SSID", G_CALLBACK(wlan_prepare_filter_as_not_selected_SSID_cb)},
+ { "/Prepare a Filter/Not Selected/BSSID and SSID",NULL, "BSSID and SSID", NULL, "BSSID and SSID", G_CALLBACK(wlan_prepare_filter_as_not_selected_BSSID_and_SSID_cb)},
+ { "/Prepare a Filter/Not Selected/BSSID or SSID", NULL, "BSSID or SSID", NULL, "BSSID or SSID", G_CALLBACK(wlan_prepare_filter_as_not_selected_BSSID_or_SSID_cb)},
+
+ /* Prepare a and Selected */
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/BSSID", NULL, "BSSID", NULL, "BSSID", G_CALLBACK(wlan_prepare_filter_and_selected_BSSID_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/SSID", NULL, "SSID", NULL, "SSID", G_CALLBACK(wlan_prepare_filter_and_selected_SSID_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/BSSID and SSID", NULL, "BSSID and SSID", NULL, "BSSID and SSID", G_CALLBACK(wlan_prepare_filter_and_selected_BSSID_and_SSID_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected/BSSID or SSID", NULL, "BSSID or SSID", NULL, "BSSID or SSID", G_CALLBACK(wlan_prepare_filter_and_selected_BSSID_or_SSID_cb)},
+
+ /* Prepare a or Selected */
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/BSSID", NULL, "BSSID", NULL, "BSSID", G_CALLBACK(wlan_prepare_filter_or_selected_BSSID_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/SSID", NULL, "SSID", NULL, "SSID", G_CALLBACK(wlan_prepare_filter_or_selected_SSID_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/BSSID and SSID", NULL, "BSSID and SSID", NULL, "BSSID and SSID", G_CALLBACK(wlan_prepare_filter_or_selected_BSSID_and_SSID_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected/BSSID or SSID", NULL, "BSSID or SSID", NULL, "BSSID or SSID", G_CALLBACK(wlan_prepare_filter_or_selected_BSSID_or_SSID_cb)},
+
+ /* /Prepare a Filter/... and not Selected */
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/BSSID", NULL, "BSSID", NULL, "BSSID", G_CALLBACK(wlan_prepare_filter_and_not_selected_BSSID_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/SSID", NULL, "SSID", NULL, "SSID", G_CALLBACK(wlan_prepare_filter_and_not_selected_SSID_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/BSSID and SSID",NULL, "BSSID and SSID", NULL, "BSSID and SSID", G_CALLBACK(wlan_prepare_filter_and_not_selected_BSSID_and_SSID_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected/BSSID or SSID", NULL, "BSSID or SSID", NULL, "BSSID or SSID", G_CALLBACK(wlan_prepare_filter_and_not_selected_BSSID_or_SSID_cb)},
+
+ /* /Prepare a Filter/... or not Selected */
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/BSSID", NULL, "BSSID", NULL, "BSSID", G_CALLBACK(wlan_prepare_filter_or_not_selected_BSSID_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/SSID", NULL, "SSID", NULL, "SSID", G_CALLBACK(wlan_prepare_filter_or_not_selected_SSID_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/BSSID and SSID", NULL, "BSSID and SSID", NULL, "BSSID and SSID", G_CALLBACK(wlan_prepare_filter_or_not_selected_BSSID_and_SSID_cb)},
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected/BSSID or SSID", NULL, "BSSID or SSID", NULL, "BSSID or SSID", G_CALLBACK(wlan_prepare_filter_or_not_selected_BSSID_or_SSID_cb)},
+
+ /* Find Frame*/
+ { "/Find Frame/Find Frame", NULL, "Find Frame", NULL, NULL, NULL },
+ { "/Find Frame/Find Next", NULL, "Find Next", NULL, NULL, NULL },
+ { "/Find Frame/Find Previous", NULL, "Find Previous", NULL, NULL, NULL },
+
+ /* Find Frame/Find Frame*/
+ { "/Find Frame/Find Frame/BSSID", NULL, "BSSID", NULL, "BSSID", G_CALLBACK(wlan_find_frame_BSSID_cb)},
+ { "/Find Frame/Find Frame/SSID", NULL, "SSID", NULL, "SSID", G_CALLBACK(wlan_find_frame_SSID_cb)},
+ { "/Find Frame/Find Frame/BSSID and SSID", NULL, "SSID and SSID", NULL, "SSID and SSID", G_CALLBACK(wlan_find_frame_BSSID_and_SSID_cb)},
+ { "/Find Frame/Find Frame/BSSID or SSID", NULL, "BSSID", NULL, "BSSID", G_CALLBACK(wlan_find_frame_BSSID_or_SSID_cb)},
+
+ /* Find Frame/Find Next*/
+ { "/Find Frame/Find Next/BSSID", NULL, "BSSID", NULL, "BSSID", G_CALLBACK(wlan_find_frame_next_BSSID_cb)},
+ { "/Find Frame/Find Next/SSID", NULL, "SSID", NULL, "SSID", G_CALLBACK(wlan_find_frame_next_SSID_cb)},
+ { "/Find Frame/Find Next/BSSID and SSID", NULL, "SSID and SSID", NULL, "SSID and SSID", G_CALLBACK(wlan_find_frame_next_BSSID_and_SSID_cb)},
+ { "/Find Frame/Find Next/BSSID or SSID", NULL, "BSSID", NULL, "BSSID", G_CALLBACK(wlan_find_frame_next_BSSID_or_SSID_cb)},
+
+ /* Find Frame/Find Previous*/
+ { "/Find Frame/Find Previous/BSSID", NULL, "BSSID", NULL, "BSSID", G_CALLBACK(wlan_find_frame_previous_BSSID_cb)},
+ { "/Find Frame/Find Previous/SSID", NULL, "SSID", NULL, "SSID", G_CALLBACK(wlan_find_frame_previous_SSID_cb)},
+ { "/Find Frame/Find Previous/BSSID and SSID", NULL, "SSID and SSID", NULL, "SSID and SSID", G_CALLBACK(wlan_find_frame_previous_BSSID_and_SSID_cb)},
+ { "/Find Frame/Find Previous/BSSID or SSID", NULL, "BSSID", NULL, "BSSID", G_CALLBACK(wlan_find_frame_previous_BSSID_or_SSID_cb)},
+
+ /* Colorize */
+ { "/Colorize/BSSID", NULL, "BSSID", NULL, "BSSID", G_CALLBACK(wlan_colorize_BSSID_cb)},
+ { "/Colorize/SSID", NULL, "SSID", NULL, "SSID", G_CALLBACK(wlan_colorize_SSID_cb)},
+ { "/Colorize/BSSID and SSID", NULL, "BSSID and SSID", NULL, "BSSID and SSID", G_CALLBACK(wlan_colorize_BSSID_and_SSID_cb)},
+ { "/Colorize/BSSID or SSID", NULL, "BSSID or SSID", NULL, "BSSID or SSID", G_CALLBACK(wlan_colorize_BSSID_or_SSID_cb)},
+
+};
+
+static void
+wlan_create_popup_menu(wlanstat_t *hs)
+{
+ GtkUIManager *ui_manager;
+ GtkActionGroup *action_group;
+ GError *error = NULL;
+
+ action_group = gtk_action_group_new ("WlanFilterPopupActionGroup");
+ gtk_action_group_add_actions (action_group, /* the action group */
+ (gpointer)wlans_stat_popup_entries, /* an array of action descriptions */
+ G_N_ELEMENTS(wlans_stat_popup_entries), /* the number of entries */
+ hs); /* data to pass to the action callbacks */
+
+ ui_manager = gtk_ui_manager_new ();
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ gtk_ui_manager_add_ui_from_string (ui_manager,ui_desc_wlan_stat_filter_popup, -1, &error);
+ if (error != NULL)
+ {
+ fprintf (stderr, "Warning: building Wlan Stat Filter popup failed: %s\n",
+ error->message);
+ g_error_free (error);
+ error = NULL;
+ }
+ hs->menu = gtk_ui_manager_get_widget(ui_manager, "/WlanStatFilterPopup");
+ g_signal_connect(hs->table, "button_press_event", G_CALLBACK(wlan_show_popup_menu_cb), hs);
+
+}
+
+static gboolean
+wlan_details_show_popup_menu_cb(void *widg _U_, GdkEvent *event, wlanstat_t *et)
+{
+ GdkEventButton *bevent = (GdkEventButton *)event;
+ GtkTreeSelection *sel;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ /* To qoute the "Gdk Event Structures" doc:
+ * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */
+ if(event->type==GDK_BUTTON_PRESS && bevent->button==3){
+ /* if this is a right click on one of our columns, popup the context menu */
+ sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(et->details));
+ if (gtk_tree_selection_get_selected (sel, &model, &iter)) {
+ gtk_menu_popup(GTK_MENU(et->details_menu), NULL, NULL, NULL, NULL,
+ bevent->button, bevent->time);
+ }
+ }
+
+ return FALSE;
+}
+
+/* Apply as Filter/ */
+
+static void
+wlan_details_apply_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_details_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_SELECTED, 0));
+}
+
+static void
+wlan_details_apply_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_details_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_NOT_SELECTED, 0));
+}
+
+static void
+wlan_details_apply_and_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_details_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_SELECTED, 0));
+}
+
+static void
+wlan_details_apply_or_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_details_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_SELECTED, 0));
+}
+
+static void
+wlan_details_apply_and_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_details_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_AND_NOT_SELECTED, 0));
+}
+
+static void
+wlan_details_apply_or_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_details_select_filter_cb( widget , user_data, CALLBACK_MATCH(ACTYPE_OR_NOT_SELECTED, 0));
+}
+/* Prepare a filter */
+static void
+wlan_details_prepare_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_details_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_SELECTED, 0));
+}
+
+static void
+wlan_details_prepare_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_details_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_NOT_SELECTED, 0));
+}
+
+static void
+wlan_details_prepare_and_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_details_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_SELECTED, 0));
+}
+
+static void
+wlan_details_prepare_or_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_details_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_SELECTED, 0));
+}
+
+static void
+wlan_details_prepare_and_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_details_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_AND_NOT_SELECTED, 0));
+}
+
+static void
+wlan_details_prepare_or_not_selected_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_details_select_filter_cb( widget , user_data, CALLBACK_PREPARE(ACTYPE_OR_NOT_SELECTED, 0));
+}
+
+static void
+wlan_details_find_frame_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_details_select_filter_cb( widget , user_data, CALLBACK_FIND_FRAME(ACTYPE_SELECTED, 0));
+}
+static void
+wlan_details_find_next_frame_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_details_select_filter_cb( widget , user_data, CALLBACK_FIND_FRAME(ACTYPE_OR_NOT_SELECTED, 0));
+}
+static void
+wlan_details_find_previous_frame_cb(GtkWidget *widget, gpointer user_data)
+{
+ wlan_details_select_filter_cb( widget , user_data, CALLBACK_FIND_FRAME(ACTYPE_OR_NOT_SELECTED, 0));
+}
+
+
+static const char *ui_desc_wlan_details_filter_popup =
+"<ui>\n"
+" <popup name='WlanStatFilterPopup' action='PopupAction'>\n"
+" <menu name= 'ApplyAsFilter' action='/Apply as Filter'>\n"
+" <menuitem action='/Apply as Filter/Selected'/>\n"
+" <menuitem action='/Apply as Filter/Not Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected'/>\n"
+" <menuitem action='/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected'/>\n"
+" </menu>\n"
+" <menu name= 'PrepareAFilter' action='/Prepare a Filter'>\n"
+" <menuitem action='/Prepare a Filter/Selected'/>\n"
+" <menuitem action='/Prepare a Filter/Not Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected'/>\n"
+" <menuitem action='/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected'/>\n"
+" </menu>\n"
+" <menu name= 'FindFrame' action='/Find Frame'>\n"
+" <menuitem action='/Find Frame/Find Frame'/>\n"
+" <menuitem action='/Find Frame/Find Next'/>\n"
+" <menuitem action='/Find Frame/Find Previous'/>\n"
+" </menu>\n"
+" </popup>\n"
+"</ui>\n";
+
+/*
+ * GtkActionEntry
+ * typedef struct {
+ * const gchar *name;
+ * const gchar *stock_id;
+ * const gchar *label;
+ * const gchar *accelerator;
+ * const gchar *tooltip;
+ * GCallback callback;
+ * } GtkActionEntry;
+ * const gchar *name; The name of the action.
+ * const gchar *stock_id; The stock id for the action, or the name of an icon from the icon theme.
+ * const gchar *label; The label for the action. This field should typically be marked for translation,
+ * see gtk_action_group_set_translation_domain().
+ * If label is NULL, the label of the stock item with id stock_id is used.
+ * const gchar *accelerator; The accelerator for the action, in the format understood by gtk_accelerator_parse().
+ * const gchar *tooltip; The tooltip for the action. This field should typically be marked for translation,
+ * see gtk_action_group_set_translation_domain().
+ * GCallback callback; The function to call when the action is activated.
+ *
+ */
+static const GtkActionEntry wlan_details_list_popup_entries[] = {
+ /* Top level */
+ { "/Apply as Filter", NULL, "Apply as Filter", NULL, NULL, NULL },
+ { "/Prepare a Filter", NULL, "Prepare a Filter", NULL, NULL, NULL },
+ { "/Find Frame", NULL, "Find Frame", NULL, NULL, NULL },
+
+ /* Apply as */
+ { "/Apply as Filter/Selected", NULL, "Selected" , NULL, NULL, G_CALLBACK(wlan_details_apply_selected_cb) },
+ { "/Apply as Filter/Not Selected", NULL, "Not Selected", NULL, NULL, G_CALLBACK(wlan_details_apply_not_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, NULL, G_CALLBACK(wlan_details_apply_and_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, NULL, G_CALLBACK(wlan_details_apply_or_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, NULL, G_CALLBACK(wlan_details_apply_and_not_selected_cb) },
+ { "/Apply as Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, NULL, G_CALLBACK(wlan_details_apply_or_not_selected_cb) },
+
+ { "/Prepare a Filter/Selected", NULL, "Selected" , NULL, NULL, G_CALLBACK(wlan_details_prepare_selected_cb) },
+ { "/Prepare a Filter/Not Selected", NULL, "Not Selected", NULL, NULL, G_CALLBACK(wlan_details_prepare_not_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and Selected", NULL, NULL, G_CALLBACK(wlan_details_prepare_and_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or Selected", NULL, NULL, G_CALLBACK(wlan_details_prepare_or_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " and not Selected", NULL, NULL, G_CALLBACK(wlan_details_prepare_and_not_selected_cb) },
+ { "/Prepare a Filter/" UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, UTF8_HORIZONTAL_ELLIPSIS " or not Selected", NULL, NULL, G_CALLBACK(wlan_details_prepare_or_not_selected_cb) },
+
+ /* Find Frame*/
+ { "/Find Frame/Find Frame", NULL, "Find Frame", NULL, NULL, G_CALLBACK(wlan_details_find_frame_cb) },
+ { "/Find Frame/Find Next", NULL, "Find Next", NULL, NULL, G_CALLBACK(wlan_details_find_next_frame_cb) },
+ { "/Find Frame/Find Previous", NULL, "Find Previous", NULL, NULL, G_CALLBACK(wlan_details_find_previous_frame_cb) },
+
+};
+
+static void
+wlan_details_create_popup_menu(wlanstat_t *hs)
+{
+ GtkUIManager *ui_manager;
+ GtkActionGroup *action_group;
+ GError *error = NULL;
+
+ action_group = gtk_action_group_new ("WlanDetailsPopupActionGroup");
+ gtk_action_group_add_actions (action_group, /* the action group */
+ (gpointer)wlan_details_list_popup_entries, /* an array of action descriptions */
+ G_N_ELEMENTS(wlan_details_list_popup_entries), /* the number of entries */
+ hs); /* data to pass to the action callbacks */
+
+ ui_manager = gtk_ui_manager_new ();
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ gtk_ui_manager_add_ui_from_string (ui_manager,ui_desc_wlan_details_filter_popup, -1, &error);
+ if (error != NULL)
+ {
+ fprintf (stderr, "Warning: building Wlan details list popup failed: %s\n",
+ error->message);
+ g_error_free (error);
+ error = NULL;
+ }
+ hs->details_menu = gtk_ui_manager_get_widget(ui_manager, "/WlanStatFilterPopup");
+ g_signal_connect(hs->details, "button_press_event", G_CALLBACK(wlan_details_show_popup_menu_cb), hs);
+
+}
+
+static void
+wlanstat_dlg_create (void)
+{
+ wlanstat_t *hs;
+ GString *error_string;
+ GtkWidget *scrolled_window;
+ GtkWidget *bbox;
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *frame;
+ GtkWidget *selected_vb;
+ GtkWidget *resolv_cb;
+ GtkWidget *filter_cb;
+ GtkWidget *existing_cb;
+ GtkWidget *close_bt;
+ GtkWidget *help_bt;
+ GtkWidget *copy_bt;
+ GtkListStore *store;
+ GtkTreeView *tree_view;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkTreeSelection *sel;
+ char title[256];
+ gint i;
+
+ hs=g_malloc (sizeof(wlanstat_t));
+ hs->num_entries = 0;
+ hs->ep_list = NULL;
+ hs->number_of_packets = 0;
+ hs->resolve_names = TRUE;
+ hs->use_dfilter = FALSE;
+ hs->show_only_existing = FALSE;
+
+ g_snprintf (title, sizeof(title), "Wireshark: WLAN Traffic Statistics: %s",
+ cf_get_display_name(&cfile));
+ wlanstat_dlg_w = window_new_with_geom (GTK_WINDOW_TOPLEVEL, title, "WLAN Statistics");
+ gtk_window_set_default_size (GTK_WINDOW(wlanstat_dlg_w), 750, 400);
+
+ vbox=gtk_vbox_new (FALSE, 3);
+ gtk_container_add(GTK_CONTAINER(wlanstat_dlg_w), vbox);
+ gtk_container_set_border_width (GTK_CONTAINER(vbox), 6);
+
+ wlanstat_pane = gtk_vpaned_new();
+ gtk_box_pack_start (GTK_BOX (vbox), wlanstat_pane, TRUE, TRUE, 0);
+ gtk_paned_set_position(GTK_PANED(wlanstat_pane), recent.gui_geometry_wlan_stats_pane);
+ gtk_widget_show(wlanstat_pane);
+
+ /* init a scrolled window for overview */
+ wlanstat_name_lb = gtk_frame_new("Network Overview");
+ gtk_paned_pack1(GTK_PANED(wlanstat_pane), wlanstat_name_lb, FALSE, TRUE);
+ selected_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(wlanstat_name_lb), selected_vb);
+ gtk_container_set_border_width(GTK_CONTAINER(selected_vb), 5);
+
+ scrolled_window = scrolled_window_new (NULL, NULL);
+ gtk_box_pack_start(GTK_BOX(selected_vb), scrolled_window, TRUE, TRUE, 0);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_SHADOW_IN);
+
+ store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT,
+ G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT,
+ G_TYPE_STRING, G_TYPE_FLOAT, G_TYPE_POINTER);
+ hs->table = GTK_TREE_VIEW(tree_view_new(GTK_TREE_MODEL(store)));
+ gtk_container_add(GTK_CONTAINER (scrolled_window), GTK_WIDGET(hs->table));
+ g_object_unref(G_OBJECT(store));
+
+ tree_view = hs->table;
+ gtk_tree_view_set_headers_visible(tree_view, TRUE);
+ gtk_tree_view_set_headers_clickable(tree_view, TRUE);
+
+ for (i = 0; i <= PROTECTION_COLUMN; i++) {
+ if (i == PERCENT_COLUMN) {
+ renderer = gtk_cell_renderer_progress_new();
+ column = gtk_tree_view_column_new_with_attributes(titles[i], renderer,
+ "text", i,
+ "value", PERCENT_VALUE_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_expand(column, TRUE);
+ gtk_tree_view_column_set_sort_column_id(column, PERCENT_VALUE_COLUMN);
+ } else {
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(titles[i], renderer,
+ "text", i,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, i);
+ }
+
+ if (i != BSSID_COLUMN && i != SSID_COLUMN && i != PROTECTION_COLUMN) {
+ /* Align all number columns */
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+ }
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_append_column(tree_view, column);
+
+ if (i == SSID_COLUMN) {
+ /* Sort the SSID column */
+ gtk_tree_view_column_clicked(column);
+ }
+ }
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->table));
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+ g_signal_connect(sel, "changed", G_CALLBACK(wlan_select_cb), hs);
+
+ /* init a scrolled window for details */
+ frame = gtk_frame_new("Selected Network");
+ gtk_paned_pack2(GTK_PANED(wlanstat_pane), frame, FALSE, TRUE);
+ selected_vb = gtk_vbox_new(FALSE, 0);
+ gtk_container_add(GTK_CONTAINER(frame), selected_vb);
+ gtk_container_set_border_width(GTK_CONTAINER(selected_vb), 5);
+
+ scrolled_window = scrolled_window_new (NULL, NULL);
+ gtk_box_pack_start(GTK_BOX(selected_vb), scrolled_window, TRUE, TRUE, 0);
+ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
+ GTK_SHADOW_IN);
+
+ store = gtk_list_store_new(NUM_DETAIL_COLUMNS, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT,
+ G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_STRING,
+ G_TYPE_FLOAT, G_TYPE_POINTER);
+ hs->details = GTK_TREE_VIEW(tree_view_new(GTK_TREE_MODEL(store)));
+ gtk_container_add(GTK_CONTAINER (scrolled_window), GTK_WIDGET(hs->details));
+ g_object_unref(G_OBJECT(store));
+
+ tree_view = hs->details;
+ gtk_tree_view_set_headers_visible(tree_view, TRUE);
+ gtk_tree_view_set_headers_clickable(tree_view, TRUE);
+
+ for (i = 0; i <= COMMENT_COLUMN; i++) {
+ if (i == PERCENT_2_COLUMN) {
+ renderer = gtk_cell_renderer_progress_new();
+ column = gtk_tree_view_column_new_with_attributes(detail_titles[i], renderer,
+ "text", i,
+ "value", PERCENT_VALUE_2_COLUMN,
+ NULL);
+ gtk_tree_view_column_set_expand(column, TRUE);
+ gtk_tree_view_column_set_sort_column_id(column, PERCENT_VALUE_2_COLUMN);
+ } else {
+ renderer = gtk_cell_renderer_text_new();
+ column = gtk_tree_view_column_new_with_attributes(detail_titles[i], renderer,
+ "text", i,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id(column, i);
+ }
+
+ if (i != ADDRESS_COLUMN && i != COMMENT_COLUMN) {
+ /* Align all number columns */
+ g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
+ }
+ gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+ gtk_tree_view_column_set_resizable(column, TRUE);
+ gtk_tree_view_append_column(tree_view, column);
+
+ if (i == ADDRESS_COLUMN) {
+ /* Sort the Address column */
+ gtk_tree_view_column_clicked(column);
+ }
+ }
+
+ sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->table));
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+
+ /* create popup menu for this table */
+ wlan_create_popup_menu(hs);
+ wlan_details_create_popup_menu(hs);
+
+ error_string=register_tap_listener ("wlan", hs, NULL, 0,
+ wlanstat_reset, wlanstat_packet,
+ wlanstat_draw);
+ if (error_string) {
+ simple_dialog (ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
+ g_string_free (error_string, TRUE);
+ g_free (hs);
+ return;
+ }
+
+ hbox = gtk_hbox_new(FALSE, 3);
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+ resolv_cb = gtk_check_button_new_with_mnemonic("Name resolution");
+ gtk_container_add(GTK_CONTAINER(hbox), resolv_cb);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(resolv_cb), TRUE);
+ gtk_widget_set_tooltip_text(resolv_cb, "Show results of name resolutions rather than the \"raw\" values. "
+ "Please note: The corresponding name resolution must be enabled.");
+
+ g_signal_connect(resolv_cb, "toggled", G_CALLBACK(wlan_resolve_toggle_dest), hs);
+
+ filter_cb = gtk_check_button_new_with_mnemonic("Limit to display filter");
+ gtk_container_add(GTK_CONTAINER(hbox), filter_cb);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(filter_cb), FALSE);
+ gtk_widget_set_tooltip_text(filter_cb, "Limit the list to entries matching the current display filter.");
+ g_signal_connect(filter_cb, "toggled", G_CALLBACK(wlan_filter_toggle_dest), hs);
+
+ existing_cb = gtk_check_button_new_with_mnemonic("Only show existing networks");
+ gtk_container_add(GTK_CONTAINER(hbox), existing_cb);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(existing_cb), FALSE);
+ gtk_widget_set_tooltip_text(existing_cb, "This option disables probe requests for "
+ "unknown networks.");
+ g_signal_connect(existing_cb, "toggled", G_CALLBACK(wlan_existing_toggle_dest), hs);
+
+ /* Button row. */
+ bbox = dlg_button_row_new (GTK_STOCK_CLOSE, GTK_STOCK_COPY, GTK_STOCK_HELP, NULL);
+
+ gtk_box_pack_end (GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+ close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button (wlanstat_dlg_w, close_bt, window_cancel_button_cb);
+
+ copy_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_COPY);
+/* gtk_button_set_label(GTK_BUTTON(copy_bt), "Copy Overview"); */
+ gtk_widget_set_tooltip_text(copy_bt,
+ "Copy all statistical values of this page to the clipboard in CSV (Comma Separated Values) format.");
+ g_signal_connect(copy_bt, "clicked", G_CALLBACK(wlan_copy_as_csv), hs->table);
+
+ help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
+ g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_WLAN_TRAFFIC_DIALOG);
+
+ g_signal_connect (wlanstat_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect (wlanstat_dlg_w, "destroy", G_CALLBACK(win_destroy_cb), hs);
+
+ gtk_widget_show_all (wlanstat_dlg_w);
+ window_present (wlanstat_dlg_w);
+
+ cf_retap_packets (&cfile);
+ gdk_window_raise(gtk_widget_get_window(wlanstat_dlg_w));
+}
+
+void
+wlanstat_launch (GtkAction *action _U_, gpointer user_data _U_)
+{
+ if (wlanstat_dlg_w) {
+ reactivate_window(wlanstat_dlg_w);
+ } else {
+ wlanstat_dlg_create ();
+ }
+}
+
+void
+register_tap_listener_wlanstat (void)
+{
+ static const char src[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+ SET_ADDRESS(&broadcast, AT_ETHER, 6, src);
+}
diff --git a/ui/gtk/wsp_stat.c b/ui/gtk/wsp_stat.c
new file mode 100644
index 0000000000..edcf1cf841
--- /dev/null
+++ b/ui/gtk/wsp_stat.c
@@ -0,0 +1,447 @@
+/* wsp_stat.c
+ * wsp_stat 2003 Jean-Michel FAYARD
+ *
+ * $Id$
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* #define DEBUG do{ printf("%s:%d ",__FILE__,__LINE__);} while(0); */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <epan/packet.h>
+#include <epan/packet_info.h>
+#include <epan/epan.h>
+#include <epan/tap.h>
+#include <epan/dissectors/packet-wsp.h>
+
+#include "../simple_dialog.h"
+#include "../globals.h"
+#include "../stat_menu.h"
+
+#include "ui/gtk/gui_utils.h"
+#include "ui/gtk/dlg_utils.h"
+#include "ui/gtk/tap_param_dlg.h"
+#include "ui/gtk/main.h"
+
+#include "ui/gtk/old-gtk-compat.h"
+
+/* used to keep track of the stats for a specific PDU type*/
+typedef struct _wsp_pdu_t {
+ GtkLabel *widget;
+ guint32 packets;
+} wsp_pdu_t;
+
+/* used to keep track of the statictics for an entire program interface */
+typedef struct _wsp_stats_t {
+ char *filter;
+ wsp_pdu_t *pdu_stats;
+ guint32 num_pdus;
+ GtkWidget *win;
+ GHashTable *hash;
+ GtkWidget *table_pdu_types;
+ GtkWidget *table_status_code;
+ guint index; /* Number of status code to display */
+} wspstat_t;
+/* used to keep track of a single type of status code */
+typedef struct _wsp_status_code_t {
+ const gchar *name;
+ guint32 packets;
+ GtkWidget *widget;/* label in which we print the number of packets */
+ wspstat_t *sp; /* entire program interface */
+} wsp_status_code_t;
+
+static void
+wsp_free_hash( gpointer key, gpointer value, gpointer user_data _U_ )
+{
+ g_free(key);
+ g_free(value);
+}
+static void
+wsp_reset_hash(gchar *key _U_ , wsp_status_code_t *data, gpointer ptr _U_ )
+{
+ data->packets = 0;
+}
+
+/* Update the entry corresponding to the number of packets of a special status code
+ * or create it if it don't exist.
+ */
+static void
+wsp_draw_statuscode(gchar *key _U_, wsp_status_code_t *data, gchar * unused _U_ )
+{
+ char string_buff[256];
+
+ if ((data==NULL) || (data->packets==0))
+ return;
+ if (data->widget==NULL){ /* create an entry in the table */
+ GtkWidget *tmp;
+ int x = 2*((data->sp->index) % 2);
+ int y = (data->sp->index) /2;
+
+
+ /* Maybe we should display the hexadecimal value ? */
+ /* g_snprintf(string_buff, sizeof(string_buff), "%s (0X%x)", data->name, *key); */
+ tmp = gtk_label_new( data->name /* string_buff */ );
+ gtk_table_attach_defaults(GTK_TABLE(data->sp->table_status_code), tmp, x, x+1, y, y+1);
+ gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
+ gtk_widget_show(tmp);
+
+ g_snprintf( string_buff, sizeof(string_buff), "%9d", data->packets );
+ data->widget = gtk_label_new( string_buff );
+ gtk_table_attach_defaults(GTK_TABLE(data->sp->table_status_code), data->widget, x+1, x+2, y, y+1);
+ gtk_label_set_justify(GTK_LABEL(data->widget), GTK_JUSTIFY_LEFT);
+ gtk_widget_show( data->widget );
+
+ data->sp->index++;
+ } else {
+ /* Just update the label string */
+ g_snprintf( string_buff, sizeof(string_buff), "%9d", data->packets );
+ gtk_label_set_text( GTK_LABEL(data->widget), string_buff);
+ }
+}
+static void
+wspstat_reset(void *psp)
+{
+ wspstat_t *sp=psp;
+ guint32 i;
+
+ for(i=1;i<=sp->num_pdus;i++)
+ {
+ sp->pdu_stats[i].packets=0;
+ }
+ g_hash_table_foreach( sp->hash, (GHFunc)wsp_reset_hash, NULL);
+}
+static gint
+pdut2index(gint pdut)
+{
+ if (pdut<=0x09) return pdut;
+ if (pdut>=0x40){
+ if (pdut <= 0x44){
+ return pdut-54;
+ } else if (pdut==0x60||pdut==0x61){
+ return pdut-81;
+ }
+ }
+ return 0;
+}
+static gint
+index2pdut(gint pdut)
+{
+ if (pdut<=0x09)
+ return pdut;
+ if (pdut<=14)
+ return pdut+54;
+ if (pdut<=16)
+ return pdut+81;
+ return 0;
+}
+
+static int
+wspstat_packet(void *psp, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *pri)
+{
+ wspstat_t *sp=psp;
+ const wsp_info_value_t *value=pri;
+ gint idx = pdut2index(value->pdut);
+ int retour=0;
+
+ if (value->status_code != 0) {
+ gint *key=g_malloc( sizeof(gint) );
+ wsp_status_code_t *sc;
+ *key=value->status_code ;
+ sc = g_hash_table_lookup(
+ sp->hash,
+ key);
+ if (!sc) {
+ g_warning("%s:%d What's Wrong, doc ?\n", __FILE__, __LINE__);
+ sc = g_malloc( sizeof(wsp_status_code_t) );
+ sc -> packets = 1;
+ sc -> name = NULL;
+ sc -> widget=NULL;
+ sc -> sp = sp;
+ g_hash_table_insert(
+ sp->hash,
+ key,
+ sc);
+ } else {
+ sc->packets++;
+ }
+ retour=1;
+ }
+
+
+
+ if (idx!=0) {
+ sp->pdu_stats[ idx ].packets++;
+ retour = 1;
+ }
+ return retour;
+
+}
+
+
+
+static void
+wspstat_draw(void *psp)
+{
+ wspstat_t *sp=psp;
+ guint32 i;
+ char str[256];
+ guint idx;
+
+ for(i=1;i<=sp->num_pdus ; i++)
+ {
+ g_snprintf(str, sizeof(str), "%9d", sp->pdu_stats[i ].packets);
+ gtk_label_set_text( GTK_LABEL(sp->pdu_stats[i].widget), str);
+ }
+
+ idx=sp->index;
+ g_hash_table_foreach( sp->hash, (GHFunc) wsp_draw_statuscode, NULL );
+ if (idx != sp->index){
+ /* We have inserted a new entry corresponding to a status code ,
+ * let's resize the table */
+ gtk_table_resize ( GTK_TABLE(sp->table_status_code), sp->index % 2 , 4);
+ }
+
+}
+
+
+
+/* since the gtk2 implementation of tap is multithreaded we must protect
+ * remove_tap_listener() from modifying the list while draw_tap_listener()
+ * is running. the other protected block is in main.c
+ *
+ * there should not be any other critical regions in gtk2
+ */
+static void
+win_destroy_cb(GtkWindow *win _U_, gpointer data)
+{
+ wspstat_t *sp=(wspstat_t *)data;
+
+ protect_thread_critical_region();
+ remove_tap_listener(sp);
+ unprotect_thread_critical_region();
+
+ g_free(sp->pdu_stats);
+ g_free(sp->filter);
+ g_hash_table_foreach( sp->hash, (GHFunc)wsp_free_hash, NULL);
+ g_hash_table_destroy( sp->hash);
+ g_free(sp);
+}
+
+static void
+add_table_entry(wspstat_t *sp, const char *str, int x, int y, int idx)
+{
+ GtkWidget *tmp;
+
+ tmp=gtk_label_new( str );
+ gtk_table_attach_defaults(GTK_TABLE(sp->table_pdu_types), tmp, x, x+1, y, y+1);
+ gtk_label_set_justify(GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
+ gtk_widget_show(tmp);
+ if (idx != 0) {
+ sp->pdu_stats [idx] .widget = GTK_LABEL( tmp ) ;
+ }
+}
+
+
+static void
+wsp_init_table(wspstat_t *sp)
+{
+ int pos=0;
+ guint32 i;
+ /* gchar buffer[51]; */
+
+ add_table_entry( sp, "PDU Type " , 0, pos, 0);
+ add_table_entry( sp, "packets " , 1, pos, 0);
+ add_table_entry( sp, "PDU Type " , 2, pos, 0);
+ add_table_entry( sp, "packets " , 3, pos, 0);
+ pos++;
+ for (i=1 ; i <= sp->num_pdus ; i++ )
+ {
+ int x = 0;
+ if (i> (sp->num_pdus+1) /2 ){
+ x=2;
+ }
+ /* Maybe we should display the hexadecimal value ? */
+ /* g_snprintf(buffer, sizeof(buffer), "%s (0X%x)", match_strval_ext( index2pdut( i ), &wsp_vals_pdu_type_ext), index2pdut(i) );*/
+ add_table_entry( sp,
+ match_strval_ext(index2pdut(i), &wsp_vals_pdu_type_ext), /* or buffer, */
+ x,
+ pos,
+ 0
+ );
+ add_table_entry( sp, "0", x+1, pos
+ , i /* keep a pointer to this widget to update it in _draw() */
+ );
+ pos++;
+ if (i== (sp->num_pdus+1) /2) {
+ pos=1;
+ }
+ }
+}
+
+/* When called, this function will create a new instance of gtk2-wspstat.
+ */
+static void
+gtk_wspstat_init(const char *optarg, void *userdata _U_)
+{
+ wspstat_t *sp;
+ const char *filter=NULL;
+ char *title=NULL;
+ GString *error_string;
+ GtkWidget *main_vb, *pdutypes_fr, *statuscode_fr ;
+ GtkWidget *bt_close;
+ GtkWidget *bbox;
+ guint32 i;
+ wsp_status_code_t *sc;
+ const value_string *wsp_vals_status_p;
+
+
+ if (strncmp (optarg, "wsp,stat,", 9) == 0){
+ filter=optarg+9;
+ } else {
+ filter=NULL;
+ }
+
+ sp = g_malloc( sizeof(wspstat_t) );
+ sp->win = dlg_window_new("wsp-stat"); /* transient_for top_level */
+ gtk_window_set_destroy_with_parent (GTK_WINDOW(sp->win), TRUE);
+
+ sp->hash = g_hash_table_new( g_int_hash, g_int_equal);
+ wsp_vals_status_p = VALUE_STRING_EXT_VS_P(&wsp_vals_status_ext);
+ for (i=0 ; wsp_vals_status_p[i].strptr ; i++ )
+ {
+ gint *key;
+ sc=g_malloc( sizeof(wsp_status_code_t) );
+ key=g_malloc( sizeof(gint) );
+ sc->name=wsp_vals_status_p[i].strptr;
+ sc->packets=0;
+ sc->widget=NULL;
+ sc->sp = sp;
+ *key=wsp_vals_status_p[i].value;
+ g_hash_table_insert(
+ sp->hash,
+ key,
+ sc);
+ }
+ sp->num_pdus = 16;
+ sp->pdu_stats=g_malloc( (sp->num_pdus+1) * sizeof( wsp_pdu_t) );
+ if(filter){
+ sp->filter=g_strdup(filter);
+ title=g_strdup_printf("Wireshark: WAP-WSP statistics with filter: %s", filter);
+ } else {
+ sp->filter=NULL;
+ title=g_strdup("Wireshark: WAP-WSP statistics");
+ }
+ for (i=0;i<=sp->num_pdus; i++)
+ {
+ sp->pdu_stats[i].packets=0;
+ }
+
+ gtk_window_set_title(GTK_WINDOW(sp->win), title);
+ g_free(title);
+
+ /* container for the two frames */
+ main_vb = gtk_vbox_new(FALSE, 3);
+ gtk_container_set_border_width(GTK_CONTAINER(main_vb), 12);
+ gtk_container_add(GTK_CONTAINER(sp->win), main_vb);
+
+ /* PDU Types frame */
+ pdutypes_fr = gtk_frame_new("Summary of PDU Types (wsp.pdu_type)");
+ gtk_container_add(GTK_CONTAINER(main_vb), pdutypes_fr);
+
+ sp->table_pdu_types = gtk_table_new( (sp->num_pdus+1) / 2 + 1, 4, FALSE);
+ gtk_container_add( GTK_CONTAINER( pdutypes_fr), sp->table_pdu_types);
+ gtk_container_set_border_width( GTK_CONTAINER(sp->table_pdu_types) , 10);
+
+ wsp_init_table(sp);
+
+ /* Status Codes frame */
+ statuscode_fr = gtk_frame_new("Summary of Status Code (wsp.reply.status)");
+ gtk_container_add(GTK_CONTAINER(main_vb), statuscode_fr);
+
+ sp->table_status_code = gtk_table_new( 0, 4, FALSE);
+ gtk_container_add( GTK_CONTAINER( statuscode_fr), sp->table_status_code);
+ gtk_container_set_border_width( GTK_CONTAINER(sp->table_status_code) , 10);
+ sp->index = 0; /* No answers to display yet */
+
+ error_string = register_tap_listener(
+ "wsp",
+ sp,
+ filter,
+ 0,
+ wspstat_reset,
+ wspstat_packet,
+ wspstat_draw);
+ if (error_string){
+ /* error, we failed to attach to the tap. clean up */
+ simple_dialog( ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str );
+ g_free(sp->pdu_stats);
+ g_free(sp->filter);
+ g_free(sp);
+ g_string_free(error_string, TRUE);
+ return ;
+ }
+
+ /* Button row. */
+ bbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
+ gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
+
+ bt_close = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
+ window_set_cancel_button(sp->win, bt_close, window_cancel_button_cb);
+
+ g_signal_connect(sp->win, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
+ g_signal_connect(sp->win, "destroy", G_CALLBACK(win_destroy_cb), sp);
+
+ gtk_widget_show_all(sp->win);
+ window_present(sp->win);
+
+ cf_retap_packets(&cfile);
+ gdk_window_raise(gtk_widget_get_window(sp->win));
+}
+
+static tap_param wsp_stat_params[] = {
+ { PARAM_FILTER, "Filter", NULL }
+};
+
+static tap_param_dlg wsp_stat_dlg = {
+ "WAP-WSP Packet Counter",
+ "wsp,stat",
+ gtk_wspstat_init,
+ -1,
+ G_N_ELEMENTS(wsp_stat_params),
+ wsp_stat_params
+};
+
+void
+register_tap_listener_gtkwspstat(void)
+{
+ register_dfilter_stat(&wsp_stat_dlg, "_WAP-WSP",
+ REGISTER_STAT_GROUP_TELEPHONY);
+}
+
+void wsp_stat_cb(GtkAction *action, gpointer user_data _U_)
+{
+ tap_param_dlg_cb(action, &wsp_stat_dlg);
+}
+
diff --git a/ui/qt/QtShark.pro b/ui/qt/QtShark.pro
index 2f91b73b22..9a90188f6d 100644
--- a/ui/qt/QtShark.pro
+++ b/ui/qt/QtShark.pro
@@ -37,13 +37,13 @@ win32:INCLUDEPATH += \
$${WIRESHARK_LIB_DIR}/AirPcap_Devpack_4_1_0_1622/Airpcap_Devpack/include \
$${WIRESHARK_LIB_DIR}/zlib125/include
-# XXX - If we add ../../gtk/recent.c to SOURCES, jom will try to compile everything
-# in ../../gtk. Until we move the things we need in recent.c to a common file, simply
+# XXX - If we add ../gtk/recent.c to SOURCES, jom will try to compile everything
+# in ../gtk. Until we move the things we need in recent.c to a common file, simply
# copy it to our current directory.
recent.target = recent.c
-!win32:recent.commands = $$QMAKE_COPY ../../gtk/$$recent.target .
-win32:recent.commands = $$QMAKE_COPY ..\\..\\gtk\\$$recent.target .
-recent.depends = ../../gtk/$$recent.target
+!win32:recent.commands = $$QMAKE_COPY ../gtk/$$recent.target .
+win32:recent.commands = $$QMAKE_COPY ..\\gtk\\$$recent.target .
+recent.depends = ../gtk/$$recent.target
QMAKE_EXTRA_TARGETS += recent
SOURCES += \
diff --git a/ui/qt/capture_interface_dialog.cpp b/ui/qt/capture_interface_dialog.cpp
index d1817d96cf..be9114a4de 100644
--- a/ui/qt/capture_interface_dialog.cpp
+++ b/ui/qt/capture_interface_dialog.cpp
@@ -27,7 +27,7 @@
#include "qt_ui_utils.h"
-#include "gtk/recent.h"
+#include "ui/gtk/recent.h"
#include <epan/prefs.h>
diff --git a/ui/qt/display_filter_combo.cpp b/ui/qt/display_filter_combo.cpp
index 626d2198be..48eaa7aafd 100644
--- a/ui/qt/display_filter_combo.cpp
+++ b/ui/qt/display_filter_combo.cpp
@@ -24,7 +24,7 @@
#include <stdio.h>
#include "qt_ui_utils.h"
-#include "gtk/recent.h"
+#include "ui/gtk/recent.h"
#include "display_filter_edit.h"
diff --git a/ui/qt/display_filter_edit.cpp b/ui/qt/display_filter_edit.cpp
index 84a1b55a48..fd8c3a0cab 100644
--- a/ui/qt/display_filter_edit.cpp
+++ b/ui/qt/display_filter_edit.cpp
@@ -31,7 +31,7 @@
#include "display_filter_edit.h"
-#include "gtk/utf8_entities.h"
+#include "ui/gtk/utf8_entities.h"
// platform
// osx
diff --git a/ui/qt/interface_tree.cpp b/ui/qt/interface_tree.cpp
index 39e4001462..b6061bde34 100644
--- a/ui/qt/interface_tree.cpp
+++ b/ui/qt/interface_tree.cpp
@@ -55,7 +55,7 @@ InterfaceTree::InterfaceTree(QWidget *parent) :
);
if_list = capture_interface_list(&err, &err_str);
- g_log(NULL, G_LOG_LEVEL_DEBUG, "FIX: move if_list_comparator_alph out of gtk/");
+ g_log(NULL, G_LOG_LEVEL_DEBUG, "FIX: move if_list_comparator_alph out of ui/gtk/");
// if_list = g_list_sort(if_list, if_list_comparator_alph);
if (if_list == NULL && err == CANT_GET_INTERFACE_LIST) {
diff --git a/ui/qt/main.cpp b/ui/qt/main.cpp
index 60447be151..e149176782 100644
--- a/ui/qt/main.cpp
+++ b/ui/qt/main.cpp
@@ -80,7 +80,7 @@
#include "u3.h"
#include <wsutil/file_util.h>
-#include "gtk/recent.h"
+#include "ui/gtk/recent.h"
#ifdef HAVE_LIBPCAP
#include "capture_ui_utils.h"
@@ -141,7 +141,7 @@ void create_console(void);
capture_options global_capture_opts;
#endif
-// Copied from gtk/gui_utils.c
+// Copied from ui/gtk/gui_utils.c
void pipe_input_set_handler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
{
// static pipe_input_t pipe_input;
@@ -179,7 +179,7 @@ main_cf_callback(gint event, gpointer data, gpointer user_data )
wsApp->captureFileCallback(event, data);
}
-// XXX Copied from gtk/main.c. This should be moved to a common location.
+// XXX Copied from ui/gtk/main.c. This should be moved to a common location.
static e_prefs *
read_configuration_files(char **gdp_path, char **dp_path)
{
diff --git a/ui/qt/main_window.cpp b/ui/qt/main_window.cpp
index db15c0a65e..56cfa832ea 100644
--- a/ui/qt/main_window.cpp
+++ b/ui/qt/main_window.cpp
@@ -250,7 +250,7 @@ void MainWindow::recentActionTriggered() {
}
}
-// XXX - Copied from gtk/menus.c
+// XXX - Copied from ui/gtk/menus.c
/**
* Add the capture filename (with an absolute path) to the "Recent Files" menu.
diff --git a/ui/qt/packet_list.cpp b/ui/qt/packet_list.cpp
index a00bbc63b8..91cbb4cb82 100644
--- a/ui/qt/packet_list.cpp
+++ b/ui/qt/packet_list.cpp
@@ -43,7 +43,7 @@
#include "qt_ui_utils.h"
#include "main_statusbar.h"
-#include "gtk/recent.h"
+#include "ui/gtk/recent.h"
#include <QTreeWidget>
#include <QTabWidget>
@@ -74,7 +74,7 @@ new_packet_list_append(column_info *cinfo, frame_data *fdata, packet_info *pinfo
return visible_pos;
}
-// Copied from gtk/new_packet_list.c
+// Copied from ui/gtk/new_packet_list.c
void new_packet_list_resize_column(gint col)
{
// xxx qtshark
diff --git a/ui/qt/qt_ui_utils.cpp b/ui/qt/qt_ui_utils.cpp
index 5f694265e8..8a40b661b1 100644
--- a/ui/qt/qt_ui_utils.cpp
+++ b/ui/qt/qt_ui_utils.cpp
@@ -26,9 +26,9 @@
#include "qt_ui_utils.h"
-#include "gtk/recent.h"
+#include "ui/gtk/recent.h"
-// XXX - Copied from gtk/gui_utils.c
+// XXX - Copied from ui/gtk/gui_utils.c
#define WINDOW_GEOM_KEY "window_geom"
diff --git a/ui/qt/qt_ui_utils.h b/ui/qt/qt_ui_utils.h
index 50b0808f8c..bb5ce0d21a 100644
--- a/ui/qt/qt_ui_utils.h
+++ b/ui/qt/qt_ui_utils.h
@@ -25,7 +25,7 @@
#ifndef __QT_UI_UTILS_H__
#define __QT_UI_UTILS_H__
-// xxx - copied from gtk/gui_utils.h
+// xxx - copied from ui/gtk/gui_utils.h
#include <stdio.h>
diff --git a/ui/qt/wireshark_application.cpp b/ui/qt/wireshark_application.cpp
index b2b0981f0e..2d1a359723 100644
--- a/ui/qt/wireshark_application.cpp
+++ b/ui/qt/wireshark_application.cpp
@@ -40,7 +40,7 @@
WiresharkApplication *wsApp = NULL;
-// XXX - Copied from gtk/file_dlg.c
+// XXX - Copied from ui/gtk/file_dlg.c
static char *last_open_dir = NULL;
static bool updated_last_open_dir = FALSE;