aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--capture_opts.c8
-rw-r--r--capture_opts.h2
-rw-r--r--doc/README.extcap189
-rwxr-xr-xdoc/extcap_example.py175
-rw-r--r--docbook/release-notes.asciidoc2
-rw-r--r--extcap.c171
-rw-r--r--extcap.h7
-rw-r--r--extcap_parser.c215
-rw-r--r--extcap_parser.h9
-rw-r--r--sync_pipe.h1
-rw-r--r--ui/CMakeLists.txt1
-rw-r--r--ui/Makefile.am2
-rw-r--r--ui/iface_toolbar.c68
-rw-r--r--ui/iface_toolbar.h105
-rw-r--r--ui/qt/CMakeLists.txt7
-rw-r--r--ui/qt/Makefile.am10
-rw-r--r--ui/qt/funnel_text_dialog.cpp2
-rw-r--r--ui/qt/interface_toolbar.cpp895
-rw-r--r--ui/qt/interface_toolbar.h120
-rw-r--r--ui/qt/interface_toolbar.ui69
-rw-r--r--ui/qt/interface_toolbar_lineedit.cpp149
-rw-r--r--ui/qt/interface_toolbar_lineedit.h71
-rw-r--r--ui/qt/interface_toolbar_reader.cpp140
-rw-r--r--ui/qt/interface_toolbar_reader.h65
-rw-r--r--ui/qt/main_window.cpp126
-rw-r--r--ui/qt/main_window.h6
-rw-r--r--ui/qt/main_window.ui6
-rw-r--r--ui/qt/main_window_slots.cpp40
-rw-r--r--ui/recent.c14
-rw-r--r--ui/recent.h1
30 files changed, 2648 insertions, 28 deletions
diff --git a/capture_opts.c b/capture_opts.c
index 69a24579c2..4a7c8f2ac2 100644
--- a/capture_opts.c
+++ b/capture_opts.c
@@ -68,6 +68,8 @@ capture_opts_init(capture_options *capture_opts)
capture_opts->default_options.extcap_args = NULL;
capture_opts->default_options.extcap_userdata = NULL;
capture_opts->default_options.extcap_pid = INVALID_EXTCAP_PID;
+ capture_opts->default_options.extcap_control_in = NULL;
+ capture_opts->default_options.extcap_control_out = NULL;
#endif
#ifdef CAN_SET_CAPTURE_BUFFER_SIZE
capture_opts->default_options.buffer_size = DEFAULT_CAPTURE_BUFFER_SIZE;
@@ -702,6 +704,8 @@ capture_opts_add_iface_opt(capture_options *capture_opts, const char *optarg_str
interface_opts.extcap_args = NULL;
interface_opts.extcap_pid = INVALID_EXTCAP_PID;
interface_opts.extcap_userdata = NULL;
+ interface_opts.extcap_control_in = g_strdup(capture_opts->default_options.extcap_control_in);
+ interface_opts.extcap_control_out = g_strdup(capture_opts->default_options.extcap_control_out);
#endif
#ifdef CAN_SET_CAPTURE_BUFFER_SIZE
interface_opts.buffer_size = capture_opts->default_options.buffer_size;
@@ -1129,6 +1133,8 @@ capture_opts_del_iface(capture_options *capture_opts, guint if_index)
if (interface_opts.extcap_pid != INVALID_EXTCAP_PID)
g_spawn_close_pid(interface_opts.extcap_pid);
g_free(interface_opts.extcap_userdata);
+ g_free(interface_opts.extcap_control_in);
+ g_free(interface_opts.extcap_control_out);
#endif
#ifdef HAVE_PCAP_REMOTE
if (interface_opts.src_type == CAPTURE_IFREMOTE) {
@@ -1180,6 +1186,8 @@ collect_ifaces(capture_options *capture_opts)
if (interface_opts.extcap_args)
g_hash_table_ref(interface_opts.extcap_args);
interface_opts.extcap_userdata = NULL;
+ interface_opts.extcap_control_in = NULL;
+ interface_opts.extcap_control_out = NULL;
#endif
#ifdef CAN_SET_CAPTURE_BUFFER_SIZE
interface_opts.buffer_size = device.buffer;
diff --git a/capture_opts.h b/capture_opts.h
index 780bbca526..d9ab3d86ab 100644
--- a/capture_opts.h
+++ b/capture_opts.h
@@ -226,6 +226,8 @@ typedef struct interface_options_tag {
GPid extcap_pid; /* pid of running process or INVALID_EXTCAP_PID */
gpointer extcap_userdata;
guint extcap_child_watch;
+ gchar *extcap_control_in;
+ gchar *extcap_control_out;
#endif
#ifdef CAN_SET_CAPTURE_BUFFER_SIZE
int buffer_size;
diff --git a/doc/README.extcap b/doc/README.extcap
index 94ca73584b..50db8f67a3 100644
--- a/doc/README.extcap
+++ b/doc/README.extcap
@@ -196,6 +196,195 @@ such a check is the same as for Qt RegExp classes. This feature is only active i
Qt version of Wireshark.
+TOOLBAR CONTROLS
+================
+An extcap utility can provide configuration for controls to use in an interface toolbar.
+This controls are bidirectional and can be used to control the extcap utility while
+capturing.
+
+This is useful in scenarios where configuration can be done based on findings in the
+capture process, setting temporary values or give other inputs without restarting the
+current capture.
+
+Example:
+
+$ extcapbin --extcap-interfaces
+extcap {version=1.0}{display=Example extcap interface}
+interface {value=example1}{display=Example interface 1 for extcap}
+interface {value=example2}{display=Example interface 2 for extcap}
+control {number=0}{type=string}{display=Message}{tooltip=Package message content. Must start with a capital letter.}{validation=[A-Z]+}{required=true}
+control {number=1}{type=selector}{display=Time delay}{tooltip=Time delay between packages}
+control {number=2}{type=boolean}{display=Verify}{default=true}{tooltip=Verify package content}
+control {number=3}{type=button}{display=Turn on}{tooltip=Turn on or off}
+control {number=4}{type=button}{role=logger}{display=Log}{tooltip=Show capture log}
+value {control=1}{value=1}{display=1 sec}
+value {control=1}{value=2}{display=2 sec}{default=true}
+
+All controls will be presented as GUI elements in a toolbar specific to the extcap
+utility. The extcap must not rely on using those controls (they are optional) because
+of other capturing tools not using GUI (e.g. tshark, tfshark).
+
+
+CONTROLS
+========
+The controls are similar to the ARGUMENTS, but without the CALL element. All controls
+may be given a default value at startup and most can be changed during capture, both
+by the extcap and the user (depending on the type of control).
+
+All controls must provide a NUMBER, by which they are identified. No NUMBER may be
+provided twice. Also all options must present the elements TYPE and DISPLAY, where
+TYPE provides the type of control to add to the toolbar and DISPLAY the name in the GUI.
+
+Additionally TOOLTIP and PLACEHOLDER may be provided, which will give the user an
+explanation within the GUI.
+
+All controls, except from the logger, help and reset buttons, may be disabled
+(and enabled) in GUI by the extcap during capture. This can be because of set-once
+operations, or operations which takes some time to complete.
+
+All control values which are changed by the user (not equal to the default value) will
+be sent to the extcap utility when starting a capture. The extcap utility may choose
+to discard initial values and set new values, depending on implementation.
+
+This TYPEs are defined as controls:
+
+ * BOOLEAN - This provides a checkbox with the possibility to set a true/false value.
+
+ The extcap utility can set a default value at startup, and can change (set) and receive
+ value changes while capturing. When starting a capture the GUI will send the value if
+ different from the default value.
+
+ The payload is one byte with binary value 0 or 1.
+
+ Valid Commands: Set value, Enable, Disable.
+
+ * BUTTON - This provides a button with different ROLEs:
+
+ ** CONTROL - This button will send a signal.
+ This is the default role if nothing is configured.
+
+ The extcap utility can set the button text at startup, and can change (set) the
+ button text and receive button press signals while capturing. The button is
+ disabled and the button text is restored to the default text when not capturing.
+
+ The payload is either the button text or empty (signal).
+
+ Valid Commands: Set value, Enable, Disable.
+
+ ** LOGGER - This provides a logger mechanism where the extcap utility can send log
+ entries to be presented in a log window. This communication is unidirectional.
+
+ The payload is the log entry, and should be ended with a newline.
+ Maximum length is 65535 bytes.
+
+ Valid Commands: Set log entry, Add log entry.
+
+ The Set command will clear the log before adding the entry.
+
+ ** HELP - This button opens the help page, if configured.
+ This type has no controls and will not be used in communication.
+
+ Valid Commands: NONE.
+
+ ** RESET - This button will restore all control values to default.
+ This type has no controls and will not be used in communication.
+
+ Valid Commands: NONE.
+
+ * SELECTOR - This provides a combo box with fixed values which can be selected.
+
+ The extcap utility can set default values at startup, and add and remove values and
+ receive change in value selection while capturing. When starting a capture the GUI
+ will send the value if different from the default value.
+
+ The payload is a string with the value, and optionally a string with a display
+ value if this is different from the value. This two string values are separated
+ by a null character.
+
+ Valid Commands: Set selected value, Add value, Remove value, Enable, Disable.
+
+ If value is empty the Remove command will remove all entries.
+
+ * STRING - This provides a text edit line with the possibility to set a string or any
+ value which can be represented in a string (integer, float, date, etc.).
+
+ The extcap utility can set a default string value at startup, and can change (set) and
+ receive value changes while capturing. When starting a capture the GUI will send the
+ value if different from the default value.
+
+ The payload is a string with the value. Maximum length is 32767 bytes.
+
+ Valid Commands: Set value, Enable, Disable.
+
+ The element VALIDATION allows to provide a regular expression string, which is used
+ to check the user input for validity beyond normal data type or range checks.
+ Back-slashes must be escaped (as in \\b for \b).
+
+
+MESSAGES
+========
+In addition to the controls it's possible to send a single message from the extcap
+utility to the user. This message can be put in the status bar or displayed in a
+information, warning or error dialog which must be accepted by the user. This message
+does not use the NUMBER argument so this can have any value.
+
+
+CONTROL PROTOCOL
+================
+The protocol used to communicate over the control pipes has a fixed size header of
+6 bytes and a payload with 0 - 65535 bytes.
+
+Control packet:
+
+ +----+----+----+----+----+----+----+----+
+ | Sync Pipe Indication (1 byte) |
+ +----+----+----+----+----+----+----+----+
+ | Message Length |
+ | (3 bytes network order) |
+ +----+----+----+----+----+----+----+----+
+ | Control Number (1 byte) |
+ +----+----+----+----+----+----+----+----+
+ | Command (1 byte) |
+ +----+----+----+----+----+----+----+----+
+ | Payload |
+ | (0 - 65535 bytes) |
+ +----+----+----+----+----+----+----+----+
+
+ Sync Pipe Indication:
+ The common sync pipe indication. This protocol uses the value 'T'.
+
+ Message Length:
+ Payload length + 2 bytes for argument number and command.
+
+ Control Number:
+ Unique number to identify the control. This number also gives the order of
+ the controls in the interface toolbar.
+
+ Command: Control type:
+ 0 = Initialized none
+ 1 = Set boolean / button / logger / selector / string
+ 2 = Add logger / selector
+ 3 = Remove selector
+ 4 = Enable boolean / button / selector / string
+ 5 = Disable boolean / button / selector / string
+ 6 = Statusbar message none
+ 7 = Information message none
+ 8 = Warning message none
+ 9 = Error message none
+
+ Payload Length:
+ The length of the following payload. Maximum length is 65535 bytes.
+
+The Initialized command will be sent from the GUI to the extcap utility when all
+initial control values are sent after starting a capture. This is an indication
+that the GUI is ready to receive control values.
+
+The GUI will only send Initialized and Set commands. The extcap utility shall not
+send the Initialized command.
+
+Messages with unknown control number or command will be silently ignored.
+
+
DEVELOPMENT
===========
To have extcap support, extcap must be enabled. Moreover the specific extcap must
diff --git a/doc/extcap_example.py b/doc/extcap_example.py
index 6ab5699afb..e7ea7b2d6b 100755
--- a/doc/extcap_example.py
+++ b/doc/extcap_example.py
@@ -51,13 +51,38 @@ import struct
import binascii
from threading import Thread
-ERROR_USAGE = 0
-ERROR_ARG = 1
-ERROR_INTERFACE = 2
-ERROR_FIFO = 3
-ERROR_DELAY = 4
-
-globalinterface = 0
+ERROR_USAGE = 0
+ERROR_ARG = 1
+ERROR_INTERFACE = 2
+ERROR_FIFO = 3
+ERROR_DELAY = 4
+
+CTRL_CMD_INITIALIZED = 0
+CTRL_CMD_SET = 1
+CTRL_CMD_ADD = 2
+CTRL_CMD_REMOVE = 3
+CTRL_CMD_ENABLE = 4
+CTRL_CMD_DISABLE = 5
+CTRL_CMD_STATUSBAR = 6
+CTRL_CMD_INFORMATION = 7
+CTRL_CMD_WARNING = 8
+CTRL_CMD_ERROR = 9
+
+CTRL_ARG_MESSAGE = 0
+CTRL_ARG_DELAY = 1
+CTRL_ARG_VERIFY = 2
+CTRL_ARG_BUTTON = 3
+CTRL_ARG_HELP = 4
+CTRL_ARG_RESET = 5
+CTRL_ARG_LOGGER = 6
+CTRL_ARG_NONE = 255
+
+initialized = False
+message = ''
+delay = 0.0
+verify = False
+button = False
+button_disabled = False
"""
This code has been taken from http://stackoverflow.com/questions/5943249/python-argparse-and-controlling-overriding-the-exit-status-code - originally developed by Rob Cowie http://stackoverflow.com/users/46690/rob-cowie
@@ -128,12 +153,29 @@ def extcap_config(interface):
def extcap_interfaces():
- print ("extcap {version=1.0}{help=http://www.wireshark.org}")
- print ("interface {value=example1}{display=Example interface usage for extcap}")
+ print ("extcap {version=1.0}{help=http://www.wireshark.org}{display=Example extcap interface}")
+ print ("interface {value=example1}{display=Example interface 1 for extcap}")
+ print ("interface {value=example2}{display=Example interface 2 for extcap}")
+ print ("control {number=%d}{type=string}{display=Message}{tooltip=Package message content. Must start with a capital letter.}{placeholder=Enter package message content here ...}{validation=^[A-Z]+}" % CTRL_ARG_MESSAGE)
+ print ("control {number=%d}{type=selector}{display=Time delay}{tooltip=Time delay between packages}" % CTRL_ARG_DELAY)
+ print ("control {number=%d}{type=boolean}{display=Verify}{default=true}{tooltip=Verify package content}" % CTRL_ARG_VERIFY)
+ print ("control {number=%d}{type=button}{display=Turn on}{tooltip=Turn on or off}" % CTRL_ARG_BUTTON)
+ print ("control {number=%d}{type=button}{role=help}{display=Help}{tooltip=Show help}" % CTRL_ARG_HELP)
+ print ("control {number=%d}{type=button}{role=reset}{display=Reset}{tooltip=Restore default values}" % CTRL_ARG_RESET)
+ print ("control {number=%d}{type=button}{role=logger}{display=Log}{tooltip=Show capture log}" % CTRL_ARG_LOGGER)
+ print ("value {control=%d}{value=1}{display=1}" % CTRL_ARG_DELAY)
+ print ("value {control=%d}{value=2}{display=2}" % CTRL_ARG_DELAY)
+ print ("value {control=%d}{value=3}{display=3}" % CTRL_ARG_DELAY)
+ print ("value {control=%d}{value=4}{display=4}" % CTRL_ARG_DELAY)
+ print ("value {control=%d}{value=5}{display=5}{default=true}" % CTRL_ARG_DELAY)
+ print ("value {control=%d}{value=60}{display=60}" % CTRL_ARG_DELAY)
+
def extcap_dlts(interface):
if ( interface == '1' ):
print ("dlt {number=147}{name=USER0}{display=Demo Implementation for Extcap}")
+ elif ( interface == '2' ):
+ print ("dlt {number=148}{name=USER1}{display=Demo Implementation for Extcap}")
"""
@@ -216,20 +258,125 @@ def pcap_fake_package ( message, fake_ip ):
pcap += message
return pcap
-def extcap_capture(interface, fifo, delay, verify, message, remote, fake_ip):
- tdelay = delay if delay != 0 else 5
+def control_read(fn):
+ try:
+ header = fn.read(6)
+ sp, _, length, arg, typ = struct.unpack('>sBHBB', header)
+ if length > 2:
+ payload = fn.read(length - 2)
+ else:
+ payload = ''
+ return arg, typ, payload
+ except:
+ return None, None, None
+
+def control_read_thread(control_in, fn_out):
+ global initialized, message, delay, verify, button, button_disabled
+ with open(control_in, 'rb', 0 ) as fn:
+ arg = 0
+ while arg != None:
+ arg, typ, payload = control_read(fn)
+ log = ''
+ if typ == CTRL_CMD_INITIALIZED:
+ initialized = True
+ elif arg == CTRL_ARG_MESSAGE:
+ message = payload
+ log = "Message = " + payload
+ elif arg == CTRL_ARG_DELAY:
+ delay = float(payload)
+ log = "Time delay = " + payload
+ elif arg == CTRL_ARG_VERIFY:
+ # Only read this after initialized
+ if initialized:
+ verify = (payload[0] != '\0')
+ log = "Verify = " + str(verify)
+ control_write(fn_out, CTRL_ARG_NONE, CTRL_CMD_STATUSBAR, "Verify changed")
+ elif arg == CTRL_ARG_BUTTON:
+ control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_DISABLE, "")
+ button_disabled = True
+ if button == True:
+ control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_SET, "Turn on")
+ button = False
+ log = "Button turned off"
+ else:
+ control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_SET, "Turn off")
+ button = True
+ log = "Button turned on"
+
+ if len(log) > 0:
+ control_write(fn_out, CTRL_ARG_LOGGER, CTRL_CMD_ADD, log + "\n")
+
+def control_write(fn, arg, typ, payload):
+ packet = bytearray()
+ packet += struct.pack('>sBHBB', 'T', 0, len(payload) + 2, arg, typ)
+ packet += payload
+ fn.write(packet)
+
+def control_write_defaults(fn_out):
+ global initialized, message, delay, verify
+
+ while not initialized:
+ time.sleep(.1) # Wait for initial control values
+
+ # Write startup configuration to Toolbar controls
+ control_write(fn_out, CTRL_ARG_MESSAGE, CTRL_CMD_SET, message)
+ control_write(fn_out, CTRL_ARG_DELAY, CTRL_CMD_SET, str(delay))
+ control_write(fn_out, CTRL_ARG_VERIFY, CTRL_CMD_SET, struct.pack('B', verify))
+
+ for i in range(1,16):
+ item = bytearray()
+ item += str(i) + struct.pack('B', 0) + str(i) + " sec"
+ control_write(fn_out, CTRL_ARG_DELAY, CTRL_CMD_ADD, item)
+
+ control_write(fn_out, CTRL_ARG_DELAY, CTRL_CMD_REMOVE, str(60))
+
+def extcap_capture(interface, fifo, control_in, control_out, in_delay, in_verify, in_message, remote, fake_ip):
+ global message, delay, verify, button_disabled
+ delay = in_delay if in_delay != 0 else 5
+ message = in_message
+ verify = in_verify
+ counter = 1
if not os.path.exists(fifo):
print ( "Fifo does not exist, exiting!", file=sys.stderr )
sys.exit(1)
+ fn_out = None
+ if control_out != None:
+ fn_out = open(control_out, 'wb', 0)
+ control_write(fn_out, CTRL_ARG_LOGGER, CTRL_CMD_SET, "Log started at " + time.strftime("%c") + "\n")
+
+
+ if control_in != None:
+ # Start reading thread
+ thread = Thread(target = control_read_thread, args = (control_in, fn_out))
+ thread.start()
+
+
+ if fn_out != None:
+ control_write_defaults(fn_out)
+
with open(fifo, 'wb', 0 ) as fh:
fh.write (pcap_fake_header())
while True:
+ if fn_out != None:
+ log = "Received packet #" + str(counter) + "\n"
+ control_write(fn_out, CTRL_ARG_LOGGER, CTRL_CMD_ADD, log)
+ counter = counter + 1
+
+ if button_disabled == True:
+ control_write(fn_out, CTRL_ARG_BUTTON, CTRL_CMD_ENABLE, "")
+ control_write(fn_out, CTRL_ARG_NONE, CTRL_CMD_INFORMATION, "Turn action finished.")
+ button_disabled = False
+
out = ("%s|%04X%s|%s" % ( remote.strip(), len(message), message, verify )).encode("utf8")
fh.write (pcap_fake_package(out, fake_ip))
- time.sleep(tdelay)
+ time.sleep(delay)
+
+ thread.join()
+ if fn_out != None:
+ fn_out.close()
def extcap_close_fifo(fifo):
if not os.path.exists(fifo):
@@ -268,6 +415,8 @@ if __name__ == '__main__':
parser.add_argument("--extcap-config", help="Provide a list of configurations for the given interface", action="store_true")
parser.add_argument("--extcap-capture-filter", help="Used together with capture to provide a capture filter")
parser.add_argument("--fifo", help="Use together with capture to provide the fifo to dump data to")
+ parser.add_argument("--extcap-control-in", help="Use together with capture to provide the fifo to dump data to")
+ parser.add_argument("--extcap-control-out", help="Use together with capture to provide the fifo to dump data to")
# Interface Arguments
parser.add_argument("--verify", help="Demonstrates a verification bool flag", action="store_true" )
@@ -334,7 +483,7 @@ if __name__ == '__main__':
sys.exit(ERROR_DELAY)
try:
- extcap_capture(interface, args.fifo, args.delay, args.verify, message, args.remote, fake_ip)
+ extcap_capture(interface, args.fifo, args.extcap_control_in, args.extcap_control_out, args.delay, args.verify, message, args.remote, fake_ip)
except KeyboardInterrupt:
pass
else:
diff --git a/docbook/release-notes.asciidoc b/docbook/release-notes.asciidoc
index a76cb578d3..d95bc6c41b 100644
--- a/docbook/release-notes.asciidoc
+++ b/docbook/release-notes.asciidoc
@@ -48,6 +48,8 @@ since version 2.2.0:
* You can move back and forth in the selection history in the Qt UI.
* IEEE 802.15.4 dissector now uses an UAT for decryption keys. The original
decryption key preference has been obsoleted.
+* Extcap utilities can now provide configuration for a GUI interface toolbar to
+ control the extcap utility while capturing.
//=== Removed Dissectors
diff --git a/extcap.c b/extcap.c
index 87ec4f5d00..c9e357bf19 100644
--- a/extcap.c
+++ b/extcap.c
@@ -49,6 +49,8 @@
#include <epan/prefs.h>
+#include "ui/iface_toolbar.h"
+
#include <wsutil/file_util.h>
#include <wsutil/filesystem.h>
#include <wsutil/tempfile.h>
@@ -77,6 +79,11 @@ static GHashTable * _loaded_interfaces = NULL;
*/
static GHashTable * _tool_for_ifname = NULL;
+/* internal container, for all the extcap executables that have been found
+ * and that provides a toolbar with controls to be added to a Interface Toolbar
+ */
+static GHashTable *_toolbars = NULL;
+
/* internal container, to map preference names to pointers that hold preference
* values. These ensure that preferences can survive extcap if garbage
* collection, and does not lead to dangling pointers in the prefs subsystem.
@@ -198,6 +205,56 @@ extcap_find_interface_for_ifname(const gchar *ifname)
return result;
}
+static void
+extcap_free_toolbar_value(iface_toolbar_value *value)
+{
+ if (!value)
+ {
+ return;
+ }
+
+ g_free(value->value);
+ g_free(value->display);
+ g_free(value);
+}
+
+static void
+extcap_free_toolbar_control(iface_toolbar_control *control)
+{
+ if (!control)
+ {
+ return;
+ }
+
+ g_free(control->display);
+ g_free(control->validation);
+ g_free(control->tooltip);
+ if (control->ctrl_type == INTERFACE_TYPE_STRING) {
+ g_free(control->default_value.string);
+ }
+ g_list_foreach(control->values, (GFunc)extcap_free_toolbar_value, NULL);
+ g_list_free(control->values);
+ g_free(control);
+}
+
+static void
+extcap_free_toolbar(gpointer data)
+{
+ if (!data)
+ {
+ return;
+ }
+
+ iface_toolbar *toolbar = (iface_toolbar *)data;
+
+ g_free(toolbar->menu_title);
+ g_free(toolbar->help);
+ g_list_free_full(toolbar->ifnames, g_free);
+ g_list_foreach(toolbar->controls, (GFunc)extcap_free_toolbar_control, NULL);
+ g_list_free(toolbar->controls);
+ g_free(toolbar);
+}
+
static gboolean
extcap_if_exists_for_extcap(const gchar *ifname, const gchar *extcap)
{
@@ -218,6 +275,26 @@ extcap_if_executable(const gchar *ifname)
return interface != NULL ? interface->extcap_path : NULL;
}
+static void
+extcap_iface_toolbar_add(const gchar *extcap, iface_toolbar *toolbar_entry)
+{
+ char *toolname;
+
+ if (!extcap || !toolbar_entry)
+ {
+ return;
+ }
+
+ toolname = g_path_get_basename(extcap);
+
+ if (!g_hash_table_lookup(_toolbars, toolname))
+ {
+ g_hash_table_insert(_toolbars, g_strdup(toolname), toolbar_entry);
+ }
+
+ g_free(toolname);
+}
+
/* Note: args does not need to be NULL-terminated. */
static gboolean extcap_foreach(gint argc, gchar **args,
extcap_cb_t cb, extcap_callback_info_t cb_info)
@@ -815,6 +892,27 @@ extcap_has_configuration(const char *ifname, gboolean is_required)
return found;
}
+gboolean
+extcap_has_toolbar(const char *ifname)
+{
+ if (!iface_toolbar_use())
+ {
+ return FALSE;
+ }
+
+ GList *toolbar_list = g_hash_table_get_values (_toolbars);
+ for (GList *walker = toolbar_list; walker; walker = walker->next)
+ {
+ iface_toolbar *toolbar = (iface_toolbar *) walker->data;
+ if (g_list_find_custom(toolbar->ifnames, ifname, (GCompareFunc) strcmp))
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
/* taken from capchild/capture_sync.c */
static gboolean pipe_data_available(int pipe_fd)
{
@@ -896,6 +994,16 @@ void extcap_if_cleanup(capture_options *capture_opts, gchar **errormsg)
ws_unlink(interface_opts.extcap_fifo);
interface_opts.extcap_fifo = NULL;
}
+ if (interface_opts.extcap_control_in && file_exists(interface_opts.extcap_control_in))
+ {
+ ws_unlink(interface_opts.extcap_control_in);
+ interface_opts.extcap_control_in = NULL;
+ }
+ if (interface_opts.extcap_control_out && file_exists(interface_opts.extcap_control_out))
+ {
+ ws_unlink(interface_opts.extcap_control_out);
+ interface_opts.extcap_control_out = NULL;
+ }
#endif
/* Maybe the client closed and removed fifo, but ws should check if
* pid should be closed */
@@ -1088,6 +1196,16 @@ GPtrArray *extcap_prepare_arguments(interface_options interface_opts)
}
add_arg(EXTCAP_ARGUMENT_RUN_PIPE);
add_arg(interface_opts.extcap_fifo);
+ if (interface_opts.extcap_control_in)
+ {
+ add_arg(EXTCAP_ARGUMENT_CONTROL_OUT);
+ add_arg(interface_opts.extcap_control_in);
+ }
+ if (interface_opts.extcap_control_out)
+ {
+ add_arg(EXTCAP_ARGUMENT_CONTROL_IN);
+ add_arg(interface_opts.extcap_control_out);
+ }
if (interface_opts.extcap_args == NULL || g_hash_table_size(interface_opts.extcap_args) == 0)
{
/* User did not perform interface configuration.
@@ -1189,6 +1307,13 @@ extcap_init_interfaces(capture_options *capture_opts)
continue;
}
+ /* create control pipes if having toolbar */
+ if (extcap_has_toolbar(interface_opts.name))
+ {
+ extcap_create_pipe(&interface_opts.extcap_control_in);
+ extcap_create_pipe(&interface_opts.extcap_control_out);
+ }
+
/* create pipe for fifo */
if (!extcap_create_pipe(&interface_opts.extcap_fifo))
{
@@ -1393,15 +1518,22 @@ static void remove_extcap_entry(gpointer entry, gpointer data _U_)
static gboolean cb_load_interfaces(extcap_callback_info_t cb_info)
{
- GList * interfaces = NULL, * walker = NULL;
+ GList * interfaces = NULL, * control_items = NULL, * walker = NULL;
extcap_interface * int_iter = NULL;
extcap_info * element = NULL;
+ iface_toolbar * toolbar_entry = NULL;
gchar * toolname = g_path_get_basename(cb_info.extcap);
GList * interface_keys = g_hash_table_get_keys(_loaded_interfaces);
/* Load interfaces from utility */
- interfaces = extcap_parse_interfaces(cb_info.output);
+ interfaces = extcap_parse_interfaces(cb_info.output, &control_items);
+
+ if (control_items)
+ {
+ toolbar_entry = g_new0(iface_toolbar, 1);
+ toolbar_entry->controls = control_items;
+ }
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Loading interface list for %s ", cb_info.extcap);
@@ -1447,6 +1579,11 @@ static gboolean cb_load_interfaces(extcap_callback_info_t cb_info)
}
help = int_iter->help;
+ if (toolbar_entry)
+ {
+ toolbar_entry->menu_title = g_strdup(int_iter->display);
+ toolbar_entry->help = g_strdup(int_iter->help);
+ }
walker = g_list_next(walker);
continue;
@@ -1476,11 +1613,26 @@ static gboolean cb_load_interfaces(extcap_callback_info_t cb_info)
element->interfaces = g_list_append(element->interfaces, int_iter);
g_hash_table_insert(_tool_for_ifname, g_strdup(int_iter->call), g_strdup(toolname));
+
+ if (toolbar_entry)
+ {
+ if (!toolbar_entry->menu_title)
+ {
+ toolbar_entry->menu_title = g_strdup(int_iter->display);
+ }
+ toolbar_entry->ifnames = g_list_append(toolbar_entry->ifnames, g_strdup(int_iter->call));
+ }
}
walker = g_list_next(walker);
}
+ if (toolbar_entry && toolbar_entry->menu_title)
+ {
+ iface_toolbar_add(toolbar_entry);
+ extcap_iface_toolbar_add(cb_info.extcap, toolbar_entry);
+ }
+
g_list_foreach(interfaces, remove_extcap_entry, NULL);
g_list_free(interfaces);
g_list_free(interface_keys);
@@ -1499,6 +1651,21 @@ extcap_load_interface_list(void)
gchar *argv;
gchar *error;
+ if (_toolbars)
+ {
+ // Remove existing interface toolbars here instead of in extcap_clear_interfaces()
+ // to avoid flicker in shown toolbars when refreshing interfaces.
+ GList *toolbar_list = g_hash_table_get_values (_toolbars);
+ for (GList *walker = toolbar_list; walker; walker = walker->next)
+ {
+ iface_toolbar *toolbar = (iface_toolbar *) walker->data;
+ iface_toolbar_remove(toolbar->menu_title);
+ }
+ g_hash_table_remove_all(_toolbars);
+ } else {
+ _toolbars = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, extcap_free_toolbar);
+ }
+
if (_loaded_interfaces == NULL)
{
_loaded_interfaces = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, extcap_free_interface_info);
diff --git a/extcap.h b/extcap.h
index 7c7a82baaf..297745ee63 100644
--- a/extcap.h
+++ b/extcap.h
@@ -43,13 +43,15 @@
#define EXTCAP_PIPE_PREFIX "wireshark_extcap"
#define EXTCAP_ARGUMENT_CONFIG "--extcap-config"
-#define EXTCAP_ARGUMENT_LIST_INTERFACES "--extcap-interfaces"
+#define EXTCAP_ARGUMENT_LIST_INTERFACES "--extcap-interfaces"
#define EXTCAP_ARGUMENT_INTERFACE "--extcap-interface"
#define EXTCAP_ARGUMENT_LIST_DLTS "--extcap-dlts"
#define EXTCAP_ARGUMENT_RUN_CAPTURE "--capture"
#define EXTCAP_ARGUMENT_CAPTURE_FILTER "--extcap-capture-filter"
#define EXTCAP_ARGUMENT_RUN_PIPE "--fifo"
+#define EXTCAP_ARGUMENT_CONTROL_IN "--extcap-control-in"
+#define EXTCAP_ARGUMENT_CONTROL_OUT "--extcap-control-out"
typedef struct _extcap_info {
gchar * basename;
@@ -117,6 +119,9 @@ extcap_free_if_configuration(GList *list, gboolean free_args);
gboolean
extcap_has_configuration(const char * ifname, gboolean is_required);
+gboolean
+extcap_has_toolbar(const char *ifname);
+
#ifdef WIN32
HANDLE
extcap_get_win32_handle();
diff --git a/extcap_parser.c b/extcap_parser.c
index 78c48dd94f..7f7b6ceb0c 100644
--- a/extcap_parser.c
+++ b/extcap_parser.c
@@ -28,6 +28,9 @@
#include <glib.h>
#include <string.h>
+#include "ui/iface_toolbar.h"
+#include "wsutil/strtoi.h"
+
#include "extcap.h"
#include "extcap_parser.h"
@@ -120,7 +123,7 @@ static extcap_token_sentence *extcap_tokenize_sentence(const gchar *s) {
rs->sentence = NULL;
/* Regex for catching just the allowed values for sentences */
- if ((regex = g_regex_new("^[\\t| ]*(arg|value|interface|extcap|dlt)(?=[\\t| ]+\\{)",
+ if ((regex = g_regex_new("^[\\t| ]*(arg|value|interface|extcap|dlt|control)(?=[\\t| ]+\\{)",
(GRegexCompileFlags) G_REGEX_CASELESS, (GRegexMatchFlags) 0, NULL)) != NULL) {
g_regex_match(regex, s, (GRegexMatchFlags) 0, &match_info);
@@ -193,6 +196,10 @@ static extcap_token_sentence *extcap_tokenize_sentence(const gchar *s) {
param_type = EXTCAP_PARAM_VERSION;
} else if (g_ascii_strcasecmp(arg, "help") == 0) {
param_type = EXTCAP_PARAM_HELP;
+ } else if (g_ascii_strcasecmp(arg, "control") == 0) {
+ param_type = EXTCAP_PARAM_CONTROL;
+ } else if (g_ascii_strcasecmp(arg, "role") == 0) {
+ param_type = EXTCAP_PARAM_ROLE;
} else {
param_type = EXTCAP_PARAM_UNKNOWN;
}
@@ -269,6 +276,24 @@ void extcap_free_arg(extcap_arg *a) {
g_free(a);
}
+void extcap_free_toolbar_value(iface_toolbar_value *v) {
+ if (v == NULL)
+ return;
+
+ g_free(v->value);
+ g_free(v->display);
+}
+
+void extcap_free_toolbar_control(iface_toolbar_control *c) {
+ if (c == NULL)
+ return;
+
+ g_free(c->display);
+ g_free(c->validation);
+ g_free(c->tooltip);
+ g_free(c->placeholder);
+}
+
void extcap_free_arg_list(GList *a) {
g_list_foreach(a, (GFunc)extcap_free_arg, NULL);
g_list_free(a);
@@ -280,6 +305,12 @@ static gint glist_find_numbered_arg(gconstpointer listelem, gconstpointer needle
return 1;
}
+static gint glist_find_numbered_control(gconstpointer listelem, gconstpointer needle) {
+ if (((const iface_toolbar_control *) listelem)->num == *((const int *) needle))
+ return 0;
+ return 1;
+}
+
static void extcap_free_tokenized_sentence(gpointer s, gpointer user_data _U_) {
extcap_token_sentence *t = (extcap_token_sentence *)s;
@@ -604,7 +635,179 @@ static extcap_interface *extcap_parse_interface_sentence(extcap_token_sentence *
return ri;
}
-GList *extcap_parse_interfaces(gchar *output) {
+static iface_toolbar_control *extcap_parse_control_sentence(GList *control_items, extcap_token_sentence *s)
+{
+ extcap_sentence_type sent = EXTCAP_SENTENCE_UNKNOWN;
+ gchar *param_value = NULL;
+ iface_toolbar_control *control = NULL;
+ iface_toolbar_value *value = NULL;
+ GList *entry = NULL;
+ guint32 num = 0;
+
+ if (s == NULL)
+ return NULL;
+
+ if (g_ascii_strcasecmp(s->sentence, "control") == 0) {
+ sent = EXTCAP_SENTENCE_CONTROL;
+ } else if (g_ascii_strcasecmp(s->sentence, "value") == 0) {
+ sent = EXTCAP_SENTENCE_VALUE;
+ }
+
+ if (sent == EXTCAP_SENTENCE_UNKNOWN)
+ return NULL;
+
+ if (sent == EXTCAP_SENTENCE_CONTROL) {
+ control = g_new0(iface_toolbar_control, 1);
+ control->ctrl_type = INTERFACE_TYPE_UNKNOWN;
+
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_ARGNUM));
+ if (param_value == NULL) {
+ extcap_free_toolbar_control(control);
+ return NULL;
+ }
+
+ if (!ws_strtou32(param_value, NULL, &num)) {
+ extcap_free_toolbar_control(control);
+ return NULL;
+ }
+ control->num = (int)num;
+
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_DISPLAY));
+ if (param_value == NULL) {
+ extcap_free_toolbar_control(control);
+ return NULL;
+ }
+ control->display = g_strdup(param_value);
+
+ if ((param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_VALIDATION)))
+ != NULL) {
+ control->validation = g_strdup(param_value);
+ }
+
+ if ((param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_REQUIRED)))
+ != NULL) {
+ control->is_required = g_regex_match_simple(EXTCAP_BOOLEAN_REGEX, param_value, G_REGEX_CASELESS, (GRegexMatchFlags)0);
+ }
+
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_TOOLTIP));
+ if (param_value != NULL) {
+ control->tooltip = g_strdup(param_value);
+ }
+
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_PLACEHOLDER));
+ if (param_value != NULL) {
+ control->placeholder = g_strdup(param_value);
+ }
+
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_TYPE));
+ if (param_value == NULL) {
+ extcap_free_toolbar_control(control);
+ return NULL;
+ }
+
+ extcap_arg_type arg_type = EXTCAP_ARG_UNKNOWN;
+ if (g_ascii_strcasecmp(param_value, "boolean") == 0) {
+ control->ctrl_type = INTERFACE_TYPE_BOOLEAN;
+ arg_type = EXTCAP_ARG_BOOLEAN;
+ } else if (g_ascii_strcasecmp(param_value, "button") == 0) {
+ control->ctrl_type = INTERFACE_TYPE_BUTTON;
+ } else if (g_ascii_strcasecmp(param_value, "selector") == 0) {
+ control->ctrl_type = INTERFACE_TYPE_SELECTOR;
+ } else if (g_ascii_strcasecmp(param_value, "string") == 0) {
+ control->ctrl_type = INTERFACE_TYPE_STRING;
+ arg_type = EXTCAP_ARG_STRING;
+ } else {
+ printf("invalid type %s in CONTROL sentence\n", param_value);
+ extcap_free_toolbar_control(control);
+ return NULL;
+ }
+
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_ROLE));
+ if (param_value != NULL) {
+ if (g_ascii_strcasecmp(param_value, "control") == 0) {
+ control->ctrl_role = INTERFACE_ROLE_CONTROL;
+ } else if (g_ascii_strcasecmp(param_value, "help") == 0) {
+ control->ctrl_role = INTERFACE_ROLE_HELP;
+ } else if (g_ascii_strcasecmp(param_value, "logger") == 0) {
+ control->ctrl_role = INTERFACE_ROLE_LOGGER;
+ } else if (g_ascii_strcasecmp(param_value, "reset") == 0) {
+ control->ctrl_role = INTERFACE_ROLE_RESET;
+ } else {
+ printf("invalid role %s in CONTROL sentence\n", param_value);
+ control->ctrl_role = INTERFACE_ROLE_UNKNOWN;
+ }
+ } else {
+ /* Default role */
+ control->ctrl_role = INTERFACE_ROLE_CONTROL;
+ }
+
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_DEFAULT));
+ if (param_value != NULL) {
+ if (arg_type != EXTCAP_ARG_UNKNOWN) {
+ extcap_complex *complex = extcap_parse_complex(arg_type, param_value);
+ if (complex != NULL) {
+ if (arg_type == EXTCAP_ARG_BOOLEAN) {
+ control->default_value.boolean = extcap_complex_get_bool(complex);
+ } else if (arg_type == EXTCAP_ARG_STRING) {
+ control->default_value.string = g_strdup(complex->_val);
+ }
+ extcap_free_complex(complex);
+ } else {
+ printf("invalid default, couldn't parse %s\n", param_value);
+ }
+ }
+ }
+
+ } else if (sent == EXTCAP_SENTENCE_VALUE) {
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_CONTROL));
+ if (param_value == NULL) {
+ printf("no control in VALUE sentence\n");
+ return NULL;
+ }
+
+ if (!ws_strtou32(param_value, NULL, &num)) {
+ extcap_free_toolbar_control(control);
+ return NULL;
+ }
+
+ entry = g_list_find_custom(control_items, &num, glist_find_numbered_control);
+ if (entry == NULL) {
+ printf("couldn't find control %u in list for VALUE sentence\n", num);
+ return NULL;
+ }
+
+ value = g_new0(iface_toolbar_value, 1);
+ value->num = (int)num;
+
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_VALUE));
+ if (param_value == NULL) {
+ extcap_free_toolbar_value(value);
+ return NULL;
+ }
+ value->value = g_strdup(param_value);
+
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_DISPLAY));
+ if (param_value == NULL) {
+ extcap_free_toolbar_value(value);
+ return NULL;
+ }
+ value->display = g_strdup(param_value);
+
+ param_value = (gchar *)g_hash_table_lookup(s->param_list, ENUM_KEY(EXTCAP_PARAM_DEFAULT));
+ if (param_value != NULL) {
+ value->is_default = g_regex_match_simple(EXTCAP_BOOLEAN_REGEX, param_value, G_REGEX_CASELESS, (GRegexMatchFlags)0);
+ }
+
+ control = (iface_toolbar_control *)entry->data;
+ control->values = g_list_append(control->values, value);
+
+ return NULL;
+ }
+
+ return control;
+}
+
+GList *extcap_parse_interfaces(gchar *output, GList **control_items) {
GList *result = NULL;
GList *tokens = NULL;
@@ -613,6 +816,7 @@ GList *extcap_parse_interfaces(gchar *output) {
while (walker) {
extcap_interface *ri = NULL;
+ iface_toolbar_control *ti = NULL;
extcap_token_sentence *if_sentence = (extcap_token_sentence *) walker->data;
if (if_sentence) {
@@ -622,6 +826,13 @@ GList *extcap_parse_interfaces(gchar *output) {
if ((ri = extcap_parse_interface_sentence(if_sentence))) {
result = g_list_append(result, ri);
}
+ } else if (control_items &&
+ ((g_ascii_strcasecmp(if_sentence->sentence, "control") == 0) ||
+ (g_ascii_strcasecmp(if_sentence->sentence, "value") == 0)))
+ {
+ if ((ti = extcap_parse_control_sentence(*control_items, if_sentence))) {
+ *control_items = g_list_append(*control_items, ti);
+ }
}
}
diff --git a/extcap_parser.h b/extcap_parser.h
index 1fd4d42071..3d4c8d0d7b 100644
--- a/extcap_parser.h
+++ b/extcap_parser.h
@@ -34,7 +34,8 @@ typedef enum {
EXTCAP_SENTENCE_VALUE,
EXTCAP_SENTENCE_EXTCAP,
EXTCAP_SENTENCE_INTERFACE,
- EXTCAP_SENTENCE_DLT
+ EXTCAP_SENTENCE_DLT,
+ EXTCAP_SENTENCE_CONTROL
} extcap_sentence_type;
typedef enum {
@@ -78,7 +79,9 @@ typedef enum {
EXTCAP_PARAM_SAVE,
EXTCAP_PARAM_VALIDATION,
EXTCAP_PARAM_VERSION,
- EXTCAP_PARAM_HELP
+ EXTCAP_PARAM_HELP,
+ EXTCAP_PARAM_CONTROL,
+ EXTCAP_PARAM_ROLE
} extcap_param_type;
#define ENUM_KEY(s) GUINT_TO_POINTER((guint)s)
@@ -197,7 +200,7 @@ void extcap_free_arg_list(GList *a);
GList * extcap_parse_args(gchar *output);
/* Parse all sentences for interfaces */
-GList * extcap_parse_interfaces(gchar *output);
+GList * extcap_parse_interfaces(gchar *output, GList **control_items);
/* Parse all sentences for DLTs */
GList * extcap_parse_dlts(gchar *output);
diff --git a/sync_pipe.h b/sync_pipe.h
index 455b2b4bd6..d713305297 100644
--- a/sync_pipe.h
+++ b/sync_pipe.h
@@ -58,6 +58,7 @@
#define SP_PACKET_COUNT 'P' /* count of packets captured since last message */
#define SP_DROPS 'D' /* count of packets dropped in capture */
#define SP_SUCCESS 'S' /* success indication, no extra data */
+#define SP_TOOLBAR_CTRL 'T' /* interface toolbar control packet */
/*
* Win32 only: Indications sent out on the signal pipe (from parent to child)
* (UNIX-like sends signals for this)
diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt
index 8a52ef9463..e02eab1c6e 100644
--- a/ui/CMakeLists.txt
+++ b/ui/CMakeLists.txt
@@ -34,6 +34,7 @@ set(COMMON_UI_SRC
failure_message.c
filter_files.c
firewall_rules.c
+ iface_toolbar.c
iface_lists.c
io_graph_item.c
language.c
diff --git a/ui/Makefile.am b/ui/Makefile.am
index 776ad4230b..49699c6615 100644
--- a/ui/Makefile.am
+++ b/ui/Makefile.am
@@ -60,6 +60,7 @@ WIRESHARK_UI_SRC = \
failure_message.c \
filter_files.c \
firewall_rules.c \
+ iface_toolbar.c \
iface_lists.c \
io_graph_item.c \
language.c \
@@ -109,6 +110,7 @@ WIRESHARK_UI_INCLUDES = \
help_url.h \
packet_list_utils.h \
firewall_rules.h \
+ iface_toolbar.h \
iface_lists.h \
io_graph_item.h \
language.h \
diff --git a/ui/iface_toolbar.c b/ui/iface_toolbar.c
new file mode 100644
index 0000000000..0e16456ee4
--- /dev/null
+++ b/ui/iface_toolbar.c
@@ -0,0 +1,68 @@
+/* iface_toolbar.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+
+#include "iface_toolbar.h"
+
+
+static iface_toolbar_add_cb_t iface_toolbar_add_cb;
+static iface_toolbar_remove_cb_t iface_toolbar_remove_cb;
+
+void iface_toolbar_add(const iface_toolbar *toolbar)
+{
+ if (iface_toolbar_add_cb) {
+ iface_toolbar_add_cb(toolbar);
+ }
+}
+
+void iface_toolbar_remove(const gchar *menu_title)
+{
+ if (iface_toolbar_remove_cb) {
+ iface_toolbar_remove_cb(menu_title);
+ }
+}
+
+gboolean iface_toolbar_use(void)
+{
+ return iface_toolbar_add_cb ? TRUE : FALSE;
+}
+
+void iface_toolbar_register_cb(iface_toolbar_add_cb_t add_cb, iface_toolbar_remove_cb_t remove_cb)
+{
+ iface_toolbar_add_cb = add_cb;
+ iface_toolbar_remove_cb = remove_cb;
+}
+
+/*
+ * 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/iface_toolbar.h b/ui/iface_toolbar.h
new file mode 100644
index 0000000000..888c817230
--- /dev/null
+++ b/ui/iface_toolbar.h
@@ -0,0 +1,105 @@
+/* iface_toolbar.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __IFACE_TOOLBAR_H__
+#define __IFACE_TOOLBAR_H__
+
+#include "config.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef enum {
+ INTERFACE_TYPE_UNKNOWN,
+ INTERFACE_TYPE_BOOLEAN,
+ INTERFACE_TYPE_BUTTON,
+ INTERFACE_TYPE_SELECTOR,
+ INTERFACE_TYPE_STRING
+} iface_toolbar_ctrl_type;
+
+typedef enum {
+ INTERFACE_ROLE_UNKNOWN,
+ INTERFACE_ROLE_CONTROL,
+ INTERFACE_ROLE_HELP,
+ INTERFACE_ROLE_LOGGER,
+ INTERFACE_ROLE_RESET
+} iface_toolbar_ctrl_role;
+
+typedef struct _iface_toolbar_value {
+ int num;
+ gchar *value;
+ gchar *display;
+ gboolean is_default;
+} iface_toolbar_value;
+
+typedef struct _iface_toolbar_control {
+ int num;
+ iface_toolbar_ctrl_type ctrl_type;
+ iface_toolbar_ctrl_role ctrl_role;
+ gchar *display;
+ gchar *validation;
+ gboolean is_required;
+ gchar *tooltip;
+ gchar *placeholder;
+ union {
+ gboolean boolean;
+ gchar *string;
+ } default_value;
+ GList *values;
+} iface_toolbar_control;
+
+typedef struct _iface_toolbar {
+ gchar *menu_title;
+ gchar *help;
+ GList *ifnames;
+ GList *controls;
+} iface_toolbar;
+
+typedef void (*iface_toolbar_add_cb_t)(const iface_toolbar *);
+typedef void (*iface_toolbar_remove_cb_t)(const gchar *);
+
+void iface_toolbar_add(const iface_toolbar *toolbar);
+
+void iface_toolbar_remove(const gchar *menu_title);
+
+gboolean iface_toolbar_use(void);
+
+void iface_toolbar_register_cb(iface_toolbar_add_cb_t, iface_toolbar_remove_cb_t);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __IFACE_TOOLBAR_H__ */
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt
index e8936e9119..ca3d67f530 100644
--- a/ui/qt/CMakeLists.txt
+++ b/ui/qt/CMakeLists.txt
@@ -87,6 +87,9 @@ set(WIRESHARK_QT_HEADERS
gsm_map_summary_dialog.h
iax2_analysis_dialog.h
import_text_dialog.h
+ interface_toolbar.h
+ interface_toolbar_reader.h
+ interface_toolbar_lineedit.h
interface_tree_model.h
interface_tree_cache_model.h
interface_sort_filter_model.h
@@ -257,6 +260,9 @@ set(WIRESHARK_QT_SRC
geometry_state_dialog.cpp
iax2_analysis_dialog.cpp
import_text_dialog.cpp
+ interface_toolbar.cpp
+ interface_toolbar_reader.cpp
+ interface_toolbar_lineedit.cpp
interface_tree_model.cpp
interface_tree_cache_model.cpp
interface_sort_filter_model.cpp
@@ -418,6 +424,7 @@ set(WIRESHARK_QT_UI
iax2_analysis_dialog.ui
import_text_dialog.ui
interface_frame.ui
+ interface_toolbar.ui
io_graph_dialog.ui
layout_preferences_frame.ui
lbm_lbtrm_transport_dialog.ui
diff --git a/ui/qt/Makefile.am b/ui/qt/Makefile.am
index 77d17ea449..7f52918fcf 100644
--- a/ui/qt/Makefile.am
+++ b/ui/qt/Makefile.am
@@ -70,6 +70,7 @@ NODIST_GENERATED_HEADER_FILES = \
ui_iax2_analysis_dialog.h \
ui_import_text_dialog.h \
ui_interface_frame.h \
+ ui_interface_toolbar.h \
ui_io_graph_dialog.h \
ui_layout_preferences_frame.h \
ui_lbm_lbtrm_transport_dialog.h \
@@ -215,6 +216,9 @@ MOC_HDRS = \
iax2_analysis_dialog.h \
import_text_dialog.h \
interface_frame.h \
+ interface_toolbar.h \
+ interface_toolbar_lineedit.h \
+ interface_toolbar_reader.h \
interface_tree_model.h \
interface_tree_cache_model.h \
interface_sort_filter_model.h \
@@ -340,6 +344,7 @@ UI_FILES = \
iax2_analysis_dialog.ui \
import_text_dialog.ui \
interface_frame.ui \
+ interface_toolbar.ui \
io_graph_dialog.ui \
layout_preferences_frame.ui \
lbm_lbtrm_transport_dialog.ui \
@@ -499,6 +504,9 @@ WIRESHARK_QT_SRC = \
iax2_analysis_dialog.cpp \
import_text_dialog.cpp \
interface_frame.cpp \
+ interface_toolbar.cpp \
+ interface_toolbar_lineedit.cpp \
+ interface_toolbar_reader.cpp \
interface_tree_model.cpp \
interface_tree_cache_model.cpp \
interface_sort_filter_model.cpp \
@@ -822,6 +830,8 @@ io_graph_dialog.$(OBJEXT): ui_io_graph_dialog.h
interface_frame.$(OBJEXT): ui_interface_frame.h
+interface_toolbar.$(OBJEXT): ui_interface_toolbar.h
+
layout_preferences_frame.$(OBJEXT): ui_layout_preferences_frame.h
lbm_lbtrm_transport_dialog.$(OBJEXT): ui_lbm_lbtrm_transport_dialog.h
diff --git a/ui/qt/funnel_text_dialog.cpp b/ui/qt/funnel_text_dialog.cpp
index 6ba873f451..8be47b032c 100644
--- a/ui/qt/funnel_text_dialog.cpp
+++ b/ui/qt/funnel_text_dialog.cpp
@@ -47,6 +47,7 @@ FunnelTextDialog::FunnelTextDialog(const QString &title) :
if (!title.isEmpty()) {
loadGeometry(0, 0, QString("Funnel %1").arg(title));
}
+ setWindowTitle(wsApp->windowTitleString(title));
funnel_text_window_.funnel_text_dialog = this;
@@ -75,7 +76,6 @@ void FunnelTextDialog::reject()
struct _funnel_text_window_t *FunnelTextDialog::textWindowNew(const QString title)
{
FunnelTextDialog *ftd = new FunnelTextDialog(title);
- ftd->setWindowTitle(wsApp->windowTitleString(title));
ftd->show();
return &ftd->funnel_text_window_;
}
diff --git a/ui/qt/interface_toolbar.cpp b/ui/qt/interface_toolbar.cpp
new file mode 100644
index 0000000000..56a1a53b34
--- /dev/null
+++ b/ui/qt/interface_toolbar.cpp
@@ -0,0 +1,895 @@
+/* interface_toolbar.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+
+#include "interface_toolbar.h"
+#include "interface_toolbar_lineedit.h"
+#include "simple_dialog.h"
+#include "ui/main_statusbar.h"
+#include <ui_interface_toolbar.h>
+
+#include "sync_pipe.h"
+#include "wsutil/file_util.h"
+
+#include <QCheckBox>
+#include <QComboBox>
+#include <QDesktopServices>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QThread>
+#include <QUrl>
+
+static const char *interface_type_property = "control_type";
+static const char *interface_role_property = "control_role";
+
+// From interface control protocol.
+enum InterfaceControlCommand {
+ commandControlInitialized = 0,
+ commandControlSet = 1,
+ commandControlAdd = 2,
+ commandControlRemove = 3,
+ commandControlEnable = 4,
+ commandControlDisable = 5,
+ commandStatusMessage = 6,
+ commandInformationMessage = 7,
+ commandWarningMessage = 8,
+ commandErrorMessage = 9,
+};
+
+// To do:
+// - Move control pipe handling to extcap
+
+InterfaceToolbar::InterfaceToolbar(QWidget *parent, const iface_toolbar *toolbar) :
+ QFrame(parent),
+ ui(new Ui::InterfaceToolbar),
+ help_link_(toolbar->help),
+ use_spacer_(true)
+{
+ ui->setupUi(this);
+
+ // Fill inn interfaces list and initialize default interface values
+ ui->interfacesComboBox->blockSignals(true);
+ for (GList *walker = toolbar->ifnames; walker; walker = walker->next)
+ {
+ QString ifname((gchar *)walker->data);
+ ui->interfacesComboBox->addItem(ifname);
+ interface_[ifname].reader_thread = NULL;
+ interface_[ifname].out_fd = -1;
+ interface_[ifname].log_dialog = NULL;
+ }
+ ui->interfacesComboBox->blockSignals(false);
+
+ initializeControls(toolbar);
+
+#ifdef Q_OS_MAC
+ foreach (QWidget *w, findChildren<QWidget *>())
+ {
+ w->setAttribute(Qt::WA_MacSmallSize, true);
+ }
+#endif
+
+ if (!use_spacer_)
+ {
+ ui->horizontalSpacer->changeSize(0,0, QSizePolicy::Fixed, QSizePolicy::Fixed);
+ }
+
+ updateWidgets();
+}
+
+InterfaceToolbar::~InterfaceToolbar()
+{
+ foreach (QString ifname, interface_.keys())
+ {
+ if (interface_[ifname].log_dialog)
+ {
+ interface_[ifname].log_dialog->close();
+ }
+ }
+
+ delete ui;
+}
+
+void InterfaceToolbar::initializeControls(const iface_toolbar *toolbar)
+{
+ for (GList *walker = toolbar->controls; walker; walker = walker->next)
+ {
+ iface_toolbar_control *control = (iface_toolbar_control *)walker->data;
+
+ if (control_widget_.contains(control->num))
+ {
+ // Already have a widget with this number
+ continue;
+ }
+
+ QWidget *widget = NULL;
+ switch (control->ctrl_type)
+ {
+ case INTERFACE_TYPE_BOOLEAN:
+ widget = createCheckbox(control);
+ break;
+
+ case INTERFACE_TYPE_BUTTON:
+ widget = createButton(control);
+ break;
+
+ case INTERFACE_TYPE_SELECTOR:
+ widget = createSelector(control);
+ break;
+
+ case INTERFACE_TYPE_STRING:
+ widget = createString(control);
+ break;
+
+ default:
+ // Not supported
+ break;
+ }
+
+ if (widget)
+ {
+ widget->setProperty(interface_type_property, control->ctrl_type);
+ widget->setProperty(interface_role_property, control->ctrl_role);
+ control_widget_[control->num] = widget;
+ }
+ }
+}
+
+void InterfaceToolbar::setDefaultValue(int num, const QByteArray &value)
+{
+ foreach (QString ifname, interface_.keys())
+ {
+ // Adding default value to all interfaces
+ interface_[ifname].value[num] = value;
+ }
+ default_value_[num] = value;
+}
+
+QWidget *InterfaceToolbar::createCheckbox(iface_toolbar_control *control)
+{
+ QCheckBox *checkbox = new QCheckBox(QString().fromUtf8(control->display));
+ checkbox->setToolTip(QString().fromUtf8(control->tooltip));
+
+ if (control->default_value.boolean)
+ {
+ checkbox->setCheckState(Qt::Checked);
+ QByteArray default_value(1, 1);
+ setDefaultValue(control->num, default_value);
+ }
+
+ connect(checkbox, SIGNAL(stateChanged(int)), this, SLOT(onCheckBoxChanged(int)));
+
+ ui->leftLayout->addWidget(checkbox);
+
+ return checkbox;
+}
+
+QWidget *InterfaceToolbar::createButton(iface_toolbar_control *control)
+{
+ QPushButton *button = new QPushButton(QString().fromUtf8((gchar *)control->display));
+ button->setMaximumHeight(27);
+ button->setToolTip(QString().fromUtf8(control->tooltip));
+
+ switch (control->ctrl_role)
+ {
+ case INTERFACE_ROLE_CONTROL:
+ setDefaultValue(control->num, (gchar *)control->display);
+ connect(button, SIGNAL(pressed()), this, SLOT(onButtonPressed()));
+ break;
+
+ case INTERFACE_ROLE_HELP:
+ connect(button, SIGNAL(pressed()), this, SLOT(onHelpButtonPressed()));
+ if (help_link_.isEmpty())
+ {
+ // No help URL provided
+ button->hide();
+ }
+ break;
+
+ case INTERFACE_ROLE_LOGGER:
+ connect(button, SIGNAL(pressed()), this, SLOT(onLogButtonPressed()));
+ break;
+
+ case INTERFACE_ROLE_RESET:
+ button->setText("Reset");
+ button->setToolTip("Restore default values");
+ connect(button, SIGNAL(pressed()), this, SLOT(onResetButtonPressed()));
+ break;
+
+ default:
+ break;
+ }
+
+ ui->rightLayout->addWidget(button);
+
+ return button;
+}
+
+QWidget *InterfaceToolbar::createSelector(iface_toolbar_control *control)
+{
+ QLabel *label = new QLabel(QString().fromUtf8(control->display));
+ label->setToolTip(QString().fromUtf8(control->tooltip));
+ QComboBox *combobox = new QComboBox();
+ combobox->setToolTip(QString().fromUtf8(control->tooltip));
+ combobox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+
+ for (GList *walker = control->values; walker; walker = walker->next)
+ {
+ iface_toolbar_value *val = (iface_toolbar_value *)walker->data;
+ QString value = QString().fromUtf8((gchar *)val->value);
+ if (value.length() == 0)
+ {
+ // Invalid value
+ continue;
+ }
+ QString display = QString().fromUtf8((gchar *)val->display);
+ QByteArray interface_value;
+
+ interface_value.append(value);
+ if (display.length() == 0)
+ {
+ display = value;
+ }
+ else
+ {
+ interface_value.append('\0' + display);
+ }
+ combobox->addItem(display, value);
+ if (val->is_default)
+ {
+ combobox->setCurrentText(display);
+ setDefaultValue(control->num, value.toUtf8());
+ }
+ foreach (QString ifname, interface_.keys())
+ {
+ // Adding values to all interfaces
+ interface_[ifname].list[control->num].append(interface_value);
+ }
+ default_list_[control->num].append(interface_value);
+ }
+
+ connect(combobox, SIGNAL(currentIndexChanged(int)), this, SLOT(onComboBoxChanged(int)));
+
+ ui->leftLayout->addWidget(label);
+ ui->leftLayout->addWidget(combobox);
+ label_widget_[control->num] = label;
+
+ return combobox;
+}
+
+QWidget *InterfaceToolbar::createString(iface_toolbar_control *control)
+{
+ QLabel *label = new QLabel(QString().fromUtf8(control->display));
+ label->setToolTip(QString().fromUtf8(control->tooltip));
+ InterfaceToolbarLineEdit *lineedit = new InterfaceToolbarLineEdit(NULL, control->validation, control->is_required);
+ lineedit->setToolTip(QString().fromUtf8(control->tooltip));
+ lineedit->setPlaceholderText(QString().fromUtf8(control->placeholder));
+
+ if (control->default_value.string)
+ {
+ lineedit->setText(QString().fromUtf8(control->default_value.string));
+ setDefaultValue(control->num, control->default_value.string);
+ }
+
+ connect(lineedit, SIGNAL(editedTextApplied()), this, SLOT(onLineEditChanged()));
+
+ ui->leftLayout->addWidget(label);
+ ui->leftLayout->addWidget(lineedit);
+ label_widget_[control->num] = label;
+ use_spacer_ = false;
+
+ return lineedit;
+}
+
+void InterfaceToolbar::setWidgetValue(QWidget *widget, int command, QByteArray payload)
+{
+ if (QComboBox *combobox = dynamic_cast<QComboBox *>(widget))
+ {
+ combobox->blockSignals(true);
+ switch (command)
+ {
+ case commandControlSet:
+ {
+ int idx = combobox->findData(payload);
+ if (idx != -1)
+ {
+ combobox->setCurrentIndex(idx);
+ }
+ break;
+ }
+
+ case commandControlAdd:
+ {
+ QString value;
+ QString display;
+ if (payload.contains('\0'))
+ {
+ // The payload contains "value\0display"
+ QList<QByteArray> values = payload.split('\0');
+ value = values[0];
+ display = values[1];
+ }
+ else
+ {
+ value = display = payload;
+ }
+
+ int idx = combobox->findData(value);
+ if (idx != -1)
+ {
+ // The value already exists, update item text
+ combobox->setItemText(idx, display);
+ }
+ else
+ {
+ combobox->addItem(display, value);
+ }
+ break;
+ }
+
+ case commandControlRemove:
+ {
+ if (payload.size() == 0)
+ {
+ combobox->clear();
+ }
+ else
+ {
+ int idx = combobox->findData(payload);
+ if (idx != -1)
+ {
+ combobox->removeItem(idx);
+ }
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ combobox->blockSignals(false);
+ }
+ else if (InterfaceToolbarLineEdit *lineedit = dynamic_cast<InterfaceToolbarLineEdit *>(widget))
+ {
+ // We don't block signals here because changes are applied with enter or apply button,
+ // and we want InterfaceToolbarLineEdit to always syntax check the text.
+ switch (command)
+ {
+ case commandControlSet:
+ lineedit->setText(payload);
+ lineedit->disableApplyButton();
+ break;
+
+ default:
+ break;
+ }
+ }
+ else if (QCheckBox *checkbox = dynamic_cast<QCheckBox *>(widget))
+ {
+ checkbox->blockSignals(true);
+ switch (command)
+ {
+ case commandControlSet:
+ {
+ Qt::CheckState state = Qt::Unchecked;
+ if (payload.size() > 0 && payload.at(0) != 0)
+ {
+ state = Qt::Checked;
+ }
+ checkbox->setCheckState(state);
+ break;
+ }
+
+ default:
+ break;
+ }
+ checkbox->blockSignals(false);
+ }
+ else if (QPushButton *button = dynamic_cast<QPushButton *>(widget))
+ {
+ if ((command == commandControlSet) &&
+ widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL)
+ {
+ button->setText(payload);
+ }
+ }
+}
+
+void InterfaceToolbar::setInterfaceValue(QString ifname, QWidget *widget, int num, int command, QByteArray payload)
+{
+ if (dynamic_cast<QComboBox *>(widget))
+ {
+ switch (command)
+ {
+ case commandControlSet:
+ foreach (QByteArray entry, interface_[ifname].list[num])
+ {
+ if (entry == payload || entry.startsWith(payload + '\0'))
+ {
+ interface_[ifname].value[num] = payload;
+ }
+ }
+ break;
+
+ case commandControlAdd:
+ interface_[ifname].list[num].append(payload);
+ break;
+
+ case commandControlRemove:
+ if (payload.size() == 0)
+ {
+ interface_[ifname].value[num].clear();
+ interface_[ifname].list[num].clear();
+ }
+ else
+ {
+ foreach (QByteArray entry, interface_[ifname].list[num])
+ {
+ if (entry == payload || entry.startsWith(payload + '\0'))
+ {
+ interface_[ifname].list[num].removeAll(entry);
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ else if (dynamic_cast<InterfaceToolbarLineEdit *>(widget))
+ {
+ switch (command)
+ {
+ case commandControlSet:
+ interface_[ifname].value[num] = payload;
+ break;
+
+ default:
+ break;
+ }
+ }
+ else if ((widget->property(interface_type_property).toInt() == INTERFACE_TYPE_BUTTON) &&
+ (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_LOGGER))
+ {
+ if (command == commandControlSet)
+ {
+ if (interface_[ifname].log_dialog)
+ {
+ interface_[ifname].log_dialog->clearText();
+ }
+ interface_[ifname].log_text.clear();
+ }
+ if (command == commandControlSet || command == commandControlAdd)
+ {
+ if (interface_[ifname].log_dialog)
+ {
+ interface_[ifname].log_dialog->appendText(payload);
+ }
+ interface_[ifname].log_text.append(payload);
+ }
+ }
+ else if (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL)
+ {
+ // QCheckBox or QPushButton
+ interface_[ifname].value[num] = payload;
+ }
+}
+
+
+void InterfaceToolbar::controlReceived(QString ifname, int num, int command, QByteArray payload)
+{
+ switch (command)
+ {
+ case commandControlSet:
+ case commandControlAdd:
+ case commandControlRemove:
+ if (QWidget *widget = control_widget_[num])
+ {
+ setInterfaceValue(ifname, widget, num, command, payload);
+
+ if (ifname.compare(ui->interfacesComboBox->currentText()) == 0)
+ {
+ setWidgetValue(widget, command, payload);
+ }
+ }
+ break;
+
+ case commandControlEnable:
+ case commandControlDisable:
+ if (QWidget *widget = control_widget_[num])
+ {
+ if (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL)
+ {
+ bool enable = (command == commandControlEnable ? true : false);
+ interface_[ifname].widget_disabled[num] = !enable;
+
+ if (ifname.compare(ui->interfacesComboBox->currentText()) == 0)
+ {
+ widget->setEnabled(enable);
+ if (label_widget_.contains(num))
+ {
+ label_widget_[num]->setEnabled(enable);
+ }
+ }
+ }
+ }
+ break;
+
+ case commandStatusMessage:
+ statusbar_push_temporary_msg("%s", payload.data());
+ break;
+
+ case commandInformationMessage:
+ simple_dialog(ESD_TYPE_INFO, ESD_BTN_OK, "%s", payload.data());
+ break;
+
+ case commandWarningMessage:
+ simple_dialog(ESD_TYPE_WARN, ESD_BTN_OK, "%s", payload.data());
+ break;
+
+ case commandErrorMessage:
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", payload.data());
+ break;
+
+ default:
+ // Unknown commands are silently ignored
+ break;
+ }
+}
+
+void InterfaceToolbar::controlSend(QString ifname, int num, int command, const QByteArray &payload = QByteArray())
+{
+ if (payload.length() > 65535)
+ {
+ // Not supported
+ return;
+ }
+
+ if (interface_[ifname].out_fd == -1)
+ {
+ // Does not have a control out channel
+ return;
+ }
+
+ ssize_t payload_length = payload.length() + 2;
+ unsigned char high_nibble = (payload_length >> 16) & 0xFF;
+ unsigned char mid_nibble = (payload_length >> 8) & 0xFF;
+ unsigned char low_nibble = (payload_length >> 0) & 0xFF;
+
+ QByteArray ba;
+
+ ba.append(SP_TOOLBAR_CTRL);
+ ba.append(high_nibble);
+ ba.append(mid_nibble);
+ ba.append(low_nibble);
+ ba.append(num);
+ ba.append(command);
+ ba.append(payload);
+
+ if (ws_write(interface_[ifname].out_fd, ba.data(), ba.length()) != ba.length())
+ {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
+ "Unable to send control message:\n%s.",
+ g_strerror(errno));
+ }
+}
+
+void InterfaceToolbar::onButtonPressed()
+{
+ const QString &ifname = ui->interfacesComboBox->currentText();
+ QPushButton *button = static_cast<QPushButton *>(sender());
+ int num = control_widget_.key(button);
+
+ controlSend(ifname, num, commandControlSet);
+}
+
+void InterfaceToolbar::onCheckBoxChanged(int state)
+{
+ const QString &ifname = ui->interfacesComboBox->currentText();
+ QCheckBox *checkbox = static_cast<QCheckBox *>(sender());
+ int num = control_widget_.key(checkbox);
+
+ QByteArray payload(1, state == Qt::Unchecked ? 0 : 1);
+ controlSend(ifname, num, commandControlSet, payload);
+ interface_[ifname].value[num] = payload;
+}
+
+void InterfaceToolbar::onComboBoxChanged(int idx)
+{
+ const QString &ifname = ui->interfacesComboBox->currentText();
+ QComboBox *combobox = static_cast<QComboBox *>(sender());
+ int num = control_widget_.key(combobox);
+ QString value = combobox->itemData(idx).toString();
+
+ QByteArray payload(value.toUtf8());
+ controlSend(ifname, num, commandControlSet, payload);
+ interface_[ifname].value[num] = payload;
+}
+
+void InterfaceToolbar::onLineEditChanged()
+{
+ const QString &ifname = ui->interfacesComboBox->currentText();
+ InterfaceToolbarLineEdit *lineedit = static_cast<InterfaceToolbarLineEdit *>(sender());
+ int num = control_widget_.key(lineedit);
+
+ QByteArray payload(lineedit->text().toUtf8());
+ controlSend(ifname, num, commandControlSet, payload);
+ interface_[ifname].value[num] = payload;
+}
+
+void InterfaceToolbar::onLogButtonPressed()
+{
+ const QString &ifname = ui->interfacesComboBox->currentText();
+
+ if (!interface_[ifname].log_dialog)
+ {
+ QPushButton *button = static_cast<QPushButton *>(sender());
+ interface_[ifname].log_dialog = new FunnelTextDialog(ifname + " " + button->text());
+ connect(interface_[ifname].log_dialog, SIGNAL(accepted()), this, SLOT(closeLog()));
+ connect(interface_[ifname].log_dialog, SIGNAL(rejected()), this, SLOT(closeLog()));
+
+ interface_[ifname].log_dialog->setText(interface_[ifname].log_text);
+ }
+
+ interface_[ifname].log_dialog->show();
+ interface_[ifname].log_dialog->raise();
+ interface_[ifname].log_dialog->activateWindow();
+}
+
+void InterfaceToolbar::onHelpButtonPressed()
+{
+ QUrl help_url(help_link_);
+
+ if (help_url.scheme().compare("file") != 0) {
+ QDesktopServices::openUrl(help_url);
+ }
+}
+
+void InterfaceToolbar::closeLog()
+{
+ FunnelTextDialog *log_dialog = static_cast<FunnelTextDialog *>(sender());
+
+ foreach (QString ifname, interface_.keys())
+ {
+ if (interface_[ifname].log_dialog == log_dialog)
+ {
+ interface_[ifname].log_dialog = NULL;
+ }
+ }
+}
+
+void InterfaceToolbar::startReaderThread(QString ifname, QString control_in)
+{
+ QThread *thread = new QThread;
+ InterfaceToolbarReader *reader = new InterfaceToolbarReader(ifname, control_in);
+ reader->moveToThread(thread);
+
+ connect(thread, SIGNAL(started()), reader, SLOT(loop()));
+ connect(reader, SIGNAL(finished()), thread, SLOT(quit()));
+ connect(reader, SIGNAL(finished()), reader, SLOT(deleteLater()));
+ connect(thread, SIGNAL(finished()), reader, SLOT(deleteLater()));
+ connect(reader, SIGNAL(received(QString, int, int, QByteArray)),
+ this, SLOT(controlReceived(QString, int, int, QByteArray)));
+
+ interface_[ifname].reader_thread = thread;
+
+ thread->start();
+}
+
+void InterfaceToolbar::startCapture(QString ifname, QString control_in, QString control_out)
+{
+ if (!interface_.contains(ifname) || // This interface is not for us
+ interface_[ifname].out_fd != -1) // Already have control channels for this interface
+ {
+ return;
+ }
+
+ // The reader thread will open control in channel
+ startReaderThread(ifname, control_in);
+
+ // Open control out channel
+ interface_[ifname].out_fd = ws_open(control_out.toUtf8(), O_WRONLY | O_BINARY, 0);
+
+ sendChangedValues(ifname);
+ controlSend(ifname, 0, commandControlInitialized);
+
+ updateWidgets();
+}
+
+void InterfaceToolbar::stopCapture()
+{
+ foreach (QString ifname, interface_.keys())
+ {
+ if (interface_[ifname].reader_thread)
+ {
+#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
+ interface_[ifname].reader_thread->requestInterruption();
+#endif
+ interface_[ifname].reader_thread = NULL;
+ }
+
+ if (interface_[ifname].out_fd != -1)
+ {
+ ws_close (interface_[ifname].out_fd);
+ interface_[ifname].out_fd = -1;
+ }
+
+ foreach (int num, control_widget_.keys())
+ {
+ // Reset disabled property for all widgets
+ interface_[ifname].widget_disabled[num] = false;
+
+ QWidget *widget = control_widget_[num];
+ if ((widget->property(interface_type_property).toInt() == INTERFACE_TYPE_BUTTON) &&
+ (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL))
+ {
+ // Reset default value for control buttons
+ interface_[ifname].value[num] = default_value_[num];
+
+ if (ifname.compare(ui->interfacesComboBox->currentText()) == 0)
+ {
+ setWidgetValue(widget, commandControlSet, default_value_[num]);
+ }
+ }
+ }
+ }
+
+ updateWidgets();
+}
+
+void InterfaceToolbar::sendChangedValues(QString ifname)
+{
+ // Send all values which has changed
+ foreach (int num, control_widget_.keys())
+ {
+ QWidget *widget = control_widget_[num];
+ if ((interface_[ifname].value[num] != default_value_[num]) &&
+ (widget->property(interface_type_property).toInt() != INTERFACE_TYPE_BUTTON) &&
+ (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL))
+ {
+ controlSend(ifname, num, commandControlSet, interface_[ifname].value[num]);
+ }
+ }
+}
+
+void InterfaceToolbar::onResetButtonPressed()
+{
+ const QString &ifname = ui->interfacesComboBox->currentText();
+
+ // Set default values to all widgets and interfaces
+ foreach (int num, control_widget_.keys())
+ {
+ QWidget *widget = control_widget_[num];
+ if (default_list_[num].size() > 0)
+ {
+ // This is a QComboBox. Clear list and add new entries.
+ setWidgetValue(widget, commandControlRemove, QByteArray());
+ interface_[ifname].list[num].clear();
+
+ foreach (QByteArray value, default_list_[num])
+ {
+ setWidgetValue(widget, commandControlAdd, value);
+ interface_[ifname].list[num].append(value);
+ }
+ }
+
+ switch (widget->property(interface_role_property).toInt())
+ {
+ case INTERFACE_ROLE_CONTROL:
+ setWidgetValue(widget, commandControlSet, default_value_[num]);
+ interface_[ifname].value[num] = default_value_[num];
+ break;
+
+ case INTERFACE_ROLE_LOGGER:
+ if (interface_[ifname].log_dialog)
+ {
+ interface_[ifname].log_dialog->clearText();
+ }
+ interface_[ifname].log_text.clear();
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+bool InterfaceToolbar::hasInterface(QString ifname)
+{
+ return interface_.contains(ifname);
+}
+
+void InterfaceToolbar::updateWidgets()
+{
+ const QString &ifname = ui->interfacesComboBox->currentText();
+ bool is_capturing = (interface_[ifname].out_fd == -1 ? false : true);
+
+ foreach (int num, control_widget_.keys())
+ {
+ QWidget *widget = control_widget_[num];
+ if (!is_capturing &&
+ (widget->property(interface_type_property).toInt() == INTERFACE_TYPE_BUTTON) &&
+ (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL))
+ {
+ widget->setEnabled(false);
+ }
+ else if (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL)
+ {
+ bool widget_enabled = !interface_[ifname].widget_disabled[num];
+ widget->setEnabled(widget_enabled);
+ if (label_widget_.contains(num))
+ {
+ label_widget_[num]->setEnabled(widget_enabled);
+ }
+ }
+ }
+
+ foreach (int num, control_widget_.keys())
+ {
+ QWidget *widget = control_widget_[num];
+ if ((widget->property(interface_type_property).toInt() == INTERFACE_TYPE_BUTTON) &&
+ (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_RESET))
+ {
+ widget->setEnabled(!is_capturing);
+ }
+ }
+}
+
+void InterfaceToolbar::on_interfacesComboBox_currentIndexChanged(const QString &ifname)
+{
+ foreach (int num, control_widget_.keys())
+ {
+ QWidget *widget = control_widget_[num];
+ if (interface_[ifname].list[num].size() > 0)
+ {
+ // This is a QComboBox. Clear list and add new entries.
+ setWidgetValue(widget, commandControlRemove, QByteArray());
+
+ foreach (QByteArray value, interface_[ifname].list[num])
+ {
+ setWidgetValue(widget, commandControlAdd, value);
+ }
+ }
+
+ if (widget->property(interface_role_property).toInt() == INTERFACE_ROLE_CONTROL)
+ {
+ setWidgetValue(widget, commandControlSet, interface_[ifname].value[num]);
+ }
+ }
+
+ updateWidgets();
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/interface_toolbar.h b/ui/qt/interface_toolbar.h
new file mode 100644
index 0000000000..ebde37db4d
--- /dev/null
+++ b/ui/qt/interface_toolbar.h
@@ -0,0 +1,120 @@
+/* interface_toolbar.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef INTERFACE_TOOLBAR_H
+#define INTERFACE_TOOLBAR_H
+
+#include <glib.h>
+
+#include "ui/iface_toolbar.h"
+#include "funnel_text_dialog.h"
+#include "interface_toolbar_reader.h"
+
+#include <QFrame>
+#include <QMap>
+#include <QString>
+
+
+namespace Ui {
+class InterfaceToolbar;
+}
+
+struct interface_values
+{
+ QThread *reader_thread;
+ int out_fd;
+ QMap<int, QByteArray> value;
+ QMap<int, QList<QByteArray> > list;
+ FunnelTextDialog *log_dialog;
+ QString log_text;
+ QMap<int, bool> widget_disabled;
+};
+
+class InterfaceToolbar : public QFrame
+{
+ Q_OBJECT
+
+public:
+ explicit InterfaceToolbar(QWidget *parent = 0, const iface_toolbar *toolbar = NULL);
+ ~InterfaceToolbar();
+
+ void startCapture(QString ifname, QString control_in, QString control_out);
+ void stopCapture();
+ bool hasInterface(QString ifname);
+
+public slots:
+ void controlReceived(QString ifname, int num, int command, QByteArray message);
+
+signals:
+ void closeReader();
+
+private slots:
+ void startReaderThread(QString ifname, QString control_in);
+ void updateWidgets();
+
+ void onButtonPressed();
+ void onCheckBoxChanged(int state);
+ void onComboBoxChanged(int idx);
+ void onLineEditChanged();
+ void onLogButtonPressed();
+ void onHelpButtonPressed();
+ void onResetButtonPressed();
+
+ void closeLog();
+
+ void on_interfacesComboBox_currentIndexChanged(const QString &ifname);
+
+private:
+ void initializeControls(const iface_toolbar *toolbar);
+ void setDefaultValue(int num, const QByteArray &value);
+ void sendChangedValues(QString ifname);
+ QWidget *createCheckbox(iface_toolbar_control *control);
+ QWidget *createButton(iface_toolbar_control *control);
+ QWidget *createSelector(iface_toolbar_control *control);
+ QWidget *createString(iface_toolbar_control *control);
+ void controlSend(QString ifname, int num, int type, const QByteArray &payload);
+ void setWidgetValue(QWidget *widget, int type, QByteArray payload);
+ void setInterfaceValue(QString ifname, QWidget *widget, int num, int type, QByteArray payload);
+
+ Ui::InterfaceToolbar *ui;
+ QMap<QString, struct interface_values> interface_;
+ QMap<int, QByteArray> default_value_;
+ QMap<int, QList<QByteArray> > default_list_;
+ QMap<int, QWidget *> control_widget_;
+ QMap<int, QWidget *> label_widget_;
+ QString help_link_;
+ bool use_spacer_;
+};
+
+#endif // INTERFACE_TOOLBAR_H
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/interface_toolbar.ui b/ui/qt/interface_toolbar.ui
new file mode 100644
index 0000000000..20faae5a57
--- /dev/null
+++ b/ui/qt/interface_toolbar.ui
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>InterfaceToolbar</class>
+ <widget class="QFrame" name="InterfaceToolbar">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>600</width>
+ <height>32</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Frame</string>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="interfacesLabel">
+ <property name="toolTip">
+ <string>Select interface</string>
+ </property>
+ <property name="text">
+ <string>Interface</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="interfacesComboBox">
+ <property name="toolTip">
+ <string>Select interface</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="leftLayout"/>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="rightLayout"/>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/ui/qt/interface_toolbar_lineedit.cpp b/ui/qt/interface_toolbar_lineedit.cpp
new file mode 100644
index 0000000000..181bcb3be5
--- /dev/null
+++ b/ui/qt/interface_toolbar_lineedit.cpp
@@ -0,0 +1,149 @@
+/* interface_toolbar_lineedit.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include "interface_toolbar_lineedit.h"
+#include "stock_icon_tool_button.h"
+#include "epan/prefs.h"
+#include "color_utils.h"
+
+#include <QStyle>
+
+// To do:
+// - Make a narrower apply button
+
+InterfaceToolbarLineEdit::InterfaceToolbarLineEdit(QWidget *parent, QString validation_regex, bool is_required) :
+ QLineEdit(parent),
+ validation_regex_(validation_regex),
+ is_required_(is_required),
+ text_edited_(false)
+{
+ apply_button_ = new StockIconToolButton(this, "x-filter-apply");
+ apply_button_->setCursor(Qt::ArrowCursor);
+ apply_button_->setEnabled(false);
+ apply_button_->setToolTip(tr("Apply changes"));
+ apply_button_->setIconSize(QSize(24, 14));
+ apply_button_->setStyleSheet(
+ "QToolButton {"
+ " border: none;"
+ " background: transparent;" // Disables platform style on Windows.
+ " padding: 0 0 0 0;"
+ "}"
+ );
+
+ updateStyleSheet(isValid());
+
+ connect(this, SIGNAL(textChanged(const QString &)), this, SLOT(validateText()));
+ connect(this, SIGNAL(textEdited(const QString &)), this, SLOT(validateEditedText()));
+ connect(this, SIGNAL(returnPressed()), this, SLOT(applyEditedText()));
+ connect(apply_button_, SIGNAL(clicked()), this, SLOT(applyEditedText()));
+}
+
+void InterfaceToolbarLineEdit::validateText()
+{
+ bool valid = isValid();
+
+ apply_button_->setEnabled(valid);
+ updateStyleSheet(valid);
+}
+
+void InterfaceToolbarLineEdit::validateEditedText()
+{
+ text_edited_ = true;
+}
+
+void InterfaceToolbarLineEdit::applyEditedText()
+{
+ if (text_edited_ && isValid())
+ {
+ emit editedTextApplied();
+ disableApplyButton();
+ }
+}
+
+void InterfaceToolbarLineEdit::disableApplyButton()
+{
+ apply_button_->setEnabled(false);
+ text_edited_ = false;
+}
+
+bool InterfaceToolbarLineEdit::isValid()
+{
+ bool valid = true;
+
+ if (is_required_ && text().length() == 0)
+ {
+ valid = false;
+ }
+
+ if (!validation_regex_.isEmpty() && text().length() > 0)
+ {
+ QRegExp expr(validation_regex_);
+ if (!expr.isValid() || expr.indexIn(text(), 0) == -1)
+ {
+ valid = false;
+ }
+ }
+
+ return valid;
+}
+
+void InterfaceToolbarLineEdit::updateStyleSheet(bool is_valid)
+{
+ int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
+ QSize apsz = apply_button_->sizeHint();
+
+ QString style_sheet = QString(
+ "InterfaceToolbarLineEdit {"
+ " padding-right: %1px;"
+ " background-color: %2;"
+ "}"
+ )
+ .arg(apsz.width() + frameWidth)
+ .arg(is_valid ? QString("") : ColorUtils::fromColorT(prefs.gui_text_invalid).name());
+
+ setStyleSheet(style_sheet);
+}
+
+void InterfaceToolbarLineEdit::resizeEvent(QResizeEvent *)
+{
+ int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
+ QSize apsz = apply_button_->sizeHint();
+
+ apply_button_->move(contentsRect().right() - frameWidth - apsz.width() + 2,
+ contentsRect().top());
+ apply_button_->setMinimumHeight(contentsRect().height());
+ apply_button_->setMaximumHeight(contentsRect().height());
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/interface_toolbar_lineedit.h b/ui/qt/interface_toolbar_lineedit.h
new file mode 100644
index 0000000000..e26a50c976
--- /dev/null
+++ b/ui/qt/interface_toolbar_lineedit.h
@@ -0,0 +1,71 @@
+/* interface_toolbar_lineedit.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef INTERFACE_TOOLBAR_LINEEDIT_H
+#define INTERFACE_TOOLBAR_LINEEDIT_H
+
+#include <QLineEdit>
+
+class StockIconToolButton;
+
+class InterfaceToolbarLineEdit : public QLineEdit
+{
+ Q_OBJECT
+
+public:
+ explicit InterfaceToolbarLineEdit(QWidget *parent = 0, QString validation_regex = QString(), bool is_required = false);
+ void disableApplyButton();
+
+protected:
+ void resizeEvent(QResizeEvent *);
+
+signals:
+ void editedTextApplied();
+
+private slots:
+ void validateText();
+ void validateEditedText();
+ void applyEditedText();
+
+private:
+ bool isValid();
+ void updateStyleSheet(bool is_valid);
+
+ StockIconToolButton *apply_button_;
+ QString validation_regex_;
+ bool is_required_;
+ bool text_edited_;
+};
+
+#endif // INTERFACE_TOOLBAR_LINEEDIT_H
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/interface_toolbar_reader.cpp b/ui/qt/interface_toolbar_reader.cpp
new file mode 100644
index 0000000000..5c8c52a247
--- /dev/null
+++ b/ui/qt/interface_toolbar_reader.cpp
@@ -0,0 +1,140 @@
+/* interface_toolbar_reader.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <errno.h>
+
+#include "interface_toolbar_reader.h"
+#include "sync_pipe.h"
+#include "wsutil/file_util.h"
+
+#include <QThread>
+
+const int header_size = 6;
+
+// To do:
+// - Add support for WIN32
+
+void InterfaceToolbarReader::loop()
+{
+#ifndef _WIN32
+ struct timeval timeout;
+ QByteArray header;
+ QByteArray payload;
+ fd_set readfds;
+
+ int fd = ws_open(control_in_.toUtf8(), O_RDONLY | O_BINARY | O_NONBLOCK, 0);
+ if (fd == -1)
+ {
+ emit finished();
+ return;
+ }
+
+ forever
+ {
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+
+ timeout.tv_sec = 2;
+ timeout.tv_usec = 0;
+
+ int ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
+ if (ret == -1)
+ {
+ break;
+ }
+#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)
+ if (QThread::currentThread()->isInterruptionRequested())
+ {
+ break;
+ }
+#endif
+
+ if (ret > 0 && FD_ISSET(fd, &readfds))
+ {
+ header.resize(header_size);
+ if (ws_read(fd, header.data(), header_size) != header_size)
+ {
+ break;
+ }
+
+ unsigned char high_nibble = header[1] & 0xFF;
+ unsigned char mid_nibble = header[2] & 0xFF;
+ unsigned char low_nibble = header[3] & 0xFF;
+ ssize_t payload_len = (ssize_t)((high_nibble << 16) + (mid_nibble << 8) + low_nibble) - 2;
+
+ payload.resize(payload_len);
+ if (payload_len > 0)
+ {
+ ssize_t total_len = 0;
+ while (total_len < payload_len)
+ {
+ ssize_t read_len = ws_read(fd, payload.data() + total_len, payload_len - total_len);
+ if (read_len == -1)
+ {
+ if (errno != EAGAIN)
+ {
+ break;
+ }
+ }
+ else
+ {
+ total_len += read_len;
+ }
+ }
+ if (total_len != payload_len)
+ {
+ break;
+ }
+ }
+ if (header[0] == SP_TOOLBAR_CTRL)
+ {
+ emit received(ifname_, (unsigned char)header[4], (unsigned char)header[5], payload);
+ }
+ }
+ }
+
+ ws_close(fd);
+#endif
+ emit finished();
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/interface_toolbar_reader.h b/ui/qt/interface_toolbar_reader.h
new file mode 100644
index 0000000000..1b34db4fb0
--- /dev/null
+++ b/ui/qt/interface_toolbar_reader.h
@@ -0,0 +1,65 @@
+/* interface_toolbar_reader.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef INTERFACE_TOOLBAR_READER_H
+#define INTERFACE_TOOLBAR_READER_H
+
+#include <QObject>
+#include <QByteArray>
+
+namespace Ui {
+class InterfaceToolbarReader;
+}
+
+class InterfaceToolbarReader : public QObject
+{
+ Q_OBJECT
+
+public:
+ InterfaceToolbarReader(QString ifname, QString control_in, QObject *parent = 0) :
+ QObject(parent), ifname_(ifname), control_in_(control_in) {}
+
+public slots:
+ void loop();
+
+signals:
+ void received(QString ifname, int num, int command, QByteArray payload);
+ void finished();
+
+private:
+ QString ifname_;
+ QString control_in_;
+};
+
+#endif // INTERFACE_TOOLBAR_READER_H
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/main_window.cpp b/ui/qt/main_window.cpp
index 1570b4f4cc..0952594516 100644
--- a/ui/qt/main_window.cpp
+++ b/ui/qt/main_window.cpp
@@ -33,6 +33,7 @@
#include <epan/export_object.h>
#include "ui/commandline.h"
+#include "ui/iface_toolbar.h"
#ifdef HAVE_LIBPCAP
#include "ui/capture.h"
@@ -61,6 +62,7 @@
#include "file_set_dialog.h"
#include "funnel_statistics.h"
#include "import_text_dialog.h"
+#include "interface_toolbar.h"
#include "packet_list.h"
#include "proto_tree.h"
#include "simple_dialog.h"
@@ -205,6 +207,23 @@ static void plugin_if_mainwindow_update_toolbars(gconstpointer user_data)
if (g_hash_table_lookup_extended(data_set, "toolbar_name", NULL, NULL)) {
QString toolbarName((const char *)g_hash_table_lookup(data_set, "toolbar_name"));
gbl_cur_main_window_->removeAdditionalToolbar(toolbarName);
+
+ }
+}
+
+static void mainwindow_add_toolbar(const iface_toolbar *toolbar_entry)
+{
+ if (gbl_cur_main_window_ && toolbar_entry)
+ {
+ gbl_cur_main_window_->addInterfaceToolbar(toolbar_entry);
+ }
+}
+
+static void mainwindow_remove_toolbar(const gchar *menu_title)
+{
+ if (gbl_cur_main_window_ && menu_title)
+ {
+ gbl_cur_main_window_->removeInterfaceToolbar(menu_title);
}
}
@@ -759,6 +778,17 @@ MainWindow::MainWindow(QWidget *parent) :
#endif
plugin_if_register_gui_cb(PLUGIN_IF_REMOVE_TOOLBAR, plugin_if_mainwindow_update_toolbars);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) && !defined(_WIN32)
+ // Register Interface Toolbar callbacks
+ //
+ // Qt version must be 5.2 or higher because the use of
+ // QThread::requestInterruption() in interface_toolbar.cpp and
+ // QThread::isInterruptionRequested() in interface_toolbar_reader.cpp
+ //
+ // The toolbar in/out control pipes are not supported on WIN32 yet.
+ iface_toolbar_register_cb(mainwindow_add_toolbar, mainwindow_remove_toolbar);
+#endif
+
main_ui_->mainStack->setCurrentWidget(main_welcome_);
}
@@ -779,6 +809,13 @@ QMenu *MainWindow::createPopupMenu()
menu->addAction(main_ui_->actionViewFilterToolbar);
menu->addAction(main_ui_->actionViewWirelessToolbar);
+ if (!main_ui_->menuInterfaceToolbars->actions().isEmpty()) {
+ QMenu *submenu = menu->addMenu(main_ui_->menuInterfaceToolbars->title());
+ foreach (QAction *action, main_ui_->menuInterfaceToolbars->actions()) {
+ submenu->addAction(action);
+ }
+ }
+
if (!main_ui_->menuAdditionalToolbars->actions().isEmpty()) {
QMenu *subMenu = menu->addMenu(main_ui_->menuAdditionalToolbars->title());
foreach (QAction *action, main_ui_->menuAdditionalToolbars->actions()) {
@@ -795,6 +832,76 @@ QMenu *MainWindow::createPopupMenu()
return menu;
}
+void MainWindow::addInterfaceToolbar(const iface_toolbar *toolbar_entry)
+{
+ QMenu *menu = main_ui_->menuInterfaceToolbars;
+ bool visible = g_list_find_custom(recent.interface_toolbars, toolbar_entry->menu_title, (GCompareFunc) strcmp) ? true : false;
+
+ QString title = QString().fromUtf8(toolbar_entry->menu_title);
+ QAction *action = new QAction(title, menu);
+ action->setEnabled(true);
+ action->setCheckable(true);
+ action->setChecked(visible);
+ action->setToolTip(tr("Show or hide the toolbar"));
+
+ QAction *before = NULL;
+ foreach (QAction *action, menu->actions()) {
+ // Ensure we add the menu entries in sorted order
+ if (action->text().compare(title, Qt::CaseInsensitive) > 0) {
+ before = action;
+ break;
+ }
+ }
+ menu->insertAction(before, action);
+
+ InterfaceToolbar *interface_toolbar = new InterfaceToolbar(this, toolbar_entry);
+
+ QToolBar *toolbar = new QToolBar(this);
+ toolbar->addWidget(interface_toolbar);
+ toolbar->setMovable(false);
+ toolbar->setVisible(visible);
+
+ action->setData(qVariantFromValue(toolbar));
+
+ addToolBar(Qt::TopToolBarArea, toolbar);
+ insertToolBarBreak(toolbar);
+
+ if (show_hide_actions_) {
+ show_hide_actions_->addAction(action);
+ }
+
+ menu->menuAction()->setVisible(true);
+}
+
+void MainWindow::removeInterfaceToolbar(const gchar *menu_title)
+{
+ QMenu *menu = main_ui_->menuInterfaceToolbars;
+ QAction *action = NULL;
+ QMap<QAction *, QWidget *>::iterator i;
+
+ QString title = QString().fromUtf8(menu_title);
+ foreach (action, menu->actions()) {
+ if (title.compare(action->text()) == 0) {
+ break;
+ }
+ }
+
+ if (action) {
+ if (show_hide_actions_) {
+ show_hide_actions_->removeAction(action);
+ }
+ menu->removeAction(action);
+
+ QToolBar *toolbar = action->data().value<QToolBar *>();
+ removeToolBar(toolbar);
+
+ delete action;
+ delete toolbar;
+ }
+
+ menu->menuAction()->setVisible(!menu->actions().isEmpty());
+}
+
void MainWindow::setPipeInputHandler(gint source, gpointer user_data, ws_process_id *child_process, pipe_input_cb_t input_cb)
{
pipe_source_ = source;
@@ -1895,6 +2002,9 @@ void MainWindow::initShowHideMainWidgets()
showHideMainWidgets(shmwa);
}
+ // Initial hide the Interface Toolbar submenu
+ main_ui_->menuInterfaceToolbars->menuAction()->setVisible(false);
+
/* Initially hide the additional toolbars menus */
main_ui_->menuAdditionalToolbars->menuAction()->setVisible(false);
@@ -2356,7 +2466,7 @@ void MainWindow::resizeEvent(QResizeEvent *event)
}
/* Update main window items based on whether there's a capture in progress. */
-void MainWindow::setForCaptureInProgress(bool capture_in_progress)
+void MainWindow::setForCaptureInProgress(bool capture_in_progress, GArray *ifaces)
{
setMenusForCaptureInProgress(capture_in_progress);
@@ -2368,6 +2478,20 @@ void MainWindow::setForCaptureInProgress(bool capture_in_progress)
// set_capture_if_dialog_for_capture_in_progress(capture_in_progress);
#endif
+
+#ifdef HAVE_EXTCAP
+ QList<InterfaceToolbar *> toolbars = findChildren<InterfaceToolbar *>();
+ foreach (InterfaceToolbar *toolbar, toolbars) {
+ if (capture_in_progress && ifaces) {
+ for (guint i = 0; i < ifaces->len; i++) {
+ interface_options interface_opts = g_array_index(ifaces, interface_options, i);
+ toolbar->startCapture(interface_opts.name, interface_opts.extcap_control_in, interface_opts.extcap_control_out);
+ }
+ } else {
+ toolbar->stopCapture();
+ }
+ }
+#endif
}
static QList<register_stat_group_t> menu_groups = QList<register_stat_group_t>()
diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h
index aeb3a18052..23e1de7cce 100644
--- a/ui/qt/main_window.h
+++ b/ui/qt/main_window.h
@@ -31,6 +31,7 @@
#include "file.h"
#include "ui/ui_util.h"
+#include "ui/iface_toolbar.h"
#include <epan/prefs.h>
#include <epan/plugin_if.h>
@@ -100,6 +101,9 @@ public:
void removeAdditionalToolbar(QString toolbarName);
+ void addInterfaceToolbar(const iface_toolbar *toolbar_entry);
+ void removeInterfaceToolbar(const gchar *menu_title);
+
protected:
virtual bool eventFilter(QObject *obj, QEvent *event);
virtual void keyPressEvent(QKeyEvent *event);
@@ -224,7 +228,7 @@ private:
void externalMenuHelper(ext_menu_t * menu, QMenu * subMenu, gint depth);
- void setForCaptureInProgress(bool capture_in_progress = false);
+ void setForCaptureInProgress(bool capture_in_progress = false, GArray *ifaces = NULL);
QMenu* findOrAddMenu(QMenu *parent_menu, QString& menu_text);
void recursiveCopyProtoTreeItems(QTreeWidgetItem *item, QString &clip, int ident_level);
diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui
index 39bfc55cb1..8ee6ec69c9 100644
--- a/ui/qt/main_window.ui
+++ b/ui/qt/main_window.ui
@@ -268,6 +268,11 @@
<property name="title">
<string>&amp;View</string>
</property>
+ <widget class="QMenu" name="menuInterfaceToolbars">
+ <property name="title">
+ <string>Interface Toolbars</string>
+ </property>
+ </widget>
<widget class="QMenu" name="menuZoom">
<property name="title">
<string>&amp;Zoom</string>
@@ -345,6 +350,7 @@
<addaction name="actionViewMainToolbar"/>
<addaction name="actionViewFilterToolbar"/>
<addaction name="actionViewWirelessToolbar"/>
+ <addaction name="menuInterfaceToolbars"/>
<addaction name="menuAdditionalToolbars" />
<addaction name="actionViewStatusBar"/>
<addaction name="separator"/>
diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp
index 4103da00b8..33eaa56318 100644
--- a/ui/qt/main_window_slots.cpp
+++ b/ui/qt/main_window_slots.cpp
@@ -119,6 +119,7 @@
#include "funnel_statistics.h"
#include "gsm_map_summary_dialog.h"
#include "iax2_analysis_dialog.h"
+#include "interface_toolbar.h"
#include "io_graph_dialog.h"
#include <additional_toolbar.h>
#include "lbm_stream_dialog.h"
@@ -486,6 +487,15 @@ void MainWindow::layoutToolbars()
main_ui_->wirelessToolBar->setVisible(recent.wireless_toolbar_show);
main_ui_->statusBar->setVisible(recent.statusbar_show);
+ foreach (QAction *action, main_ui_->menuInterfaceToolbars->actions()) {
+ QToolBar *toolbar = action->data().value<QToolBar *>();
+ if (g_list_find_custom(recent.interface_toolbars, action->text().toUtf8(), (GCompareFunc) strcmp)) {
+ toolbar->setVisible(true);
+ } else {
+ toolbar->setVisible(false);
+ }
+ }
+
QList<QToolBar *> toolbars = findChildren<QToolBar *>();
foreach (QToolBar *bar, toolbars) {
AdditionalToolBar *iftoolbar = dynamic_cast<AdditionalToolBar *>(bar);
@@ -495,6 +505,7 @@ void MainWindow::layoutToolbars()
visible = true;
iftoolbar->setVisible(visible);
+
}
}
}
@@ -523,6 +534,14 @@ void MainWindow::updateRecentActions()
main_ui_->actionViewPacketDetails->setChecked(recent.tree_view_show && prefs_has_layout_pane_content(layout_pane_content_pdetails));
main_ui_->actionViewPacketBytes->setChecked(recent.byte_view_show && prefs_has_layout_pane_content(layout_pane_content_pbytes));
+ foreach (QAction *action, main_ui_->menuInterfaceToolbars->actions()) {
+ if (g_list_find_custom(recent.interface_toolbars, action->text().toUtf8(), (GCompareFunc) strcmp)) {
+ action->setChecked(true);
+ } else {
+ action->setChecked(false);
+ }
+ }
+
foreach (QAction * action, main_ui_->menuAdditionalToolbars->actions()) {
ext_toolbar_t * toolbar = VariantPointer<ext_toolbar_t>::asPtr(action->data());
bool checked = false;
@@ -628,7 +647,7 @@ void MainWindow::queuedFilterAction(QString action_filter, FilterAction::Action
// Capture callbacks
-void MainWindow::captureCapturePrepared(capture_session *) {
+void MainWindow::captureCapturePrepared(capture_session *session) {
#ifdef HAVE_LIBPCAP
setTitlebarForCaptureInProgress();
@@ -636,7 +655,7 @@ void MainWindow::captureCapturePrepared(capture_session *) {
/* Disable menu items that make no sense if you're currently running
a capture. */
- setForCaptureInProgress(true);
+ setForCaptureInProgress(true, session->capture_opts->ifaces);
// set_capture_if_dialog_for_capture_in_progress(TRUE);
// /* Don't set up main window for a capture file. */
@@ -645,14 +664,14 @@ void MainWindow::captureCapturePrepared(capture_session *) {
#endif // HAVE_LIBPCAP
}
-void MainWindow::captureCaptureUpdateStarted(capture_session *) {
+void MainWindow::captureCaptureUpdateStarted(capture_session *session) {
#ifdef HAVE_LIBPCAP
/* We've done this in "prepared" above, but it will be cleared while
switching to the next multiple file. */
setTitlebarForCaptureInProgress();
- setForCaptureInProgress(true);
+ setForCaptureInProgress(true, session->capture_opts->ifaces);
setForCapturedPackets(true);
#endif // HAVE_LIBPCAP
@@ -2257,6 +2276,19 @@ void MainWindow::showHideMainWidgets(QAction *action)
recent.byte_view_show = show;
main_ui_->actionViewPacketBytes->setChecked(show);
} else {
+ foreach (QAction *action, main_ui_->menuInterfaceToolbars->actions()) {
+ QToolBar *toolbar = action->data().value<QToolBar *>();
+ if (widget == toolbar) {
+ GList *entry = g_list_find_custom(recent.interface_toolbars, action->text().toUtf8(), (GCompareFunc) strcmp);
+ if (show && !entry) {
+ recent.interface_toolbars = g_list_append(recent.interface_toolbars, g_strdup(action->text().toUtf8()));
+ } else if (!show && entry) {
+ recent.interface_toolbars = g_list_remove(recent.interface_toolbars, entry->data);
+ }
+ action->setChecked(show);
+ }
+ }
+
ext_toolbar_t * toolbar = VariantPointer<ext_toolbar_t>::asPtr(action->data());
if (toolbar) {
GList *entry = g_list_find_custom(recent.gui_additional_toolbars, toolbar->name, (GCompareFunc) strcmp);
diff --git a/ui/recent.c b/ui/recent.c
index 30c44f485a..898296c1c0 100644
--- a/ui/recent.c
+++ b/ui/recent.c
@@ -75,6 +75,7 @@
#define RECENT_GUI_RLC_PDUS_FROM_MAC_FRAMES "gui.rlc_pdus_from_mac_frames"
#define RECENT_GUI_CUSTOM_COLORS "gui.custom_colors"
#define RECENT_GUI_TOOLBAR_SHOW "gui.additional_toolbar_show"
+#define RECENT_GUI_INTERFACE_TOOLBAR_SHOW "gui.interface_toolbar_show"
#define RECENT_GUI_GEOMETRY "gui.geom."
@@ -866,6 +867,12 @@ write_profile_recent(void)
fprintf(rf, RECENT_GUI_TOOLBAR_SHOW ": %s\n", string_list);
g_free(string_list);
+ fprintf(rf, "\n# Interface Toolbars show.\n");
+ fprintf(rf, "# List of interface toolbars to show.\n");
+ string_list = join_string_list(recent.interface_toolbars);
+ fprintf(rf, RECENT_GUI_INTERFACE_TOOLBAR_SHOW ": %s\n", string_list);
+ g_free(string_list);
+
fclose(rf);
/* XXX - catch I/O errors (e.g. "ran out of disk space") and return
@@ -1121,6 +1128,8 @@ read_set_recent_pair_static(gchar *key, const gchar *value,
recent.gui_fileopen_remembered_dir = g_strdup(value);
} else if (strcmp(key, RECENT_GUI_TOOLBAR_SHOW) == 0) {
recent.gui_additional_toolbars = prefs_get_string_list(value);
+ } else if (strcmp(key, RECENT_GUI_INTERFACE_TOOLBAR_SHOW) == 0) {
+ recent.interface_toolbars = prefs_get_string_list(value);
}
return PREFS_SET_OK;
@@ -1294,6 +1303,11 @@ recent_read_profile_static(char **rf_path_return, int *rf_errno_return)
recent.gui_additional_toolbars = NULL;
}
+ if (recent.interface_toolbars) {
+ g_list_free_full (recent.interface_toolbars, g_free);
+ recent.interface_toolbars = NULL;
+ }
+
/* Construct the pathname of the user's profile recent file. */
rf_path = get_persconffile_path(RECENT_FILE_NAME, TRUE);
diff --git a/ui/recent.h b/ui/recent.h
index 2f33302b9b..d41dd60d3d 100644
--- a/ui/recent.h
+++ b/ui/recent.h
@@ -111,6 +111,7 @@ typedef struct recent_settings_tag {
gboolean gui_rlc_use_pdus_from_mac;
GList *custom_colors;
GList *gui_additional_toolbars;
+ GList *interface_toolbars;
} recent_settings_t;
/** Global recent settings. */