aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoland Knall <roland.knall@br-automation.com>2014-02-25 14:05:11 +0100
committerMichael Mann <mmann78@netscape.net>2014-08-21 03:34:02 +0000
commitbed29af46db06f4bce00d8a4dab26317d4563dd3 (patch)
tree3502e7ee703097a9c7c3e067ac9e6c7b5ad9ed8a
parent401469880b8b98a4d42011bdf9af7fbb67c6f057 (diff)
Extcap Capture Interface
Extcap is a plugin interface, which allows for the usage of external capture interfaces via pipes using a predefined configuration language which results in a graphical gui. This implementation seeks for a generic implementation, which results in a seamless integration with the current system, and does add all external interfaces as simple interfaces. Windows Note: Due to limitations with GTK and Windows, a gspawn-winXX-helper.exe, respective gspawn-winXX-helper-console.exe is needed, which is part of any GTK windows installation. The default installation directory from the build is an extcap subdirectory underneath the run directory. The folder used by extcap may be viewed in the folders tab of the about dialog. The default installation directory for extcap plugins with a pre-build or installer version of wireshark is the extcap subdirectory underneath the main wireshark directory. For more information see: http://youtu.be/Nn84T506SwU bug #9009 Also take a look in doc/extcap_example.py for a Python-example and in extcap.pod for the arguments grammer. Todo: - Integrate with Qt - currently no GUI is generated, but the interfaces are still usable Change-Id: I4f1239b2f1ebd8b2969f73af137915f5be1ce50f Signed-off-by: Mike Ryan <mikeryan+wireshark@lacklustre.net> Signed-off-by: Mike Kershaw <dragorn@kismetwireless.net> Signed-off-by: Roland Knall <rknall@gmail.com> Reviewed-on: https://code.wireshark.org/review/359 Petri-Dish: Michael Mann <mmann78@netscape.net> Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org> Reviewed-by: Michael Mann <mmann78@netscape.net>
-rw-r--r--CMakeLists.txt14
-rw-r--r--CMakeOptions.txt1
-rw-r--r--Makefile.common6
-rw-r--r--Makefile.nmake2
-rw-r--r--capchild/capture_ifinfo.c31
-rw-r--r--capchild/capture_sync.c52
-rw-r--r--capture_opts.c36
-rw-r--r--capture_opts.h10
-rw-r--r--caputils/capture-pcap-util.c6
-rw-r--r--caputils/capture_ifinfo.h6
-rw-r--r--cmakeconfig.h.in6
-rw-r--r--config.h.win321
-rw-r--r--configure.ac27
-rw-r--r--doc/extcap.pod78
-rwxr-xr-xdoc/extcap_example.py249
-rw-r--r--dumpcap.c29
-rw-r--r--extcap.c654
-rw-r--r--extcap.h90
-rw-r--r--extcap_parser.c881
-rw-r--r--extcap_parser.h253
-rw-r--r--packaging/nsis/wireshark.nsi2
-rw-r--r--ui/gtk/CMakeLists.txt7
-rw-r--r--ui/gtk/Makefile.common2
-rw-r--r--ui/gtk/about_dlg.c12
-rw-r--r--ui/gtk/capture_dlg.c77
-rw-r--r--ui/gtk/capture_if_dlg.c3
-rw-r--r--ui/gtk/extcap_gtk.c806
-rw-r--r--ui/gtk/extcap_gtk.h122
-rw-r--r--ui/iface_lists.c11
-rw-r--r--ui/qt/QtShark.pro2
-rw-r--r--ui/qt/about_dialog.cpp13
-rw-r--r--wsutil/filesystem.c111
-rw-r--r--wsutil/filesystem.h7
33 files changed, 3589 insertions, 18 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a928f69f91..07a9863783 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -792,6 +792,11 @@ foreach(PLUGIN_DIR ${PLUGIN_SRC_DIRS})
add_subdirectory( ${PLUGIN_DIR} )
endforeach()
+if(ENABLE_EXTCAP)
+ set(HAVE_EXTCAP 1)
+ set(EXTCAP_DIR "${DATAFILE_DIR}/extcap/")
+endif()
+
add_subdirectory( asn1 EXCLUDE_FROM_ALL )
add_subdirectory( capchild )
add_subdirectory( caputils )
@@ -945,6 +950,15 @@ set(SHARK_COMMON_SRC
version_info.c
)
+# sources for external capture interfaces
+if(ENABLE_EXTCAP)
+ set(SHARK_COMMON_SRC
+ ${SHARK_COMMON_SRC}
+ extcap.c
+ extcap_parser.c
+ )
+endif()
+
set(TSHARK_TAP_SRC
ui/cli/tap-afpstat.c
ui/cli/tap-ansi_astat.c
diff --git a/CMakeOptions.txt b/CMakeOptions.txt
index ce0b944719..75e61658aa 100644
--- a/CMakeOptions.txt
+++ b/CMakeOptions.txt
@@ -44,6 +44,7 @@ endif()
option(ENABLE_STATIC "Build a static version of Wireshark (not yet working)" OFF)
option(ENABLE_ECHLD "Enable echld support" OFF)
option(ENABLE_PLUGINS "Build with plugins" ON)
+option(ENABLE_EXTCAP "Build with extcap hooks" ON)
option(ENABLE_GUIDES "Build Guides" OFF)
option(ENABLE_PCAP_NG_DEFAULT "Enable pcap-ng as default file format" ON)
diff --git a/Makefile.common b/Makefile.common
index 688b6e56b9..a6f501a764 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -44,12 +44,16 @@ SHARK_COMMON_SRC = \
cfile.c \
frame_tvbuff.c \
sync_pipe_write.c \
- version_info.c
+ version_info.c \
+ extcap.c \
+ extcap_parser.c
# corresponding headers
SHARK_COMMON_INCLUDES = \
cfile.h \
color.h \
+ extcap.h \
+ extcap_parser.h \
file.h \
fileset.h \
frame_tvbuff.h \
diff --git a/Makefile.nmake b/Makefile.nmake
index 5eb24dcafc..47af7e7028 100644
--- a/Makefile.nmake
+++ b/Makefile.nmake
@@ -1371,6 +1371,8 @@ install-all: install-generated-files
xcopy $(GTK_DIR)\bin\libgobject-2.0-0.dll $(INSTALL_DIR) /d
xcopy $(GTK_DIR)\bin\libgthread-2.0-0.dll $(INSTALL_DIR) /d
xcopy $(GTK_DIR)\bin\$(INTL_DLL) $(INSTALL_DIR) /d
+ xcopy $(GTK_DIR)\bin\gspawn-$(WIRESHARK_TARGET_PLATFORM)-helper.exe $(INSTALL_DIR) /d
+ xcopy $(GTK_DIR)\bin\gspawn-$(WIRESHARK_TARGET_PLATFORM)-helper-console.exe $(INSTALL_DIR) /d
!IFDEF ZLIB_DIR
xcopy $(ZLIB_DLL) $(INSTALL_DIR) /d
!ENDIF
diff --git a/capchild/capture_ifinfo.c b/capchild/capture_ifinfo.c
index 54e4d4c4fc..0ae807ec53 100644
--- a/capchild/capture_ifinfo.c
+++ b/capchild/capture_ifinfo.c
@@ -49,6 +49,9 @@
#include "capture_opts.h"
#include <capchild/capture_session.h>
#include <capchild/capture_sync.h>
+#ifdef HAVE_EXTCAP
+#include "extcap.h"
+#endif
#include "log.h"
#include <caputils/capture_ifinfo.h>
@@ -117,6 +120,10 @@ capture_interface_list(int *err, char **err_str, void (*update_cb)(void))
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface List ...");
*err = 0;
+#ifdef HAVE_EXTCAP
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Loading External Capture Interface List ...");
+ if_list = extcap_interface_list(err_str);
+#endif
/* Try to get our interface list */
ret = sync_interface_list_open(&data, &primary_msg, &secondary_msg, update_cb);
@@ -143,12 +150,22 @@ capture_interface_list(int *err, char **err_str, void (*update_cb)(void))
g_free(data);
for (i = 0; raw_list[i] != NULL; i++) {
+#ifdef HAVE_EXTCAP
+ if_parts = g_strsplit(raw_list[i], "\t", 7);
+ if (if_parts[0] == NULL || if_parts[1] == NULL || if_parts[2] == NULL ||
+ if_parts[3] == NULL || if_parts[4] == NULL || if_parts[5] == NULL ||
+ if_parts[6] == NULL) {
+ g_strfreev(if_parts);
+ continue;
+ }
+#else
if_parts = g_strsplit(raw_list[i], "\t", 6);
if (if_parts[0] == NULL || if_parts[1] == NULL || if_parts[2] == NULL ||
if_parts[3] == NULL || if_parts[4] == NULL || if_parts[5] == NULL) {
g_strfreev(if_parts);
continue;
}
+#endif
/* Number followed by the name, e.g "1. eth0" */
name = strchr(if_parts[0], ' ');
@@ -184,6 +201,9 @@ capture_interface_list(int *err, char **err_str, void (*update_cb)(void))
}
if (strcmp(if_parts[5], "loopback") == 0)
if_info->loopback = TRUE;
+#ifdef HAVE_EXTCAP
+ if_info->extcap = g_strdup(if_parts[6]);
+#endif
g_strfreev(if_parts);
g_strfreev(addr_parts);
if_list = g_list_append(if_list, if_info);
@@ -213,6 +233,17 @@ capture_get_if_capabilities(const gchar *ifname, gboolean monitor_mode,
g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_MESSAGE, "Capture Interface Capabilities ...");
+#ifdef HAVE_EXTCAP
+ /* see if the interface is from extcap */
+ caps = extcap_get_if_dlts(ifname, err_str);
+ if (caps != NULL)
+ return caps;
+
+ /* return if the extcap interface generated an error */
+ if (err_str != NULL && *err_str != NULL)
+ return NULL;
+#endif /* HAVE_EXTCAP */
+
/* Try to get our interface list */
err = sync_if_capabilities_open(ifname, monitor_mode, &data,
&primary_msg, &secondary_msg, update_cb);
diff --git a/capchild/capture_sync.c b/capchild/capture_sync.c
index f744b1a92d..d49a5c8080 100644
--- a/capchild/capture_sync.c
+++ b/capchild/capture_sync.c
@@ -98,6 +98,9 @@
#include <wsutil/filesystem.h>
#include <wsutil/file_util.h>
#include <wsutil/report_err.h>
+#ifdef HAVE_EXTCAP
+#include "extcap.h"
+#endif
#include "log.h"
#ifdef _WIN32
@@ -391,6 +394,14 @@ sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, voi
cap_session->fork_child = -1;
+#ifdef HAVE_EXTCAP
+ if (!extcaps_init_initerfaces(capture_opts)) {
+ report_failure("Unable to init extcaps. (tmp fifo already exists?)");
+ return FALSE;
+ }
+
+#endif
+
argv = init_pipe_args(&argc);
if (!argv) {
/* We don't know where to find dumpcap. */
@@ -463,7 +474,12 @@ sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, voi
interface_opts = g_array_index(capture_opts->ifaces, interface_options, j);
argv = sync_pipe_add_arg(argv, &argc, "-i");
- argv = sync_pipe_add_arg(argv, &argc, interface_opts.name);
+#ifdef HAVE_EXTCAP
+ if (interface_opts.extcap_fifo != NULL)
+ argv = sync_pipe_add_arg(argv, &argc, interface_opts.extcap_fifo);
+ else
+#endif
+ argv = sync_pipe_add_arg(argv, &argc, interface_opts.name);
if (interface_opts.cfilter != NULL && strlen(interface_opts.cfilter) != 0) {
argv = sync_pipe_add_arg(argv, &argc, "-f");
@@ -476,8 +492,12 @@ sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, voi
}
if (interface_opts.linktype != -1) {
- argv = sync_pipe_add_arg(argv, &argc, "-y");
- argv = sync_pipe_add_arg(argv, &argc, linktype_val_to_name(interface_opts.linktype));
+ const char *linktype = linktype_val_to_name(interface_opts.linktype);
+ if ( linktype != NULL )
+ {
+ argv = sync_pipe_add_arg(argv, &argc, "-y");
+ argv = sync_pipe_add_arg(argv, &argc, linktype);
+ }
}
if (!interface_opts.promisc_mode) {
@@ -487,6 +507,8 @@ sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, voi
#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
if (interface_opts.buffer_size != DEFAULT_CAPTURE_BUFFER_SIZE) {
argv = sync_pipe_add_arg(argv, &argc, "-B");
+ if(interface_opts.buffer_size == 0x00)
+ interface_opts.buffer_size = DEFAULT_CAPTURE_BUFFER_SIZE;
g_snprintf(buffer_size, ARGV_NUMBER_LEN, "%d", interface_opts.buffer_size);
argv = sync_pipe_add_arg(argv, &argc, buffer_size);
}
@@ -591,7 +613,20 @@ sync_pipe_start(capture_options *capture_opts, capture_session *cap_session, voi
#else
si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE; /* this hides the console window */
- si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+#if defined(_WIN32)
+ /* needs first a check if NULL *
+ * otherwise wouldnt work with non extcap interfaces */
+ if(interface_opts.extcap_fifo != NULL)
+ {
+ if(strncmp(interface_opts.extcap_fifo,"\\\\.\\pipe\\",9)== 0)
+ {
+ si.hStdInput = extcap_get_win32_handle();
+ }
+ }
+ else
+#endif
+ si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdError = sync_pipe_write;
/*si.hStdError = (HANDLE) _get_osfhandle(2);*/
@@ -805,7 +840,8 @@ sync_pipe_open_command(char** argv, int *data_read_fd,
#else
si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE; /* this hides the console window */
- si.hStdInput = NULL;
+ si.hStdInput = NULL; /* handle for named pipe*/
+
si.hStdOutput = data_pipe[PIPE_WRITE];
si.hStdError = sync_pipe[PIPE_WRITE];
#endif
@@ -1741,6 +1777,10 @@ sync_pipe_input_cb(gint source, gpointer user_data)
#ifdef _WIN32
ws_close(cap_session->signal_pipe_write_fd);
#endif
+#ifdef HAVE_EXTCAP
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "sync_pipe_input_cb: cleaning extcap pipe");
+ extcap_cleanup(cap_session->capture_opts);
+#endif
capture_input_closed(cap_session, primary_msg);
g_free(primary_msg);
return FALSE;
@@ -2047,7 +2087,6 @@ sync_pipe_stop(capture_session *cap_session)
DWORD childstatus;
gboolean terminate = TRUE;
#endif
-
if (cap_session->fork_child != -1) {
#ifndef _WIN32
/* send the SIGINT signal to close the capture child gracefully. */
@@ -2116,6 +2155,7 @@ sync_pipe_kill(int fork_child)
* And this also will require to have the process id.
*/
TerminateProcess((HANDLE) (fork_child), 0);
+
#endif
}
}
diff --git a/capture_opts.c b/capture_opts.c
index 313471f1b9..9d5620c0f2 100644
--- a/capture_opts.c
+++ b/capture_opts.c
@@ -66,6 +66,12 @@ capture_opts_init(capture_options *capture_opts)
capture_opts->default_options.linktype = -1; /* use interface default */
capture_opts->default_options.promisc_mode = TRUE;
capture_opts->default_options.if_type = IF_WIRED;
+#ifdef HAVE_EXTCAP
+ capture_opts->default_options.extcap = NULL;
+ capture_opts->default_options.extcap_fifo = NULL;
+ capture_opts->default_options.extcap_args = NULL;
+ capture_opts->default_options.extcap_pid = (GPid)-1;
+#endif
#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
capture_opts->default_options.buffer_size = DEFAULT_CAPTURE_BUFFER_SIZE;
#endif
@@ -138,6 +144,11 @@ capture_opts_log(const char *log_domain, GLogLevelFlags log_level, capture_optio
g_log(log_domain, log_level, "Snap length[%02d] (%u) : %d", i, interface_opts.has_snaplen, interface_opts.snaplen);
g_log(log_domain, log_level, "Link Type[%02d] : %d", i, interface_opts.linktype);
g_log(log_domain, log_level, "Promiscuous Mode[%02d]: %s", i, interface_opts.promisc_mode?"TRUE":"FALSE");
+#ifdef HAVE_EXTCAP
+ g_log(log_domain, log_level, "Extcap[%02d] : %s", i, interface_opts.extcap ? interface_opts.extcap : "(unspecified)");
+ g_log(log_domain, log_level, "Extcap FIFO[%02d] : %s", i, interface_opts.extcap_fifo ? interface_opts.extcap_fifo : "(unspecified)");
+ g_log(log_domain, log_level, "Extcap PID[%02d] : %d", i, interface_opts.extcap_pid);
+#endif
#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
g_log(log_domain, log_level, "Buffer size[%02d] : %d (MB)", i, interface_opts.buffer_size);
#endif
@@ -174,6 +185,10 @@ capture_opts_log(const char *log_domain, GLogLevelFlags log_level, capture_optio
g_log(log_domain, log_level, "Snap length[df] (%u) : %d", capture_opts->default_options.has_snaplen, capture_opts->default_options.snaplen);
g_log(log_domain, log_level, "Link Type[df] : %d", capture_opts->default_options.linktype);
g_log(log_domain, log_level, "Promiscuous Mode[df]: %s", capture_opts->default_options.promisc_mode?"TRUE":"FALSE");
+#ifdef HAVE_EXTCAP
+ g_log(log_domain, log_level, "Extcap[df] : %s", capture_opts->default_options.extcap ? capture_opts->default_options.extcap : "(unspecified)");
+ g_log(log_domain, log_level, "Extcap FIFO[df] : %s", capture_opts->default_options.extcap_fifo ? capture_opts->default_options.extcap_fifo : "(unspecified)");
+#endif
#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
g_log(log_domain, log_level, "Buffer size[df] : %d (MB)", capture_opts->default_options.buffer_size);
#endif
@@ -591,6 +606,12 @@ capture_opts_add_iface_opt(capture_options *capture_opts, const char *optarg_str
interface_opts.linktype = capture_opts->default_options.linktype;
interface_opts.promisc_mode = capture_opts->default_options.promisc_mode;
interface_opts.if_type = capture_opts->default_options.if_type;
+#ifdef HAVE_EXTCAP
+ interface_opts.extcap = g_strdup(capture_opts->default_options.extcap);
+ interface_opts.extcap_fifo = g_strdup(capture_opts->default_options.extcap_fifo);
+ interface_opts.extcap_args = NULL;
+ interface_opts.extcap_pid = (GPid)-1;
+#endif
#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
interface_opts.buffer_size = capture_opts->default_options.buffer_size;
#endif
@@ -1020,6 +1041,14 @@ capture_opts_del_iface(capture_options *capture_opts, guint if_index)
if (interface_opts.console_display_name != NULL)
g_free(interface_opts.console_display_name);
g_free(interface_opts.cfilter);
+#ifdef HAVE_EXTCAP
+ g_free(interface_opts.extcap);
+ g_free(interface_opts.extcap_fifo);
+ if (interface_opts.extcap_args)
+ g_hash_table_unref(interface_opts.extcap_args);
+ if (interface_opts.extcap_pid > 0)
+ g_spawn_close_pid(interface_opts.extcap_pid);
+#endif
#ifdef HAVE_PCAP_REMOTE
if (interface_opts.src_type == CAPTURE_IFREMOTE) {
g_free(interface_opts.remote_host);
@@ -1061,6 +1090,13 @@ collect_ifaces(capture_options *capture_opts)
interface_opts.has_snaplen = device.has_snaplen;
interface_opts.promisc_mode = device.pmode;
interface_opts.if_type = device.if_info.type;
+#ifdef HAVE_EXTCAP
+ interface_opts.extcap = g_strdup(device.if_info.extcap);
+ interface_opts.extcap_fifo = NULL;
+ interface_opts.extcap_args = device.external_cap_args_settings;
+ interface_opts.extcap_pid = (GPid)-1;
+ g_hash_table_ref(interface_opts.extcap_args);
+#endif
#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
interface_opts.buffer_size = device.buffer;
#endif
diff --git a/capture_opts.h b/capture_opts.h
index 2097e2ab4d..9410db280c 100644
--- a/capture_opts.h
+++ b/capture_opts.h
@@ -195,6 +195,10 @@ typedef struct interface_tag {
gboolean selected;
gboolean hidden;
gboolean locked;
+#ifdef HAVE_EXTCAP
+ /* External capture cached data */
+ GHashTable *external_cap_args_settings;
+#endif
} interface_t;
typedef struct link_row_tag {
@@ -212,6 +216,12 @@ typedef struct interface_options_tag {
int linktype;
gboolean promisc_mode;
interface_type if_type;
+#ifdef HAVE_EXTCAP
+ gchar *extcap;
+ gchar *extcap_fifo;
+ GHashTable *extcap_args;
+ GPid extcap_pid;
+#endif
#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
int buffer_size;
#endif
diff --git a/caputils/capture-pcap-util.c b/caputils/capture-pcap-util.c
index 98054c4a0f..8755fcefaf 100644
--- a/caputils/capture-pcap-util.c
+++ b/caputils/capture-pcap-util.c
@@ -246,6 +246,9 @@ if_info_new(const char *name, const char *description, gboolean loopback)
if_info->friendly_name = NULL; /* default - unknown */
if_info->vendor_description = NULL;
if_info->type = IF_WIRED; /* default */
+#ifdef HAVE_EXTCAP
+ if_info->extcap = g_strdup("");
+#endif
#ifdef _WIN32
/*
* Get the interface type.
@@ -506,6 +509,9 @@ free_if_cb(gpointer data, gpointer user_data _U_)
g_free(if_info->name);
g_free(if_info->friendly_name);
g_free(if_info->vendor_description);
+#ifdef HAVE_EXTCAP
+ g_free(if_info->extcap);
+#endif
g_slist_foreach(if_info->addrs, free_if_info_addr_cb, NULL);
g_slist_free(if_info->addrs);
diff --git a/caputils/capture_ifinfo.h b/caputils/capture_ifinfo.h
index 2353f06fed..4293b816b1 100644
--- a/caputils/capture_ifinfo.h
+++ b/caputils/capture_ifinfo.h
@@ -38,6 +38,9 @@ typedef enum {
IF_WIRELESS,
IF_DIALUP,
IF_USB,
+#ifdef HAVE_EXTCAP
+ IF_EXTCAP,
+#endif
IF_VIRTUAL
} interface_type;
@@ -56,6 +59,9 @@ typedef struct {
GSList *addrs; /* containing address values of if_addr_t */
interface_type type; /* type of interface */
gboolean loopback; /* TRUE if loopback, FALSE otherwise */
+#ifdef HAVE_EXTCAP
+ char *extcap; /* extcap arguments, which present the data to call the extcap interface */
+#endif
} if_info_t;
/*
diff --git a/cmakeconfig.h.in b/cmakeconfig.h.in
index 6c4df84143..7d0f7dc252 100644
--- a/cmakeconfig.h.in
+++ b/cmakeconfig.h.in
@@ -18,6 +18,12 @@
/* Build wsutil with SIMD optimization */
#cmakedefine HAVE_SSE4_2 1
+/* Directory where extcap hooks reside */
+#define EXTCAP_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/${CPACK_PACKAGE_NAME}/extcap/"
+
+/* Define to 1 if we want to enable extcap */
+#cmakedefine HAVE_EXTCAP 1
+
/* Define to 1 if we want to enable plugins */
#cmakedefine HAVE_PLUGINS 1
diff --git a/config.h.win32 b/config.h.win32
index b19b7dc7b6..a5cdf7435d 100644
--- a/config.h.win32
+++ b/config.h.win32
@@ -39,6 +39,7 @@
#define YYTEXT_POINTER 1
#define HAVE_PLUGINS 1
+#define HAVE_EXTCAP 1
/* #undef HAVE_SA_LEN */
diff --git a/configure.ac b/configure.ac
index 044728202e..ff9c4f7fc5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2699,6 +2699,32 @@ CPPFLAGS="$CPPFLAGS '-DPLUGIN_INSTALL_DIR=\"\$(plugindir)\"'"
PLUGIN_LIBS=""
AC_SUBST(PLUGIN_LIBS)
+
+dnl Use extcap by default
+extcapdir='${datadir}/wireshark/extcap/'
+AC_ARG_WITH(extcap,
+ AC_HELP_STRING( [--with-extcap@<:@=DIR@:>@],
+ [use extcap for external capture sources (installed in DIR, if supplied) @<:@default=yes, if possible@:>@]),
+[
+ if test "x$withval" = "xno"; then
+ have_extcap=no
+ elif test "x$withval" = "xyes"; then
+ have_extcap=yes
+ elif test "x$withval" != "xyes"; then
+ have_extcap=yes
+ extcapdir ="$withval"
+ fi
+],[
+ have_extcap=yes
+])
+AM_CONDITIONAL(HAVE_EXTCAP, test "x$have_extcap" = "xyes")
+if test "x$have_extcap" = "xyes"
+then
+ AC_DEFINE(HAVE_EXTCAP, 1, [Define if external capture sources should be enabled])
+ AC_DEFINE_UNQUOTED(EXTCAP_DIR,"$extcapdir", [Directory for extcap plugins])
+fi
+AC_SUBST(extcapdir)
+
#
# Check if (emem) memory allocations must be 8-byte aligned.
# I haven't been able to write C code that reliably makes that determination
@@ -3079,6 +3105,7 @@ echo " Install dumpcap with capabilities : $setcap_message"
echo " Install dumpcap setuid : $setuid_message"
echo " Use dumpcap group : $dumpcap_group_message"
echo " Use plugins : $have_plugins"
+echo " Use external capture sources : $have_extcap"
echo " Use Lua library : $lua_message"
echo " Build rtp_player : $portaudio_message"
echo " Build profile binaries : $enable_profile_build"
diff --git a/doc/extcap.pod b/doc/extcap.pod
new file mode 100644
index 0000000000..c6cc9bb1bc
--- /dev/null
+++ b/doc/extcap.pod
@@ -0,0 +1,78 @@
+
+=head1 NAME
+
+extcap - Extcap grammar elements
+
+=head1 SYNOPSIS
+
+Suggested config grammar elements:
+arg (options) argument for CLI calling
+number Reference # of argument for other values, display order
+call Literal argument to call (--call=...)
+display Displayed name
+default Default value, in proper form for type
+range Range of valid values for UI checking (min,max) in proper form
+type Argument type for UI filtering for raw, or UI type for selector:
+ integer
+ unsigned
+ long (may include scientific / special notation)
+float
+menu (display popup menu in UI)
+selector (display selector table, all values as strings)
+boolean (display checkbox)
+radio (display group of radio buttons with provided values, all values as strings)
+
+value (options) Values for argument selection
+ arg Argument # this value applies to
+value Passed value
+display Displayed value
+default Boolean (true if default, all others ignored, ie default=true)
+
+flag (options) external-capture level flags
+ dedicated Bypass dumpcap & mux for high speed
+ failure Failure message
+
+
+Possible grammar example:
+
+arg {number=0}{call=channel}{display=Wi-Fi Channel}{type=integer}
+arg {number=1}{call=chanflags}{display=Channel Flags}{type=radio}
+arg {number=2}{call=interface}{display=Interface}{type=selector}
+value {arg=0}{range=1,11}
+value {arg=1}{value=ht40p}{display=HT40+}
+value {arg=1}{value=ht40m}{display=HT40-}
+value {arg=1}{value=ht20}{display=HT20}
+value {arg=2}{value=wlan0}{display=wlan0}
+
+Example 2
+arg {number=0}{call=usbdevice}{USB Device}{type=selector}
+value {arg=0}{call=/dev/sysfs/usb/foo/123}{display=Ubertooth One sn 1234}
+value {arg=0}{call=”/dev/sysfs/usb/foo/456}{display=Ubertooth One sn 8901}
+
+Example 3
+arg {number=0}{call=usbdevice}{USB Device}{type=selector}
+flag {failure=Permission denied opening Ubertooth device}
+
+
+Security awareness:
+
+- Users running wireshark as root, we can’t save you
+- Dumpcap retains suid/setgid and group+x permissions to allow users in wireshark group only
+- Third-party capture programs run w/ whatever privs they’re installed with
+- If an attacker can write to a system binary directory, we’re game over anyhow
+- Don’t let wireshark be told to look for capture binaries somewhere else?
+
+Notes:
+- daemonized dumpcap?
+- multiuser?
+- sync_pipe.h commands
+- expand pipe commands to have status notifications, etc?
+- Wireshark->dumpcap options for channel control, etc?
+
+TODO
+define grammar
+write grammar to HTML mockup
+sketch interface with dumpcap
+launch external-pcap from wireshark, bypass dumpcap
+launch external-pcap from wireshark, hand fd to dumpcap
+extract netif capture as first cap source \ No newline at end of file
diff --git a/doc/extcap_example.py b/doc/extcap_example.py
new file mode 100755
index 0000000000..02524bc9e4
--- /dev/null
+++ b/doc/extcap_example.py
@@ -0,0 +1,249 @@
+#!/usr/bin/env python
+
+"""
+This is a generic example, which produces pcap packages every n seconds, and
+is configurable via extcap options.
+
+@note
+{
+To use this script on Windows, please generate an extcap_example.bat inside
+the extcap folder, with the following content:
+
+-------
+@echo off
+<Path to python interpreter> <Path to script file> $*
+-------
+
+Windows is not able to execute Python scripts directly, which also goes for all
+other script-based formates beside VBScript
+}
+
+"""
+
+import os
+import sys
+import signal
+import re
+import argparse
+import time
+import struct
+import binascii
+from threading import Thread
+
+ERROR_USAGE = 0
+ERROR_ARG = 1
+ERROR_INTERFACE = 2
+ERROR_FIFO = 3
+
+doExit = False
+globalinterface = 0
+
+def signalHandler(signal, frame):
+ global doExit
+ doExit = True
+
+#### EXTCAP FUNCTIONALITY
+
+"""@brief Extcap configuration
+This method prints the extcap configuration, which will be picked up by the
+interface in Wireshark to present a interface specific configuration for
+this extcap plugin
+"""
+def extcap_config(interface):
+ args = []
+ values = []
+
+ args.append ( (0, '--delay', 'Time delay', 'Time delay between packages', 'integer', '{range=1,15}') )
+ args.append ( (1, '--message', 'Message', 'Package message content', 'string', '') )
+ args.append ( (2, '--verify', 'Verify', 'Verify package content', 'boolflag', '') )
+ args.append ( (3, '--remote', 'Remote Channel', 'Remote Channel Selector', 'selector', ''))
+
+ values.append ( (3, "if1", "Remote1", "true" ) )
+ values.append ( (3, "if2", "Remote2", "false" ) )
+
+ for arg in args:
+ print ("arg {number=%d}{call=%s}{display=%s}{tooltip=%s}{type=%s}%s" % arg)
+
+ for value in values:
+ print ("value {arg=%d}{value=%s}{display=%s}{default=%s}" % value)
+
+
+def extcap_interfaces():
+ print ("interface {value=example1}{display=Example interface usage for extcap}")
+
+def extcap_dlts(interface):
+ if ( interface == 'example1' ):
+ print ("dlt {number=147}{name=USER0}{display=Demo Implementation for Extcap}")
+
+"""
+
+### FAKE DATA GENERATOR
+
+Extcap capture routine
+ This routine simulates a capture by any kind of user defined device. The parameters
+ are user specified and must be handled by the extcap.
+
+ The data captured inside this routine is fake, so change this routine to present
+ your own input data, or call your own capture program via Popen for example. See
+
+ for more details.
+
+"""
+def unsigned(n):
+ return int(n) & 0xFFFFFFFF
+
+def append_bytes(ba, blist):
+ for c in range(0, len(blist)):
+ ba.append(blist[c])
+ return ba
+
+def pcap_fake_header():
+
+ header = bytearray()
+ header = append_bytes(header, struct.pack('<L', int ('a1b2c3d4', 16) ))
+ header = append_bytes(header, struct.pack('<H', unsigned(2)) ) # Pcap Major Version
+ header = append_bytes(header, struct.pack('<H', unsigned(4)) ) # Pcap Minor Version
+ header = append_bytes(header, struct.pack('<I', int(0))) # Timezone
+ header = append_bytes(header, struct.pack('<I', int(0))) # Accurancy of timestamps
+ header = append_bytes(header, struct.pack('<L', int ('0000ffff', 16) )) # Max Length of capture frame
+ header = append_bytes(header, struct.pack('<L', unsigned(1))) # Ethernet
+ return header
+
+# Calculates and returns the IP checksum based on the given IP Header
+def ip_checksum(iph):
+ #split into bytes
+ words = splitN(''.join(iph.split()),4)
+ csum = 0;
+ for word in words:
+ csum += int(word, base=16)
+ csum += (csum >> 16)
+ csum = csum & 0xFFFF ^ 0xFFFF
+ return csum
+
+def pcap_fake_package ( message ):
+
+ pcap = bytearray()
+ #length = 14 bytes [ eth ] + 20 bytes [ ip ] + messagelength
+
+ caplength = len(message) + 14 + 20
+ timestamp = int(time.time())
+
+ pcap = append_bytes(pcap, struct.pack('<L', unsigned(timestamp) ) ) # timestamp seconds
+ pcap = append_bytes(pcap, struct.pack('<L', 0x00 ) ) # timestamp nanoseconds
+ pcap = append_bytes(pcap, struct.pack('<L', unsigned(caplength) ) ) # length captured
+ pcap = append_bytes(pcap, struct.pack('<L', unsigned(caplength) ) ) # length in frame
+
+# ETH
+ pcap = append_bytes(pcap, struct.pack('h', 0 )) # source mac
+ pcap = append_bytes(pcap, struct.pack('h', 0 )) # source mac
+ pcap = append_bytes(pcap, struct.pack('h', 0 )) # source mac
+ pcap = append_bytes(pcap, struct.pack('h', 0 )) # dest mac
+ pcap = append_bytes(pcap, struct.pack('h', 0 )) # dest mac
+ pcap = append_bytes(pcap, struct.pack('h', 0 )) # dest mac
+ pcap = append_bytes(pcap, struct.pack('<h', unsigned(8) )) # protocol (ip)
+
+# IP
+ pcap = append_bytes(pcap, struct.pack('b', int ( '45', 16) )) # IP version
+ pcap = append_bytes(pcap, struct.pack('b', int ( '0', 16) )) #
+ pcap = append_bytes(pcap, struct.pack('>H', unsigned(len(message)+20) )) # length of data + payload
+ pcap = append_bytes(pcap, struct.pack('<H', int ( '0', 16) )) # Identification
+ pcap = append_bytes(pcap, struct.pack('b', int ( '40', 16) )) # Don't fragment
+ pcap = append_bytes(pcap, struct.pack('b', int ( '0', 16) )) # Fragment Offset
+ pcap = append_bytes(pcap, struct.pack('b', int ( '40', 16) ))
+ pcap = append_bytes(pcap, struct.pack('B', 0xFE )) # Protocol (2 = unspecified)
+ pcap = append_bytes(pcap, struct.pack('<H', int ( '0000', 16) )) # Checksum
+ pcap = append_bytes(pcap, struct.pack('>L', int ( '7F000001', 16) )) # Source IP
+ pcap = append_bytes(pcap, struct.pack('>L', int ( '7F000001', 16) )) # Dest IP
+
+ pcap = append_bytes(pcap, message)
+ return pcap
+
+def extcap_capture(interface, fifo, delay, verify, message, remote):
+ global doExit
+
+ signal.signal(signal.SIGINT, signalHandler)
+ signal.signal(signal.SIGTERM , signalHandler)
+
+ tdelay = delay if delay != 0 else 5
+
+ try:
+ os.stat(fifo)
+ except OSError:
+ doExit = True
+ print ( "Fifo does not exist, exiting!" )
+
+ fh = open(fifo, 'w+b', 0 )
+ fh.write (pcap_fake_header())
+
+ while doExit == False:
+ out = str( "%s|%04X%s|%s" % ( remote.strip(), len(message), message, verify ) )
+ try:
+ fh.write (pcap_fake_package(out))
+ time.sleep(tdelay)
+ except IOError:
+ doExit = True
+
+ fh.close()
+
+####
+
+def usage():
+ print ( "Usage: %s <--extcap-interfaces | --extcap-dlts | --extcap-interface | --extcap-config | --capture | --fifo>" % sys.argv[0] )
+
+if __name__ == '__main__':
+ interface = ""
+
+ # Capture options
+ delay = 0
+ message = ""
+
+ parser = argparse.ArgumentParser(
+ prog="Extcap Example",
+ description="Extcap example program for python"
+ )
+
+ # Extcap Arguments
+ parser.add_argument("--capture", help="Start the capture routine", action="store_true" )
+ parser.add_argument("--extcap-interfaces", help="Provide a list of interfaces to capture from", action="store_true")
+ parser.add_argument("--extcap-interface", help="Provide the interface to capture from")
+ parser.add_argument("--extcap-dlts", help="Provide a list of dlts for the given interface", action="store_true")
+ parser.add_argument("--extcap-config", help="Provide a list of configurations for the given interface", action="store_true")
+ parser.add_argument("--fifo", 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" )
+ parser.add_argument("--delay", help="Demonstrates an integer variable", type=int, default=0, choices=[0, 1, 2, 3, 4, 5] )
+ parser.add_argument("--remote", help="Demonstrates a selector choice", default="if1", choices=["if1", "if2"] )
+ parser.add_argument("--message", help="Demonstrates string variable", nargs='?', default="" )
+
+ args = parser.parse_args()
+ if ( len(sys.argv) <= 1 ):
+ parser.exit("No arguments given!")
+
+ if ( args.extcap_interfaces == False and args.extcap_interface == None ):
+ parser.exit("An interface must be provided or the selection must be displayed")
+
+ if ( args.extcap_interfaces == True or args.extcap_interface == None ):
+ extcap_interfaces()
+ sys.exit(0)
+
+ m = re.match ( 'example(\d+)', args.extcap_interface )
+ if not m:
+ sys.exit(ERROR_INTERFACE)
+ interface = m.group(1)
+
+ message = args.message
+ if ( args.message == None or len(args.message) == 0 ):
+ message = "Extcap Test"
+
+ if args.extcap_config:
+ extcap_config(interface)
+ elif args.extcap_dlts:
+ extcap_dlts(interface)
+ elif args.capture:
+ if args.fifo is None:
+ sys.exit(ERROR_FIFO)
+ extcap_capture(interface, args.fifo, args.delay, args.verify, message, args.remote)
+ else:
+ usage()
+ sys.exit(ERROR_USAGE)
diff --git a/dumpcap.c b/dumpcap.c
index 94a8a720dc..7bb7dd994f 100644
--- a/dumpcap.c
+++ b/dumpcap.c
@@ -193,7 +193,7 @@ enable_kernel_bpf_jit_compiler(void)
ssize_t written _U_;
static const char file[] = "/proc/sys/net/core/bpf_jit_enable";
- fd = open(file, O_WRONLY);
+ fd = ws_open(file, O_WRONLY);
if (fd < 0)
return;
@@ -1373,7 +1373,9 @@ print_machine_readable_interfaces(GList *if_list)
printf("\tloopback");
else
printf("\tnetwork");
-
+#ifdef HAVE_EXTCAP
+ printf("\t%s", if_info->extcap);
+#endif
printf("\n");
}
}
@@ -1864,14 +1866,14 @@ cap_open_socket(char *pipename, pcap_options *pcap_opts, char *errmsg, int errms
goto fail_invalid;
}
- strncpy(buf, sockname, len);
+ g_snprintf ( buf,(gulong)len + 1, "%s", sockname );
buf[len] = '\0';
if (inet_pton(AF_INET, buf, &sa.sin_addr) <= 0) {
goto fail_invalid;
}
sa.sin_family = AF_INET;
- sa.sin_port = htons((u_short)port);
+ sa.sin_port = g_htons((u_short)port);
if (((fd = (int)socket(AF_INET, SOCK_STREAM, 0)) < 0) ||
(connect(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)) {
@@ -1893,7 +1895,7 @@ cap_open_socket(char *pipename, pcap_options *pcap_opts, char *errmsg, int errms
if (errorText)
LocalFree(errorText);
#else
- " %d: %s", errno, strerror(errno));
+ " %d: %s", errno, g_strerror(errno));
#endif
pcap_opts->cap_pipe_err = PIPERR;
@@ -1947,12 +1949,12 @@ cap_pipe_open_live(char *pipename,
#else /* _WIN32 */
char *pncopy, *pos;
wchar_t *err_str;
+ interface_options interface_opts;
#endif
ssize_t b;
int fd = -1, sel_ret;
size_t bytes_read;
guint32 magic = 0;
-
pcap_opts->cap_pipe_fd = -1;
#ifdef _WIN32
pcap_opts->cap_pipe_h = INVALID_HANDLE_VALUE;
@@ -2083,10 +2085,16 @@ cap_pipe_open_live(char *pipename,
return;
}
+ interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, 0);
+
/* Wait for the pipe to appear */
while (1) {
- pcap_opts->cap_pipe_h = CreateFile(utf_8to16(pipename), GENERIC_READ, 0, NULL,
- OPEN_EXISTING, 0, NULL);
+
+ if(strncmp(interface_opts.name,"\\\\.\\pipe\\",9)== 0)
+ pcap_opts->cap_pipe_h = GetStdHandle(STD_INPUT_HANDLE);
+ else
+ pcap_opts->cap_pipe_h = CreateFile(utf_8to16(pipename), GENERIC_READ, 0, NULL,
+ OPEN_EXISTING, 0, NULL);
if (pcap_opts->cap_pipe_h != INVALID_HANDLE_VALUE)
break;
@@ -2105,7 +2113,7 @@ cap_pipe_open_live(char *pipename,
if (!WaitNamedPipe(utf_8to16(pipename), 30 * 1000)) {
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL);
+ NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL);
g_snprintf(errmsg, errmsgl,
"The capture session on \"%s\" timed out during "
"pipe open: %s (error %d)",
@@ -4526,7 +4534,6 @@ main(int argc, char *argv[])
/* Set the initial values in the capture options. This might be overwritten
by the command line parameters. */
capture_opts_init(&global_capture_opts);
-
/* We always save to a file - if no file was specified, we save to a
temporary file. */
global_capture_opts.saving_to_file = TRUE;
@@ -4857,6 +4864,7 @@ main(int argc, char *argv[])
interface_options interface_opts;
interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, ii);
+
caps = get_if_capabilities(interface_opts.name,
interface_opts.monitor_mode, &err_str);
if (caps == NULL) {
@@ -4900,7 +4908,6 @@ main(int argc, char *argv[])
fflush(stderr);
/* Now start the capture. */
-
if (capture_loop_start(&global_capture_opts, &stats_known, &stats) == TRUE) {
/* capture ok */
exit_main(0);
diff --git a/extcap.c b/extcap.c
new file mode 100644
index 0000000000..a676fc7233
--- /dev/null
+++ b/extcap.c
@@ -0,0 +1,654 @@
+/* extcap.h
+ *
+ * Routines for extcap external capture
+ * Copyright 2013, Mike Ryan <mikeryan@lacklustre.net>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef WIN32
+#include <windows.h>
+#include <process.h>
+#include <time.h>
+#else
+/* Include for unlink */
+#include <unistd.h>
+#endif
+
+#include <glib.h>
+#include <log.h>
+
+#include <wsutil/file_util.h>
+#include <wsutil/filesystem.h>
+#include <wsutil/tempfile.h>
+
+#include "capture_opts.h"
+
+#ifdef HAVE_EXTCAP
+
+#include "extcap.h"
+#include "extcap_parser.h"
+
+#ifdef _WIN32
+static HANDLE pipe_h = NULL;
+#endif
+
+/* internal container, for all the extcap interfaces that have been found.
+ * will be resetted by every call to extcap_interface_list() and is being
+ * used in extcap_get_if_* as well as extcaps_init_initerfaces to ensure,
+ * that only extcap interfaces are being given to underlying extcap programs
+ */
+static GHashTable *ifaces = NULL;
+
+/* Prefix for the pipe interfaces */
+#define EXTCAP_PIPE_PREFIX "wireshark_extcap"
+
+/* Callback definition for extcap_foreach */
+typedef gboolean (*extcap_cb_t)(const gchar *extcap, gchar *output, void *data,
+ gchar **err_str);
+
+/* #define ARG_DEBUG */
+#if ARG_DEBUG
+static void extcap_debug_arguments ( extcap_arg *arg_iter );
+#endif
+
+static gboolean
+extcap_if_exists(const char *ifname)
+{
+ if ( ifname != NULL )
+ {
+ if ( ifaces != NULL )
+ {
+ if ( g_hash_table_size(ifaces) > 0 )
+ {
+ if ( g_hash_table_lookup(ifaces, (const gchar *)ifname) != NULL )
+ {
+ return TRUE;
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+static gboolean
+extcap_if_exists_for_extcap(const char *ifname, const char *extcap)
+{
+ gchar * entry = NULL;
+
+ if ( extcap_if_exists(ifname) )
+ {
+ if ( ( entry = (gchar *)g_hash_table_lookup(ifaces, (const gchar *)ifname) ) != NULL )
+ {
+ if ( strcmp(entry, extcap) == 0 )
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gchar *
+extcap_if_executable(const char *ifname)
+{
+ if ( extcap_if_exists(ifname) )
+ return (gchar *)g_hash_table_lookup(ifaces, (const gchar *)ifname);
+
+ return (gchar *)NULL;
+}
+
+static void
+extcap_if_cleanup(void)
+{
+ if ( ifaces == NULL )
+ ifaces = g_hash_table_new(g_str_hash, g_str_equal);
+
+ g_hash_table_remove_all(ifaces);
+}
+
+static void
+extcap_if_add(gchar *ifname, gchar *extcap)
+{
+ if ( !g_hash_table_contains(ifaces, ifname) )
+ g_hash_table_insert(ifaces, ifname, extcap);
+}
+
+static void extcap_foreach(gint argc, gchar **args, extcap_cb_t cb,
+ void *cb_data, char **err_str, const char * ifname _U_) {
+ const char *dirname = get_extcap_dir();
+ GDir *dir;
+ const gchar *file;
+ gboolean keep_going;
+ gchar **argv;
+
+ keep_going = TRUE;
+
+ argv = (gchar **) g_malloc0(sizeof(gchar *) * (argc + 2));
+
+ if ((dir = g_dir_open(dirname, 0, NULL)) != NULL) {
+#ifdef WIN32
+ dirname = g_strescape(dirname,NULL);
+#endif
+ while (keep_going && (file = g_dir_read_name(dir)) != NULL ) {
+ GString *extcap_string = NULL;
+ gchar *extcap = NULL;
+ gchar *command_output = NULL;
+ gboolean status = FALSE;
+ gint i;
+ gint exit_status = 0;
+ GError *error = NULL;
+
+ /* full path to extcap binary */
+ extcap_string = g_string_new("");
+#ifdef WIN32
+ g_string_printf(extcap_string, "%s\\\\%s",dirname,file);
+ extcap = g_string_free(extcap_string, FALSE);
+#else
+ g_string_printf(extcap_string, "%s/%s", dirname, file);
+ extcap = g_string_free(extcap_string, FALSE);
+#endif
+ if ( extcap_if_exists(ifname) && !extcap_if_exists_for_extcap(ifname, extcap ) )
+ continue;
+
+ argv[0] = extcap;
+ for (i = 0; i < argc; ++i)
+ argv[i+1] = args[i];
+ argv[argc+1] = NULL;
+
+ status = g_spawn_sync(dirname, argv, NULL,
+ (GSpawnFlags) 0, NULL, NULL,
+ &command_output, NULL, &exit_status, &error);
+
+ if (status && exit_status == 0)
+ keep_going = cb(extcap, command_output, cb_data, err_str);
+
+ g_free(extcap);
+ g_free(command_output);
+ }
+
+ g_dir_close(dir);
+ }
+
+ g_free(argv);
+}
+
+static gboolean dlt_cb(const gchar *extcap _U_, gchar *output, void *data,
+ char **err_str) {
+ extcap_token_sentence *tokens;
+ extcap_dlt *dlts, *dlt_iter, *next;
+ if_capabilities_t *caps;
+ GList *linktype_list = NULL;
+ data_link_info_t *data_link_info;
+
+ tokens = extcap_tokenize_sentences(output);
+ extcap_parse_dlts(tokens, &dlts);
+
+ extcap_free_tokenized_sentence_list(tokens);
+
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Extcap pipe %s ", extcap);
+
+ /*
+ * Allocate the interface capabilities structure.
+ */
+ caps = (if_capabilities_t *) g_malloc(sizeof *caps);
+ caps->can_set_rfmon = FALSE;
+
+ dlt_iter = dlts;
+ while (dlt_iter != NULL ) {
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
+ " DLT %d name=\"%s\" display=\"%s\" ", dlt_iter->number,
+ dlt_iter->name, dlt_iter->display);
+
+ data_link_info = g_new(data_link_info_t, 1);
+ data_link_info->dlt = dlt_iter->number;
+ data_link_info->name = g_strdup(dlt_iter->name);
+ data_link_info->description = g_strdup(dlt_iter->display);
+ linktype_list = g_list_append(linktype_list, data_link_info);
+ dlt_iter = dlt_iter->next_dlt;
+ }
+
+ /* Check to see if we built a list */
+ if (linktype_list != NULL && data != NULL) {
+ caps->data_link_types = linktype_list;
+ *(if_capabilities_t **) data = caps;
+ } else {
+ if (err_str) {
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, " returned no DLTs");
+ *err_str = g_strdup("Extcap returned no DLTs");
+ }
+ g_free(caps);
+ }
+
+ dlt_iter = dlts;
+ while (dlt_iter != NULL ) {
+ next = dlt_iter->next_dlt;
+ extcap_free_dlt(dlt_iter);
+ dlt_iter = next;
+ }
+
+ return FALSE;
+}
+
+if_capabilities_t *
+extcap_get_if_dlts(const gchar *ifname, char **err_str) {
+ gchar *argv[3];
+ gint i;
+ if_capabilities_t *caps = NULL;
+
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, " returned no DLTs");
+
+ if (ifname != NULL && err_str != NULL)
+ *err_str = NULL;
+
+ if ( extcap_if_exists(ifname) )
+ {
+ argv[0] = g_strdup(EXTCAP_ARGUMENT_LIST_DLTS);
+ argv[1] = g_strdup(EXTCAP_ARGUMENT_INTERFACE);
+ argv[2] = g_strdup(ifname);
+
+ if (err_str)
+ *err_str = NULL;
+ extcap_foreach(3, argv, dlt_cb, &caps, err_str, ifname);
+
+ for (i = 0; i < 3; ++i)
+ g_free(argv[i]);
+ }
+
+ return caps;
+}
+
+static gboolean interfaces_cb(const gchar *extcap, gchar *output, void *data,
+ char **err_str _U_) {
+ GList **il = (GList **) data;
+ extcap_token_sentence *tokens;
+ extcap_interface *interfaces, *int_iter; /*, *next; */
+ if_info_t *if_info;
+
+ tokens = extcap_tokenize_sentences(output);
+ extcap_parse_interfaces(tokens, &interfaces);
+
+ extcap_free_tokenized_sentence_list(tokens);
+
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Extcap pipe %s ", extcap);
+
+ int_iter = interfaces;
+ while (int_iter != NULL ) {
+ if ( extcap_if_exists(int_iter->call) )
+ {
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_WARNING, "Extcap interface \"%s\" is already provided by \"%s\" ",
+ int_iter->call, (gchar *)extcap_if_executable(int_iter->call) );
+ int_iter = int_iter->next_interface;
+ continue;
+ }
+
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, " Interface [%s] \"%s\" ",
+ int_iter->call, int_iter->display);
+
+ if_info = g_new0(if_info_t, 1);
+ if_info->name = g_strdup(int_iter->call);
+ if_info->friendly_name = g_strdup(int_iter->display);
+
+ if_info->type = IF_EXTCAP;
+
+ if_info->extcap = g_strdup(extcap);
+ *il = g_list_append(*il, if_info);
+
+ extcap_if_add(g_strdup(int_iter->call), g_strdup(extcap) );
+ int_iter = int_iter->next_interface;
+ }
+
+ return TRUE;
+}
+
+GList *
+extcap_interface_list(char **err_str) {
+ gchar *argv;
+ /* gint i; */
+ GList *ret = NULL;
+
+ if (err_str != NULL)
+ *err_str = NULL;
+
+ extcap_if_cleanup();
+
+ argv = g_strdup(EXTCAP_ARGUMENT_LIST_INTERFACES);
+
+ if (err_str)
+ *err_str = NULL;
+ extcap_foreach(1, &argv, interfaces_cb, &ret, err_str, NULL);
+
+ g_free(argv);
+
+ return ret;
+}
+
+static gboolean search_cb(const gchar *extcap _U_, gchar *output, void *data,
+ char **err_str _U_) {
+ extcap_token_sentence *tokens = NULL;
+ GList *arguments = NULL;
+ GList **il = (GList **) data;
+
+ tokens = extcap_tokenize_sentences(output);
+ arguments = extcap_parse_args(tokens);
+
+ extcap_free_tokenized_sentence_list(tokens);
+
+#if ARG_DEBUG
+ extcap_debug_arguments ( arguments );
+#endif
+
+ *il = g_list_append(*il, arguments);
+
+ /* By returning false, extcap_foreach will break on first found */
+ return TRUE;
+}
+
+GList *
+extcap_get_if_configuration(const char * ifname) {
+ gchar *argv[4];
+ GList *ret = NULL;
+ gchar **err_str = NULL;
+
+ if ( extcap_if_exists(ifname) )
+ {
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "Extcap path %s",
+ get_extcap_dir());
+
+ if (err_str != NULL)
+ *err_str = NULL;
+
+ argv[0] = g_strdup(EXTCAP_ARGUMENT_CONFIG);
+ argv[1] = g_strdup(EXTCAP_ARGUMENT_INTERFACE);
+ argv[2] = g_strdup(ifname);
+ argv[3] = NULL;
+
+ extcap_foreach(4, argv, search_cb, &ret, err_str, ifname);
+ }
+
+ return ret;
+}
+
+void extcap_cleanup(capture_options * capture_opts) {
+ interface_options interface_opts;
+ guint icnt = 0;
+
+ for (icnt = 0; icnt < capture_opts->ifaces->len; icnt++) {
+ interface_opts = g_array_index(capture_opts->ifaces, interface_options,
+ icnt);
+
+ /* skip native interfaces */
+ if (interface_opts.if_type != IF_EXTCAP)
+ continue;
+
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
+ "Extcap [%s] - Cleaning up fifo: %s; PID: %d", interface_opts.name,
+ interface_opts.extcap_fifo, interface_opts.extcap_pid);
+#ifdef WIN32
+ if (pipe_h)
+ {
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
+ "Extcap [%s] - Closing pipe", interface_opts.name);
+ FlushFileBuffers(pipe_h);
+ DisconnectNamedPipe(pipe_h);
+ CloseHandle(pipe_h);
+ }
+#else
+ if (interface_opts.extcap_fifo != NULL && file_exists(interface_opts.extcap_fifo))
+ {
+ /* the fifo will not be freed here, but with the other capture_opts in capture_sync */
+ ws_unlink(interface_opts.extcap_fifo);
+ interface_opts.extcap_fifo = NULL;
+ }
+#endif
+ /* Maybe the client closed and removed fifo, but ws should check if
+ * pid should be closed */
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
+ "Extcap [%s] - Closing spawned PID: %d", interface_opts.name,
+ interface_opts.extcap_pid);
+
+ if (interface_opts.extcap_pid != (GPid)-1 )
+ {
+ g_spawn_close_pid(interface_opts.extcap_pid);
+ interface_opts.extcap_pid = (GPid)-1;
+ }
+ }
+}
+
+static void
+extcap_arg_cb(gpointer key, gpointer value, gpointer data) {
+ GPtrArray *args = (GPtrArray *)data;
+
+ if ( key != NULL )
+ {
+ g_ptr_array_add(args, key);
+
+ if ( value != NULL )
+ g_ptr_array_add(args, value);
+ }
+}
+
+/* call mkfifo for each extcap,
+ * returns FALSE if there's an error creating a FIFO */
+gboolean
+extcaps_init_initerfaces(capture_options *capture_opts)
+{
+ guint i;
+ interface_options interface_opts;
+
+ for (i = 0; i < capture_opts->ifaces->len; i++)
+ {
+ GPtrArray *args = NULL;
+ GPid pid = 0;
+
+ interface_opts = g_array_index(capture_opts->ifaces, interface_options, i);
+
+ /* skip native interfaces */
+ if (interface_opts.if_type != IF_EXTCAP )
+ continue;
+
+ /* create pipe for fifo */
+ if ( ! extcap_create_pipe ( &interface_opts.extcap_fifo ) )
+ return FALSE;
+
+ /* Create extcap call */
+ args = g_ptr_array_new_with_free_func(g_free);
+#define add_arg(X) g_ptr_array_add(args, g_strdup(X))
+
+ add_arg(interface_opts.extcap);
+ add_arg(EXTCAP_ARGUMENT_RUN_CAPTURE);
+ add_arg(EXTCAP_ARGUMENT_INTERFACE);
+ add_arg(interface_opts.name);
+ add_arg(EXTCAP_ARGUMENT_RUN_PIPE);
+ add_arg(interface_opts.extcap_fifo);
+ if (interface_opts.extcap_args != NULL)
+ g_hash_table_foreach(interface_opts.extcap_args, extcap_arg_cb, args);
+ add_arg(NULL);
+#undef add_arg
+
+ /* Wireshark for windows crashes here sometimes *
+ * Access violation reading location 0x... */
+ g_spawn_async(NULL, (gchar **)args->pdata, NULL,
+ (GSpawnFlags) 0, NULL, NULL,
+ &pid,NULL);
+
+ interface_opts.extcap_pid = pid;
+ capture_opts->ifaces = g_array_remove_index(capture_opts->ifaces, i);
+ g_array_insert_val(capture_opts->ifaces, i, interface_opts);
+ }
+
+ return TRUE;
+}
+
+#ifdef WIN32
+/* called by capture_sync to get the CreatNamedPipe handle*/
+HANDLE
+extcap_get_win32_handle()
+{
+ return pipe_h;
+}
+#endif
+
+gboolean extcap_create_pipe(char ** fifo)
+{
+#ifdef WIN32
+ gchar timestr[ 14+1 ];
+ time_t current_time;
+
+ gchar *pipename = NULL;
+
+ LPSECURITY_ATTRIBUTES security = NULL;
+ /* create pipename */
+ current_time = time(NULL);
+ strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S", localtime(&current_time));
+ pipename = g_strconcat ( "\\\\.\\pipe\\", EXTCAP_PIPE_PREFIX, "_", timestr, NULL );
+
+ /* Security struct to enable Inheritable HANDLE */
+ security = (LPSECURITY_ATTRIBUTES)g_malloc0(sizeof(LPSECURITY_ATTRIBUTES));
+ security->nLength = sizeof(LPSECURITY_ATTRIBUTES);
+ security->bInheritHandle = TRUE;
+ security->lpSecurityDescriptor = NULL;
+
+ /* create a namedPipe*/
+ pipe_h = CreateNamedPipe(
+ utf_8to16(pipename),
+ PIPE_ACCESS_DUPLEX,
+ PIPE_TYPE_MESSAGE| PIPE_READMODE_MESSAGE | PIPE_WAIT,
+ 5, 65536, 65536,
+ 300,
+ security);
+
+ if (pipe_h == INVALID_HANDLE_VALUE)
+ {
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,"\nError creating pipe => (%d)", GetLastError());
+ return FALSE;
+ }
+ else
+ {
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,"\nWireshark Created pipe =>(%s)",pipename);
+ *fifo = g_strdup(pipename);
+ }
+#else
+ gchar *temp_name = NULL;
+ int fd = 0;
+
+ if ( ( fd = create_tempfile ( &temp_name, EXTCAP_PIPE_PREFIX ) ) == 0 )
+ return FALSE;
+
+ ws_close(fd);
+
+ g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG,
+ "Extcap - Creating fifo: %s", temp_name);
+
+ if ( file_exists(temp_name) )
+ ws_unlink(temp_name);
+
+ if (mkfifo(temp_name, 0600) == 0)
+ *fifo = g_strdup(temp_name);
+#endif
+
+ return TRUE;
+}
+
+#if ARG_DEBUG
+void extcap_debug_arguments ( extcap_arg *arg_iter )
+{
+ extcap_value *v = NULL;
+ GList *walker = NULL;
+
+ printf("debug - parser dump\n");
+ while (arg_iter != NULL) {
+ printf("ARG %d call=%s display=\"%s\" type=", arg_iter->arg_num, arg_iter->call, arg_iter->display);
+
+ switch (arg_iter->arg_type) {
+ case EXTCAP_ARG_INTEGER:
+ printf("int\n");
+ break;
+ case EXTCAP_ARG_UNSIGNED:
+ printf("unsigned\n");
+ break;
+ case EXTCAP_ARG_LONG:
+ printf("long\n");
+ break;
+ case EXTCAP_ARG_DOUBLE:
+ printf("double\n");
+ break;
+ case EXTCAP_ARG_BOOLEAN:
+ printf("boolean\n");
+ break;
+ case EXTCAP_ARG_MENU:
+ printf("menu\n");
+ break;
+ case EXTCAP_ARG_RADIO:
+ printf("radio\n");
+ break;
+ case EXTCAP_ARG_SELECTOR:
+ printf("selctor\n");
+ break;
+ case EXTCAP_ARG_STRING:
+ printf ( "string\n" );
+ break;
+ case EXTCAP_ARG_MULTICHECK:
+ printf ( "unknown\n" );
+ break;
+ case EXTCAP_ARG_UNKNOWN:
+ printf ( "unknown\n" );
+ break;
+ }
+
+ if (arg_iter->range_start != NULL && arg_iter->range_end != NULL) {
+ printf("\tRange: ");
+ extcap_printf_complex(arg_iter->range_start);
+ printf(" - ");
+ extcap_printf_complex(arg_iter->range_end);
+ printf("\n");
+ }
+
+ for ( walker = g_list_first ( arg_iter->value_list ); walker; walker = walker->next )
+ {
+ v = (extcap_value *)walker->data;
+ if (v->is_default == TRUE)
+ printf("*");
+ printf("\tcall=\"%p\" display=\"%p\"\n", v->call, v->display);
+ printf("\tcall=\"%s\" display=\"%s\"\n", v->call, v->display);
+ }
+
+ arg_iter = arg_iter->next_arg;
+ }
+}
+#endif
+#endif
+
+/*
+ * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=4 noexpandtab:
+ * :indentSize=4:tabSize=4:noTabs=false:
+ */
diff --git a/extcap.h b/extcap.h
new file mode 100644
index 0000000000..64ea855045
--- /dev/null
+++ b/extcap.h
@@ -0,0 +1,90 @@
+/* extcap.h
+ * Definitions for extcap external capture
+ * Copyright 2013, Mike Ryan <mikeryan@lacklustre.net>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __EXTCAP_H__
+#define __EXTCAP_H__
+
+#include "config.h"
+
+#include <glib.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#include <wsutil/unicode-utils.h>
+#endif
+
+#include <ui/capture_ui_utils.h>
+
+#ifdef HAVE_EXTCAP
+
+#define EXTCAP_ARGUMENT_CONFIG "--extcap-config"
+#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_RUN_PIPE "--fifo"
+
+/* try to get if capabilities from extcap */
+if_capabilities_t *
+extcap_get_if_dlts(const gchar * ifname, char ** err_str);
+
+/* get a list of all capture interfaces */
+GList *
+extcap_interface_list(char **err_str);
+
+/* returns the configuration for the given interface name, or an
+ * empty list, if no configuration has been found */
+GList *
+extcap_get_if_configuration(const char * ifname);
+
+#ifdef WIN32
+HANDLE
+extcap_get_win32_handle();
+#endif
+
+gboolean
+extcaps_init_initerfaces(capture_options * capture_opts);
+
+gboolean
+extcap_create_pipe(char ** fifo);
+
+void
+extcap_cleanup(capture_options * capture_opts _U_);
+
+#endif
+
+#endif
+
+/*
+ * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=4 noexpandtab:
+ * :indentSize=4:tabSize=4:noTabs=false:
+ */
diff --git a/extcap_parser.c b/extcap_parser.c
new file mode 100644
index 0000000000..274428f981
--- /dev/null
+++ b/extcap_parser.c
@@ -0,0 +1,881 @@
+/* extcap_parser.c
+ *
+ * Routines for extcap external capture
+ * Copyright 2013, Mike Ryan <mikeryan@lacklustre.net>
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <glib.h>
+#include <string.h>
+
+#include "extcap_parser.h"
+
+void extcap_printf_complex(extcap_complex *comp) {
+ gchar *ret = extcap_get_complex_as_string(comp);
+ printf("%s", ret);
+ g_free(ret);
+}
+
+gchar *extcap_get_complex_as_string(extcap_complex *comp) {
+ /* Pick an arbitrary size that should be big enough */
+ gchar *ret = g_new(gchar, 32);
+
+ if (comp == NULL) {
+ g_snprintf(ret, 32, "(null)");
+ return ret;
+ }
+
+ switch (comp->complex_type) {
+ case EXTCAP_ARG_INTEGER:
+ g_snprintf(ret, 32, "%d", comp->complex_value.int_value);
+ break;
+ case EXTCAP_ARG_UNSIGNED:
+ g_snprintf(ret, 32, "%u", comp->complex_value.uint_value);
+ break;
+ case EXTCAP_ARG_LONG:
+ g_snprintf(ret, 32, "%ld", comp->complex_value.long_value);
+ break;
+ case EXTCAP_ARG_DOUBLE:
+ g_snprintf(ret, 32, "%f", comp->complex_value.double_value);
+ break;
+ case EXTCAP_ARG_BOOLEAN:
+ g_snprintf(ret, 32, "%s",
+ comp->complex_value.bool_value ? "TRUE" : "FALSE");
+ break;
+ case EXTCAP_ARG_STRING:
+ case EXTCAP_ARG_FILESELECT:
+ g_free(ret);
+ ret = g_strdup(comp->complex_value.string_value);
+ break;
+ default:
+ /* Nulling out the return string */
+ g_snprintf(ret, 32, " ");
+ break;
+ }
+
+ return ret;
+}
+
+extcap_complex *extcap_parse_complex(extcap_arg_type complex_type,
+ const gchar *data) {
+ extcap_complex *rc = g_new(extcap_complex, 1);
+ gboolean success = FALSE;
+ long double exp_f;
+
+ switch (complex_type) {
+ case EXTCAP_ARG_INTEGER:
+ if (sscanf(data, "%Lf", &exp_f) == 1) {
+ rc->complex_value.int_value = (int) exp_f;
+ success = TRUE;
+ break;
+ }
+ break;
+ case EXTCAP_ARG_UNSIGNED:
+ if (sscanf(data, "%Lf", &exp_f) == 1) {
+ rc->complex_value.uint_value = (unsigned int) exp_f;
+ success = TRUE;
+ break;
+ }
+ break;
+ case EXTCAP_ARG_LONG:
+ if (sscanf(data, "%Lf", &exp_f) == 1) {
+ rc->complex_value.long_value = (long) exp_f;
+ success = TRUE;
+ break;
+ }
+ break;
+ case EXTCAP_ARG_DOUBLE:
+ if (sscanf(data, "%Lf", &exp_f) == 1) {
+ rc->complex_value.double_value = (double) exp_f;
+ success = TRUE;
+ break;
+ }
+ break;
+ case EXTCAP_ARG_BOOLEAN:
+ case EXTCAP_ARG_BOOLFLAG:
+ if (data[0] == 't' || data[0] == 'T' || data[0] == '1') {
+ rc->complex_value.bool_value = 1;
+ } else {
+ rc->complex_value.bool_value = 0;
+ }
+ success = TRUE;
+ break;
+ case EXTCAP_ARG_STRING:
+ case EXTCAP_ARG_FILESELECT:
+ rc->complex_value.string_value = g_strdup(data);
+ success = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ if (!success) {
+ g_free(rc);
+ return NULL ;
+ }
+
+ rc->complex_type = complex_type;
+ rc->value_filled = TRUE;
+
+ return rc;
+}
+
+gboolean extcap_compare_is_default(extcap_arg *element, extcap_complex *test) {
+ gboolean result = FALSE;
+
+ if (element->default_complex == NULL)
+ return result;
+
+ switch (element->arg_type) {
+ case EXTCAP_ARG_INTEGER:
+ if (extcap_complex_get_int(test)
+ == extcap_complex_get_int(element->default_complex))
+ result = TRUE;
+ break;
+ case EXTCAP_ARG_UNSIGNED:
+ if (extcap_complex_get_uint(test)
+ == extcap_complex_get_uint(element->default_complex))
+ result = TRUE;
+ break;
+ case EXTCAP_ARG_LONG:
+ if (extcap_complex_get_long(test)
+ == extcap_complex_get_long(element->default_complex))
+ result = TRUE;
+ break;
+ case EXTCAP_ARG_DOUBLE:
+ if (extcap_complex_get_double(test)
+ == extcap_complex_get_double(element->default_complex))
+ result = TRUE;
+ break;
+ case EXTCAP_ARG_BOOLEAN:
+ case EXTCAP_ARG_BOOLFLAG:
+ if (extcap_complex_get_bool(test)
+ == extcap_complex_get_bool(element->default_complex))
+ result = TRUE;
+ break;
+ case EXTCAP_ARG_STRING:
+ if (strcmp(extcap_complex_get_string(test),
+ extcap_complex_get_string(element->default_complex)) == 0)
+ result = TRUE;
+ break;
+
+ default:
+ break;
+ }
+
+ return result;
+}
+
+void extcap_free_complex(extcap_complex *comp) {
+ if (comp->complex_type == EXTCAP_ARG_STRING
+ || comp->complex_type == EXTCAP_ARG_FILESELECT)
+ g_free(comp->complex_value.string_value);
+
+ g_free(comp);
+}
+
+int extcap_complex_get_int(extcap_complex *comp) {
+ if ( comp == NULL )
+ return (int)0;
+ return comp->complex_value.int_value;
+}
+
+unsigned int extcap_complex_get_uint(extcap_complex *comp) {
+ if ( comp == NULL )
+ return (unsigned int)0;
+ return comp->complex_value.uint_value;
+}
+
+long extcap_complex_get_long(extcap_complex *comp) {
+ if ( comp == NULL )
+ return (long)0;
+ return comp->complex_value.long_value;
+}
+
+double extcap_complex_get_double(extcap_complex *comp) {
+ if ( comp == NULL )
+ return (double)0;
+ return comp->complex_value.double_value;
+}
+
+gboolean extcap_complex_get_bool(extcap_complex *comp) {
+ if ( comp == NULL )
+ return FALSE;
+ return comp->complex_value.bool_value;
+}
+
+gchar *extcap_complex_get_string(extcap_complex *comp) {
+ return comp->complex_value.string_value;
+}
+
+void extcap_free_tokenized_param(extcap_token_param *v) {
+ if (v == NULL)
+ return;
+
+ if (v->arg != NULL)
+ g_free(v->arg);
+
+ if (v->value != NULL)
+ g_free(v->value);
+
+ g_free(v);
+}
+
+void extcap_free_tokenized_sentence(extcap_token_sentence *s) {
+ extcap_token_param *tv;
+
+ if (s == NULL)
+ return;
+
+ if (s->sentence != NULL)
+ g_free(s->sentence);
+
+ while (s->param_list != NULL ) {
+ tv = s->param_list;
+ s->param_list = tv->next_token;
+
+ extcap_free_tokenized_param(tv);
+ }
+}
+
+void extcap_free_tokenized_sentence_list(extcap_token_sentence *f) {
+ extcap_token_sentence *t;
+
+ while (f != NULL ) {
+ t = f->next_sentence;
+ extcap_free_tokenized_sentence(f);
+ f = t;
+ }
+}
+
+extcap_token_sentence *extcap_tokenize_sentence(const gchar *s) {
+ gchar *b, *e, *eq;
+
+ extcap_token_param *tv = NULL;
+
+ extcap_token_sentence *rs = g_new(extcap_token_sentence, 1);
+
+ rs->sentence = NULL;
+ rs->next_sentence = NULL;
+ rs->param_list = NULL;
+
+ if ((b = g_strstr_len(s, -1, " ")) == NULL) {
+ extcap_free_tokenized_sentence(rs);
+ return NULL ;
+ }
+
+ rs->sentence = g_strndup(s, b - s);
+
+ if ((b = g_strstr_len(s, -1, "{")) == NULL) {
+ /* printf("debug - tokenizer - sentence with no values\n"); */
+ extcap_free_tokenized_sentence(rs);
+ return NULL ;
+ }
+
+ while (b != NULL ) {
+ if ((e = g_strstr_len(b, -1, "}")) == NULL) {
+ /* printf("debug - tokenizer - invalid, missing }\n"); */
+ extcap_free_tokenized_sentence(rs);
+ return NULL ;
+ }
+
+ if ((eq = g_strstr_len(b, -1, "=")) == NULL) {
+ /* printf("debug - tokenizer - invalid, missing =\n"); */
+ extcap_free_tokenized_sentence(rs);
+ return NULL ;
+ }
+
+ b++;
+ e--;
+
+ if (b >= eq || e <= eq) {
+ /* printf("debug - tokenizer - invalid, missing arg or value in {}\n"); */
+ extcap_free_tokenized_sentence(rs);
+ return NULL ;
+ }
+
+ tv = g_new(extcap_token_param, 1);
+ tv->arg = g_strndup(b, eq - b);
+ tv->value = g_strndup(eq + 1, e - eq);
+
+ if (g_ascii_strcasecmp(tv->arg, "number") == 0) {
+ tv->param_type = EXTCAP_PARAM_ARGNUM;
+ } else if (g_ascii_strcasecmp(tv->arg, "call") == 0) {
+ tv->param_type = EXTCAP_PARAM_CALL;
+ } else if (g_ascii_strcasecmp(tv->arg, "display") == 0) {
+ tv->param_type = EXTCAP_PARAM_DISPLAY;
+ } else if (g_ascii_strcasecmp(tv->arg, "type") == 0) {
+ tv->param_type = EXTCAP_PARAM_TYPE;
+ } else if (g_ascii_strcasecmp(tv->arg, "arg") == 0) {
+ tv->param_type = EXTCAP_PARAM_ARG;
+ } else if (g_ascii_strcasecmp(tv->arg, "default") == 0) {
+ tv->param_type = EXTCAP_PARAM_DEFAULT;
+ } else if (g_ascii_strcasecmp(tv->arg, "value") == 0) {
+ tv->param_type = EXTCAP_PARAM_VALUE;
+ } else if (g_ascii_strcasecmp(tv->arg, "range") == 0) {
+ tv->param_type = EXTCAP_PARAM_RANGE;
+ } else if (g_ascii_strcasecmp(tv->arg, "tooltip") == 0) {
+ tv->param_type = EXTCAP_PARAM_TOOLTIP;
+ } else if (g_ascii_strcasecmp(tv->arg, "mustexist") == 0) {
+ tv->param_type = EXTCAP_PARAM_FILE_MUSTEXIST;
+ } else if (g_ascii_strcasecmp(tv->arg, "name") == 0) {
+ tv->param_type = EXTCAP_PARAM_NAME;
+ } else if (g_ascii_strcasecmp(tv->arg, "enabled") == 0) {
+ tv->param_type = EXTCAP_PARAM_ENABLED;
+ } else {
+ tv->param_type = EXTCAP_PARAM_UNKNOWN;
+ }
+
+ tv->next_token = rs->param_list;
+ rs->param_list = tv;
+
+ /* printf("debug - tokenizer - got '%s' = '%s'\n", tv->arg, tv->value); */
+
+ b = e + 1;
+ if ((size_t) (b - s) > strlen(s))
+ break;
+
+ b = g_strstr_len(b, -1, "{");
+ }
+
+ return rs;
+}
+
+extcap_token_sentence *extcap_tokenize_sentences(const gchar *s) {
+ extcap_token_sentence *first = NULL, *cur = NULL, *last = NULL;
+
+ gchar **list, **list_iter;
+
+ list_iter = list = g_strsplit(s, "\n", 0);
+
+ while (*list_iter != NULL ) {
+ cur = extcap_tokenize_sentence(*list_iter);
+
+ if (cur != NULL) {
+ if (first == NULL) {
+ first = cur;
+ last = cur;
+ } else {
+ last->next_sentence = cur;
+ last = cur;
+ }
+ }
+
+ list_iter++;
+ }
+
+ g_strfreev(list);
+
+ return first;
+}
+
+extcap_token_param *extcap_find_param_by_type(extcap_token_param *first,
+ extcap_param_type t) {
+ while (first != NULL ) {
+ if (first->param_type == t) {
+ return first;
+ }
+
+ first = first->next_token;
+ }
+
+ return NULL ;
+}
+
+void extcap_free_value(extcap_value *v) {
+ if (v == NULL)
+ return;
+
+ if (v->call != NULL)
+ g_free(v->call);
+
+ if (v->display != NULL)
+ g_free(v->display);
+
+ g_free(v);
+}
+
+extcap_interface *extcap_new_interface(void) {
+ extcap_interface *r = g_new(extcap_interface, 1);
+
+ r->call = r->display = NULL;
+ r->next_interface = NULL;
+
+ return r;
+}
+
+void extcap_free_interface(extcap_interface *i) {
+ if (i == NULL)
+ return;
+
+ if (i->call != NULL)
+ g_free(i->call);
+
+ if (i->display != NULL)
+ g_free(i->display);
+}
+
+extcap_dlt *extcap_new_dlt(void) {
+ extcap_dlt *r = g_new(extcap_dlt, 1);
+
+ r->number = -1;
+ r->name = r->display = NULL;
+ r->next_dlt = NULL;
+
+ return r;
+}
+
+void extcap_free_dlt(extcap_dlt *d) {
+ if (d == NULL)
+ return;
+
+ if (d->name != NULL)
+ g_free(d->name);
+
+ if (d->display != NULL)
+ g_free(d->display);
+}
+
+extcap_arg *extcap_new_arg(void) {
+ extcap_arg *r = g_new(extcap_arg, 1);
+
+ r->call = NULL;
+ r->display = NULL;
+ r->tooltip = NULL;
+ r->arg_type = EXTCAP_ARG_UNKNOWN;
+ r->range_start = NULL;
+ r->range_end = NULL;
+ r->default_complex = NULL;
+ r->fileexists = FALSE;
+
+ r->values = NULL;
+ /*r->next_arg = NULL; */
+
+ return r;
+}
+
+static void extcap_free_valuelist(gpointer data, gpointer user_data _U_) {
+ extcap_free_value((extcap_value *) data);
+}
+
+void extcap_free_arg(extcap_arg *a) {
+
+ if (a == NULL)
+ return;
+
+ if (a->call != NULL)
+ g_free(a->call);
+
+ if (a->display != NULL)
+ g_free(a->display);
+
+ if (a->tooltip != NULL)
+ g_free(a->tooltip);
+
+ if (a->range_start != NULL)
+ extcap_free_complex(a->range_start);
+
+ if (a->range_end != NULL)
+ extcap_free_complex(a->range_end);
+
+ if (a->default_complex != NULL)
+ extcap_free_complex(a->default_complex);
+
+ g_list_foreach(a->values, (GFunc) extcap_free_valuelist, NULL);
+}
+
+static void extcap_free_arg_list_cb(gpointer listentry) {
+ if (listentry != NULL)
+ extcap_free_arg((extcap_arg *) listentry);
+}
+
+void extcap_free_arg_list(GList *a) {
+ g_list_free_full(a, extcap_free_arg_list_cb);
+}
+
+static gint glist_find_numbered_arg(gconstpointer listelem, gconstpointer needle) {
+ if (((const extcap_arg *) listelem)->arg_num == *((const int*) needle))
+ return 0;
+ return 1;
+}
+
+extcap_arg *extcap_parse_arg_sentence(GList * args, extcap_token_sentence *s) {
+ extcap_token_param *v = NULL;
+ extcap_arg *target_arg = NULL;
+ extcap_value *value = NULL;
+ GList * entry = NULL;
+ int tint;
+ extcap_sentence_type sent = EXTCAP_SENTENCE_UNKNOWN;
+
+ if (s == NULL)
+ return target_arg;
+
+ if (g_ascii_strcasecmp(s->sentence, "arg") == 0) {
+ sent = EXTCAP_SENTENCE_ARG;
+ /* printf("ARG sentence\n"); */
+ } else if (g_ascii_strcasecmp(s->sentence, "value") == 0) {
+ sent = EXTCAP_SENTENCE_VALUE;
+ /* printf("VALUE sentence\n"); */
+ }
+
+ if (sent == EXTCAP_SENTENCE_ARG) {
+ target_arg = extcap_new_arg();
+
+ if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_ARGNUM))
+ == NULL) {
+ extcap_free_arg(target_arg);
+ return NULL ;
+ }
+
+ if (sscanf(v->value, "%d", &(target_arg->arg_num)) != 1) {
+ extcap_free_arg(target_arg);
+ return NULL ;
+ }
+
+ if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_CALL))
+ == NULL) {
+ extcap_free_arg(target_arg);
+ return NULL ;
+ }
+ target_arg->call = g_strdup(v->value);
+
+ /* No value only parameters allowed */
+ if (strlen(target_arg->call) == 0) {
+ extcap_free_arg(target_arg);
+ return NULL ;
+ }
+
+ if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_DISPLAY))
+ == NULL) {
+ extcap_free_arg(target_arg);
+ return NULL ;
+ }
+ target_arg->display = g_strdup(v->value);
+
+ if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_TOOLTIP))
+ != NULL) {
+ target_arg->tooltip = g_strdup(v->value);
+ }
+
+ if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_FILE_MUSTEXIST))
+ != NULL) {
+ target_arg->fileexists = (v->value[0] == 't' || v->value[0] == 'T');
+ }
+
+ if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_TYPE))
+ == NULL) {
+ /* printf("no type in ARG sentence\n"); */
+ extcap_free_arg(target_arg);
+ return NULL ;
+ }
+
+ if (g_ascii_strcasecmp(v->value, "integer") == 0) {
+ target_arg->arg_type = EXTCAP_ARG_INTEGER;
+ } else if (g_ascii_strcasecmp(v->value, "unsigned") == 0) {
+ target_arg->arg_type = EXTCAP_ARG_UNSIGNED;
+ } else if (g_ascii_strcasecmp(v->value, "long") == 0) {
+ target_arg->arg_type = EXTCAP_ARG_LONG;
+ } else if (g_ascii_strcasecmp(v->value, "double") == 0) {
+ target_arg->arg_type = EXTCAP_ARG_DOUBLE;
+ } else if (g_ascii_strcasecmp(v->value, "boolean") == 0) {
+ target_arg->arg_type = EXTCAP_ARG_BOOLEAN;
+ } else if (g_ascii_strcasecmp(v->value, "boolflag") == 0) {
+ target_arg->arg_type = EXTCAP_ARG_BOOLFLAG;
+ } else if (g_ascii_strcasecmp(v->value, "menu") == 0) {
+ target_arg->arg_type = EXTCAP_ARG_MENU;
+ } else if (g_ascii_strcasecmp(v->value, "selector") == 0) {
+ target_arg->arg_type = EXTCAP_ARG_SELECTOR;
+ } else if (g_ascii_strcasecmp(v->value, "radio") == 0) {
+ target_arg->arg_type = EXTCAP_ARG_RADIO;
+ } else if (g_ascii_strcasecmp(v->value, "string") == 0) {
+ target_arg->arg_type = EXTCAP_ARG_STRING;
+ } else if (g_ascii_strcasecmp(v->value, "fileselect") == 0) {
+ target_arg->arg_type = EXTCAP_ARG_FILESELECT;
+ } else if (g_ascii_strcasecmp(v->value, "multicheck") == 0) {
+ target_arg->arg_type = EXTCAP_ARG_MULTICHECK;
+ } else {
+ printf("invalid type %s in ARG sentence\n", v->value);
+ extcap_free_arg(target_arg);
+ return NULL ;
+ }
+
+ if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_RANGE))
+ != NULL) {
+ gchar *cp = g_strstr_len(v->value, -1, ",");
+
+ if (cp == NULL) {
+ printf("invalid range, expected value,value got %s\n",
+ v->value);
+ extcap_free_arg(target_arg);
+ return NULL ;
+ }
+
+ if ((target_arg->range_start = extcap_parse_complex(
+ target_arg->arg_type, v->value)) == NULL) {
+ printf("invalid range, expected value,value got %s\n",
+ v->value);
+ extcap_free_arg(target_arg);
+ return NULL ;
+ }
+
+ if ((target_arg->range_end = extcap_parse_complex(
+ target_arg->arg_type, cp + 1)) == NULL) {
+ printf("invalid range, expected value,value got %s\n",
+ v->value);
+ extcap_free_arg(target_arg);
+ return NULL ;
+ }
+ }
+
+ if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_DEFAULT))
+ != NULL) {
+ if ((target_arg->default_complex = extcap_parse_complex(
+ target_arg->arg_type, v->value)) == NULL) {
+ printf("invalid default, couldn't parse %s\n", v->value);
+ }
+ }
+
+ } else if (sent == EXTCAP_SENTENCE_VALUE) {
+ if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_ARG))
+ == NULL) {
+ printf("no arg in VALUE sentence\n");
+ return NULL ;
+ }
+
+ if (sscanf(v->value, "%d", &tint) != 1) {
+ printf("invalid arg in VALUE sentence\n");
+ return NULL ;
+ }
+
+ ;
+ if ((entry = g_list_find_custom(args, &tint, glist_find_numbered_arg))
+ == NULL) {
+ printf("couldn't find arg %d in list for VALUE sentence\n", tint);
+ return NULL ;
+ }
+
+ value = g_new(extcap_value, 1);
+ value->display = NULL;
+ value->call = NULL;
+ value->enabled = FALSE;
+ value->is_default = FALSE;
+ value->arg_num = tint;
+
+ if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_VALUE))
+ == NULL) {
+ /* printf("no value in VALUE sentence\n"); */
+ extcap_free_value(value);
+ return NULL ;
+ }
+ value->call = g_strdup(v->value);
+
+ if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_DISPLAY))
+ == NULL) {
+ /* printf("no display in VALUE sentence\n"); */
+ extcap_free_value(value);
+ return NULL ;
+ }
+ value->display = g_strdup(v->value);
+
+ if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_DEFAULT))
+ != NULL) {
+ /* printf("found default value\n"); */
+ value->is_default = (v->value[0] == 't' || v->value[0] == 'T');
+ }
+
+ if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_ENABLED))
+ != NULL) {
+ value->enabled = (v->value[0] == 't' || v->value[0] == 'T');
+ }
+
+ ((extcap_arg*) entry->data)->values = g_list_append(
+ ((extcap_arg*) entry->data)->values, value);
+
+ return NULL ;
+ }
+
+ return target_arg;
+}
+
+GList * extcap_parse_args(extcap_token_sentence *first_s) {
+ GList * args = NULL;
+
+ while (first_s) {
+ extcap_arg *ra = NULL;
+
+ if ((ra = extcap_parse_arg_sentence(args, first_s)) != NULL)
+ args = g_list_append(args, (gpointer) ra);
+
+ first_s = first_s->next_sentence;
+ }
+
+ return args;
+}
+
+int extcap_parse_interface_sentence(extcap_token_sentence *s,
+ extcap_interface **ri) {
+ extcap_token_param *v = NULL;
+ extcap_sentence_type sent = EXTCAP_SENTENCE_UNKNOWN;
+
+ *ri = NULL;
+
+ if (s == NULL)
+ return -1;
+
+ if (g_ascii_strcasecmp(s->sentence, "interface") == 0) {
+ sent = EXTCAP_SENTENCE_INTERFACE;
+ /* printf("INTERFACE sentence\n"); */
+ }
+
+ if (sent == EXTCAP_SENTENCE_UNKNOWN)
+ return -1;
+
+ *ri = extcap_new_interface();
+
+ if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_VALUE))
+ == NULL) {
+ printf("No value in INTERFACE sentence\n");
+ extcap_free_interface(*ri);
+ return -1;
+ }
+ (*ri)->call = g_strdup(v->value);
+
+ if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_DISPLAY))
+ == NULL) {
+ printf("No display in INTERFACE sentence\n");
+ extcap_free_interface(*ri);
+ return -1;
+ }
+ (*ri)->display = g_strdup(v->value);
+
+ return 1;
+}
+
+int extcap_parse_interfaces(extcap_token_sentence *first_s,
+ extcap_interface **first_int) {
+ extcap_interface *first_i = NULL, *last_i = NULL;
+
+ while (first_s) {
+ extcap_interface *ri;
+
+ if (extcap_parse_interface_sentence(first_s, &ri) >= 0 && ri != NULL) {
+ if (first_i == NULL) {
+ first_i = last_i = ri;
+ } else {
+ last_i->next_interface = ri;
+ last_i = ri;
+ }
+ }
+
+ first_s = first_s->next_sentence;
+ }
+
+ *first_int = first_i;
+
+ return 1;
+}
+
+int extcap_parse_dlt_sentence(extcap_token_sentence *s, extcap_dlt **rd) {
+ extcap_token_param *v = NULL;
+ extcap_sentence_type sent = EXTCAP_SENTENCE_UNKNOWN;
+
+ *rd = NULL;
+
+ if (s == NULL)
+ return -1;
+
+ if (g_ascii_strcasecmp(s->sentence, "dlt") == 0) {
+ sent = EXTCAP_SENTENCE_DLT;
+ }
+
+ if (sent == EXTCAP_SENTENCE_UNKNOWN)
+ return -1;
+
+ *rd = extcap_new_dlt();
+
+ if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_ARGNUM))
+ == NULL) {
+ printf("No number in DLT sentence\n");
+ extcap_free_dlt(*rd);
+ return -1;
+ }
+ if (sscanf(v->value, "%d", &((*rd)->number)) != 1) {
+ printf("Invalid number in DLT sentence\n");
+ extcap_free_dlt(*rd);
+ return -1;
+ }
+
+ if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_NAME))
+ == NULL) {
+ printf("No name in DLT sentence\n");
+ extcap_free_dlt(*rd);
+ return -1;
+ }
+ (*rd)->name = g_strdup(v->value);
+
+ if ((v = extcap_find_param_by_type(s->param_list, EXTCAP_PARAM_DISPLAY))
+ == NULL) {
+ printf("No display in DLT sentence\n");
+ extcap_free_dlt(*rd);
+ return -1;
+ }
+ (*rd)->display = g_strdup(v->value);
+
+ return 1;
+}
+
+int extcap_parse_dlts(extcap_token_sentence *first_s, extcap_dlt **first_dlt) {
+ extcap_dlt *first_d = NULL, *last_d = NULL;
+
+ while (first_s) {
+ extcap_dlt *rd;
+
+ if (extcap_parse_dlt_sentence(first_s, &rd) >= 0 && rd != NULL) {
+ if (first_d == NULL) {
+ first_d = last_d = rd;
+ } else {
+ last_d->next_dlt = rd;
+ last_d = rd;
+ }
+ }
+
+ first_s = first_s->next_sentence;
+ }
+
+ *first_dlt = first_d;
+
+ return 1;
+}
+
+/*
+ * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=4 noexpandtab:
+ * :indentSize=4:tabSize=4:noTabs=false:
+ */
diff --git a/extcap_parser.h b/extcap_parser.h
new file mode 100644
index 0000000000..0058597535
--- /dev/null
+++ b/extcap_parser.h
@@ -0,0 +1,253 @@
+/* extcap_parser.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 __EXTCAP_PARSER_H__
+#define __EXTCAP_PARSER_H__
+
+#include <stdio.h>
+#include <glib.h>
+#include <string.h>
+
+typedef enum {
+ EXTCAP_SENTENCE_UNKNOWN,
+ EXTCAP_SENTENCE_ARG,
+ EXTCAP_SENTENCE_VALUE,
+ EXTCAP_SENTENCE_FLAG,
+ EXTCAP_SENTENCE_INTERFACE,
+ EXTCAP_SENTENCE_DLT
+} extcap_sentence_type;
+
+typedef enum {
+ /* Simple types */
+ EXTCAP_ARG_UNKNOWN,
+ EXTCAP_ARG_INTEGER,
+ EXTCAP_ARG_UNSIGNED,
+ EXTCAP_ARG_LONG,
+ EXTCAP_ARG_DOUBLE,
+ EXTCAP_ARG_BOOLEAN,
+ EXTCAP_ARG_BOOLFLAG,
+ EXTCAP_ARG_STRING,
+ /* Complex GUI types which are populated with value sentences */
+ EXTCAP_ARG_MENU,
+ EXTCAP_ARG_SELECTOR,
+ EXTCAP_ARG_RADIO,
+ EXTCAP_ARG_MULTICHECK,
+ EXTCAP_ARG_FILESELECT
+} extcap_arg_type;
+
+typedef enum {
+ /* value types */
+ EXTCAP_PARAM_UNKNOWN,
+ EXTCAP_PARAM_ARGNUM,
+ EXTCAP_PARAM_CALL,
+ EXTCAP_PARAM_DISPLAY,
+ EXTCAP_PARAM_TYPE,
+ EXTCAP_PARAM_ARG,
+ EXTCAP_PARAM_DEFAULT,
+ EXTCAP_PARAM_VALUE,
+ EXTCAP_PARAM_RANGE,
+ EXTCAP_PARAM_TOOLTIP,
+ EXTCAP_PARAM_NAME,
+ EXTCAP_PARAM_ENABLED,
+ EXTCAP_PARAM_FILE_MUSTEXIST
+} extcap_param_type;
+
+/* Values for a given sentence; values are all stored as a call
+ * and a value string, or a valid range, so we only need to store
+ * those and repeat them */
+typedef struct _extcap_value {
+ int arg_num;
+
+ gchar *call;
+ gchar *display;
+ gboolean enabled;
+ gboolean is_default;
+} extcap_value;
+
+/* Complex-ish struct for storing complex values */
+typedef struct _extcap_complex {
+ extcap_arg_type complex_type;
+ union {
+ int int_value;
+ unsigned int uint_value;
+ long long_value;
+ double double_value;
+ gboolean bool_value;
+ gchar *string_value;
+ } complex_value;
+ gboolean value_filled;
+} extcap_complex;
+
+/* An argument sentence and accompanying options */
+typedef struct _extcap_arg {
+ int arg_num;
+
+ gchar *call;
+ gchar *display;
+ gchar *tooltip;
+ gboolean fileexists;
+
+ extcap_arg_type arg_type;
+
+ extcap_complex *range_start;
+ extcap_complex *range_end;
+ extcap_complex *default_complex;
+
+ GList * values;
+} extcap_arg;
+
+typedef struct _extcap_if {
+ gchar * extcap_path;
+ GList * interfaces;
+} extcap_if;
+
+typedef struct _extcap_interface {
+ gchar *call;
+ gchar *display;
+
+ struct _extcap_interface *next_interface;
+} extcap_interface;
+
+extcap_interface *extcap_new_interface(void);
+void extcap_free_interface(extcap_interface *interface);
+
+typedef struct _extcap_dlt {
+ gint number;
+ gchar *name;
+ gchar *display;
+
+ struct _extcap_dlt *next_dlt;
+} extcap_dlt;
+
+extcap_dlt *extcap_new_dlt(void);
+void extcap_free_dlt(extcap_dlt *dlt);
+
+/* Parser internals */
+typedef struct _extcap_token_param {
+ gchar *arg;
+ gchar *value;
+
+ extcap_param_type param_type;
+
+ struct _extcap_token_param *next_token;
+} extcap_token_param;
+
+typedef struct _extcap_token_sentence {
+ gchar *sentence;
+
+ extcap_token_param *param_list;
+
+ struct _extcap_token_sentence *next_sentence;
+} extcap_token_sentence;
+
+/* Parse a string into a complex type */
+extcap_complex *extcap_parse_complex(extcap_arg_type complex_type,
+ const gchar *data);
+
+/* Free a complex */
+void extcap_free_complex(extcap_complex *comp);
+
+/* Print a complex value out for debug */
+void extcap_printf_complex(extcap_complex *comp);
+
+/*
+ * Return a string representation of a complex type
+ * Caller is responsible for calling g_free on the returned string
+ */
+gchar *extcap_get_complex_as_string(extcap_complex *comp);
+
+int extcap_complex_get_int(extcap_complex *comp);
+unsigned int extcap_complex_get_uint(extcap_complex *comp);
+long extcap_complex_get_long(extcap_complex *comp);
+double extcap_complex_get_double(extcap_complex *comp);
+gboolean extcap_complex_get_bool(extcap_complex *comp);
+gchar *extcap_complex_get_string(extcap_complex *comp);
+
+/* compares the default value of an element with a given parameter */
+gboolean extcap_compare_is_default(extcap_arg *element, extcap_complex *test);
+
+void extcap_free_tokenized_param(extcap_token_param *v);
+void extcap_free_tokenized_sentence(extcap_token_sentence *s);
+void extcap_free_tokenized_sentence_list(extcap_token_sentence *f);
+
+/* Turn a sentence into logical tokens, don't validate beyond basic syntax */
+extcap_token_sentence *extcap_tokenize_sentence(const gchar *s);
+
+/* Tokenize a set of sentences (such as the output of a g_spawn_sync) */
+extcap_token_sentence *extcap_tokenize_sentences(const gchar *s);
+
+/* Find an argument in the extcap_arg list which matches the given arg=X number */
+extcap_arg *extcap_find_numbered_arg(extcap_arg *first, int number);
+
+/* Find the first occurrence in a parameter list of a parameter of the given type */
+extcap_token_param *extcap_find_param_by_type(extcap_token_param *first,
+ extcap_param_type t);
+
+void extcap_free_value(extcap_value *v);
+
+extcap_arg *extcap_new_arg(void);
+
+/* Free a single argument */
+void extcap_free_arg(extcap_arg *a);
+
+/* Free an entire arg list */
+void extcap_free_arg_list(GList *a);
+
+/*
+ * Parse a tokenized sentence and validate. If a new sentence is created, the result
+ * is returned in 'ra'. On error, < 0 is returned. Not all sentences will create a
+ * new returned sentence (VALUE sentences, for example)
+ */
+extcap_arg * extcap_parse_arg_sentence(GList * args, extcap_token_sentence *s);
+
+/* Parse all sentences for args and values */
+GList * extcap_parse_args(extcap_token_sentence *first_s);
+
+/*
+ * Parse a tokenized set of sentences and validate, looking for interface definitions.
+ */
+int extcap_parse_interface_sentence(extcap_token_sentence *s,
+ extcap_interface **ri);
+
+/* Parse all sentences for interfaces */
+int extcap_parse_interfaces(extcap_token_sentence *first_s,
+ extcap_interface **first_int);
+
+/* Parse a tokenized set of sentences and validate, looking for DLT definitions */
+int extcap_parse_dlt_sentence(extcap_token_sentence *s, extcap_dlt **ri);
+
+/* Parse all sentences for DLTs */
+int extcap_parse_dlts(extcap_token_sentence *first_s, extcap_dlt **first_dlt);
+
+#endif
+
+/*
+ * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=4 noexpandtab:
+ * :indentSize=4:tabSize=4:noTabs=false:
+ */
diff --git a/packaging/nsis/wireshark.nsi b/packaging/nsis/wireshark.nsi
index d87db3e00f..881a02eaee 100644
--- a/packaging/nsis/wireshark.nsi
+++ b/packaging/nsis/wireshark.nsi
@@ -886,6 +886,8 @@ SetOutPath $INSTDIR
File "${STAGING_DIR}\${PROGRAM_NAME_PATH_GTK}"
File "${STAGING_DIR}\${GDK_DLL}"
File "${STAGING_DIR}\libgdk_pixbuf-2.0-0.dll"
+File "${STAGING_DIR}\gspawn-${WIRESHARK_TARGET_PLATFORM}-helper.exe"
+File "${STAGING_DIR}\gspawn-${WIRESHARK_TARGET_PLATFORM}-helper-console.exe"
File "${STAGING_DIR}\${GTK_DLL}"
File "${STAGING_DIR}\libatk-1.0-0.dll"
File "${STAGING_DIR}\libpango-1.0-0.dll"
diff --git a/ui/gtk/CMakeLists.txt b/ui/gtk/CMakeLists.txt
index a97a72e893..545781eebc 100644
--- a/ui/gtk/CMakeLists.txt
+++ b/ui/gtk/CMakeLists.txt
@@ -119,6 +119,13 @@ set(WIRESHARK_GTK_SRC
webbrowser.c
)
+if(HAVE_EXTCAP)
+ set(WIRESHARK_GTK_SRC
+ ${WIRESHARK_GTK_SRC}
+ extcap_gtk.c
+ )
+endif()
+
set(WIRESHARK_DIRTY_TAP_SRC
)
diff --git a/ui/gtk/Makefile.common b/ui/gtk/Makefile.common
index 405ae13c9a..c2a0a726e1 100644
--- a/ui/gtk/Makefile.common
+++ b/ui/gtk/Makefile.common
@@ -135,6 +135,7 @@ WIRESHARK_GTK_SRC = \
uat_gui.c \
voip_calls.c \
webbrowser.c \
+ extcap_gtk.c \
$(WIRESHARK_CUSTOM_GTK_SRC)
about_dlg.c main_welcome.c: wssplash.h remote_icons.h
@@ -317,4 +318,5 @@ noinst_HEADERS = \
wsiconcap.h \
wsicon.h \
wssplash.h \
+ extcap_gtk.h \
$(WIRESHARK_CUSTOM_HDRS)
diff --git a/ui/gtk/about_dlg.c b/ui/gtk/about_dlg.c
index a7b251defc..151a691feb 100644
--- a/ui/gtk/about_dlg.c
+++ b/ui/gtk/about_dlg.c
@@ -484,6 +484,18 @@ about_folders_page_new(void)
g_free(path);
#endif
+#ifdef HAVE_EXTCAP
+ /* extcap */
+ constpath = get_extcap_dir();
+
+ resultArray = g_strsplit(constpath, G_SEARCHPATH_SEPARATOR_S, 10);
+
+ for(i = 0; resultArray[i]; i++)
+ about_folders_row(table, "Extcap path", g_strstrip(resultArray[i]),
+ "Extcap Plugins search path");
+ g_strfreev(resultArray);
+#endif
+
gtk_container_add(GTK_CONTAINER(scrolledwindow), table);
return scrolledwindow;
diff --git a/ui/gtk/capture_dlg.c b/ui/gtk/capture_dlg.c
index 4a628c022b..7a299d047c 100644
--- a/ui/gtk/capture_dlg.c
+++ b/ui/gtk/capture_dlg.c
@@ -80,6 +80,11 @@
#include "airpcap_dlg.h"
#endif
+#ifdef HAVE_EXTCAP
+#include "extcap.h"
+#include "ui/gtk/extcap_gtk.h"
+#endif
+
/*
* Symbolic names for column indices.
*/
@@ -165,6 +170,10 @@ enum
#define E_CAP_T_RESOLVE_KEY "cap_t_resolve"
#define E_CAP_E_RESOLVE_KEY "cap_e_resolve"
+#ifdef HAVE_EXTCAP
+#define E_CAP_EXTCAP_KEY "cap_extcap_vbox"
+#endif
+
#define E_CAP_IFTYPE_CBX_KEY "cap_iftype_cbx"
#ifdef HAVE_PCAP_REMOTE
#define E_CAP_IF_LIST_KEY "cap_if_list"
@@ -2382,6 +2391,9 @@ save_options_cb(GtkWidget *win _U_, gpointer user_data _U_)
#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
GtkWidget *buffer_size_sb;
#endif
+#ifdef HAVE_EXTCAP
+ GtkWidget *extcap_vbox;
+#endif
interface_t device;
gpointer ptr = NULL;
@@ -2403,6 +2415,10 @@ save_options_cb(GtkWidget *win _U_, gpointer user_data _U_)
linktype_combo_box = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CAP_LT_CBX_KEY);
+#ifdef HAVE_EXTCAP
+ extcap_vbox = (GtkWidget *) g_object_get_data(G_OBJECT(opt_edit_w), E_CAP_EXTCAP_KEY);
+#endif
+
if (device.links != NULL) {
if (ws_combo_box_get_active_pointer(GTK_COMBO_BOX(linktype_combo_box), &ptr)) {
/* Even though device.links != NULL, we might not have an active pointer
@@ -2436,6 +2452,19 @@ save_options_cb(GtkWidget *win _U_, gpointer user_data _U_)
g_free(device.cfilter);
g_assert(filter_text != NULL);
device.cfilter = filter_text;
+
+#ifdef HAVE_EXTCAP
+ if (device.external_cap_args_settings != NULL)
+ g_hash_table_unref(device.external_cap_args_settings);
+
+ device.external_cap_args_settings = extcap_gtk_get_state(extcap_vbox);
+
+ /* Destroy the args data linked in the gtk widget */
+#if 0
+ extcap_gtk_free_args(extcap_vbox);
+#endif
+#endif
+
#ifdef HAVE_PCAP_CREATE
/* if dumpcap reported that the interface does not support monitor
mode, we disable monitor mode even if the user explicitly selected it */
@@ -2472,6 +2501,36 @@ adjust_snap_sensitivity(GtkWidget *tb _U_, gpointer parent_w _U_)
g_array_insert_val(global_capture_opts.all_ifaces, marked_interface, device);
}
+#ifdef HAVE_EXTCAP
+void
+extcap_free_arglist(gpointer data, gpointer user_data _U_)
+{
+ extcap_free_arg ( (extcap_arg *) data );
+}
+
+static GtkWidget *build_extcap_options(const gchar *name, GHashTable *hash) {
+ GtkWidget *ret_box = NULL;
+ GList *arglist = NULL;
+ GList *elem = NULL;
+
+ arglist = extcap_get_if_configuration( name );
+ for ( elem = g_list_first(arglist); elem; elem = elem->next )
+ {
+ GSList *widget_list;
+
+#if GTK_CHECK_VERSION(3, 0, 0)
+ ret_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 3);
+#else
+ ret_box = gtk_vbox_new(FALSE, 3);
+#endif
+ widget_list = extcap_populate_gtk_vbox((GList *) elem->data, ret_box, hash);
+ g_object_set_data(G_OBJECT(ret_box), EXTCAP_GTK_DATA_KEY_WIDGETLIST, widget_list);
+ }
+
+ return ret_box;
+}
+#endif
+
void options_interface_cb(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *column _U_, gpointer userdata)
{
GtkWidget *caller, *window, *swindow = NULL, *if_view,
@@ -2494,7 +2553,11 @@ void options_interface_cb(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColum
*compile_bt,
#endif
*bbox, *ok_but, *cancel_bt,
+#ifdef HAVE_EXTCAP
+ *extcap_vbox,
+#endif
*help_bt;
+
GList *cf_entry, *list, *cfilter_list;
GtkAdjustment *snap_adj;
#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
@@ -2518,6 +2581,9 @@ void options_interface_cb(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColum
GtkCellRenderer *renderer;
GtkListStore *store;
const gchar *new_cfilter;
+#ifdef HAVE_EXTCAP
+ GHashTable *extcap_hash;
+#endif
window = (GtkWidget *)userdata;
caller = gtk_widget_get_toplevel(GTK_WIDGET(window));
@@ -2544,6 +2610,9 @@ void options_interface_cb(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColum
#if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
device.buffer = DEFAULT_CAPTURE_BUFFER_SIZE;
#endif
+#ifdef HAVE_EXTCAP
+ device.external_cap_args_settings = NULL;
+#endif
model = gtk_tree_view_get_model(view);
gtk_tree_model_get_iter (model, &iter, path);
@@ -2913,6 +2982,14 @@ void options_interface_cb(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColum
}
#endif
+#ifdef HAVE_EXTCAP
+ extcap_hash = device.external_cap_args_settings;
+ extcap_vbox = build_extcap_options(device.name, extcap_hash);
+ gtk_box_pack_start(GTK_BOX(capture_vb), extcap_vbox, FALSE, FALSE, 5);
+ gtk_widget_show(extcap_vbox);
+ g_object_set_data(G_OBJECT(opt_edit_w), E_CAP_EXTCAP_KEY, extcap_vbox);
+#endif
+
/* Button row: "Start", "Cancel" and "Help" buttons */
bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 5);
diff --git a/ui/gtk/capture_if_dlg.c b/ui/gtk/capture_if_dlg.c
index 9eb002cc27..d792af83eb 100644
--- a/ui/gtk/capture_if_dlg.c
+++ b/ui/gtk/capture_if_dlg.c
@@ -426,6 +426,9 @@ GtkWidget * capture_get_if_icon(interface_t *device)
return pixbuf_to_widget(network_wired_pb_data);
case IF_PIPE:
case IF_STDIN:
+#ifdef HAVE_EXTCAP
+ case IF_EXTCAP:
+#endif
return pixbuf_to_widget(pipe_pb_data);
default:
printf("unknown device type\n");
diff --git a/ui/gtk/extcap_gtk.c b/ui/gtk/extcap_gtk.c
new file mode 100644
index 0000000000..69f3908626
--- /dev/null
+++ b/ui/gtk/extcap_gtk.c
@@ -0,0 +1,806 @@
+/* extcap_gtk.c
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include <ui/gtk/gui_utils.h>
+#include <wsutil/filesystem.h>
+
+#include <extcap_parser.h>
+#include "extcap_gtk.h"
+
+GHashTable *extcap_gtk_get_state(GtkWidget *widget) {
+ GSList *widget_list, *widget_iter;
+ GSList *radio_list = NULL, *radio_iter = NULL;
+
+ GtkWidget *list_widget, *radio_widget, *tree_widget, *entry_widget;
+
+ extcap_arg *arg = NULL;
+ extcap_value *value = NULL;
+ extcap_complex *parsed_complex = NULL;
+
+ GtkTreeSelection *treeselection;
+ GtkTreeModel *treemodel;
+ GtkTreeIter treeiter;
+
+ GHashTable *ret_hash;
+
+ gchar *call_string = NULL;
+
+ gchar **multi_list = NULL;
+ int multi_num = 0;
+ gboolean multi_valid, multi_enabled;
+
+ widget_list = (GSList *) g_object_get_data(G_OBJECT(widget),
+ EXTCAP_GTK_DATA_KEY_WIDGETLIST);
+
+ if (widget_list == NULL)
+ return NULL ;
+
+ /* String hash */
+ ret_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+ for (widget_iter = widget_list; widget_iter; widget_iter =
+ widget_iter->next) {
+ list_widget = (GtkWidget *) widget_iter->data;
+
+ if ((arg = (extcap_arg *) g_object_get_data(G_OBJECT(list_widget),
+ EXTCAP_GTK_DATA_KEY_ARGPTR)) == NULL) {
+ continue;
+ }
+
+ switch (arg->arg_type) {
+ case EXTCAP_ARG_INTEGER:
+ case EXTCAP_ARG_UNSIGNED:
+ case EXTCAP_ARG_LONG:
+ case EXTCAP_ARG_DOUBLE:
+ case EXTCAP_ARG_STRING:
+ parsed_complex = extcap_parse_complex(arg->arg_type,
+ gtk_entry_get_text(GTK_ENTRY(list_widget)));
+ if (parsed_complex == NULL) {
+ continue;
+ }
+ break;
+ case EXTCAP_ARG_BOOLEAN:
+ case EXTCAP_ARG_BOOLFLAG:
+ parsed_complex = extcap_parse_complex(arg->arg_type,
+ gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(list_widget)) ? "true" : "false");
+ break;
+ case EXTCAP_ARG_FILESELECT:
+ if ((entry_widget =
+ (GtkWidget *) g_object_get_data(G_OBJECT(list_widget),
+ EXTCAP_GTK_DATA_KEY_FILENAME)) == NULL) {
+ continue;
+ }
+ parsed_complex = extcap_parse_complex(arg->arg_type,
+ gtk_entry_get_text(GTK_ENTRY(entry_widget)));
+ if (parsed_complex == NULL) {
+ continue;
+ }
+ break;
+ case EXTCAP_ARG_MENU:
+ break;
+ case EXTCAP_ARG_RADIO:
+ if ((radio_widget = (GtkWidget *) g_object_get_data(
+ G_OBJECT(list_widget),
+ EXTCAP_GTK_DATA_KEY_FIRSTRADIO)) == NULL) {
+ continue;
+ }
+
+ if ((radio_list = gtk_radio_button_get_group(
+ GTK_RADIO_BUTTON(radio_widget))) == NULL) {
+ continue;
+ }
+
+ for (radio_iter = radio_list; radio_iter;
+ radio_iter = radio_iter->next) {
+ GtkWidget *cur_radio = (GtkWidget *) radio_iter->data;
+
+ if (gtk_toggle_button_get_active(
+ GTK_TOGGLE_BUTTON(cur_radio))) {
+ if ((value = (extcap_value *) g_object_get_data(
+ G_OBJECT(cur_radio),
+ EXTCAP_GTK_DATA_KEY_VALPTR)) == NULL) {
+ continue;
+ }
+
+ if (value->is_default)
+ continue;
+
+ call_string = g_strdup(value->call);
+ break;
+ }
+ }
+
+ break;
+ case EXTCAP_ARG_SELECTOR:
+ if ((tree_widget = (GtkWidget *) g_object_get_data(
+ G_OBJECT(list_widget),
+ EXTCAP_GTK_DATA_KEY_TREEVIEW)) == NULL) {
+ continue;
+ }
+
+ treeselection = gtk_tree_view_get_selection(
+ GTK_TREE_VIEW(tree_widget));
+ treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(tree_widget));
+ if (gtk_tree_selection_get_selected(treeselection, &treemodel,
+ &treeiter)) {
+ gtk_tree_model_get(treemodel, &treeiter, EXTCAP_GTK_COL_VALUE,
+ &value, -1);
+
+ if (value->is_default)
+ continue;
+
+ call_string = g_strdup(value->call);
+ }
+
+ break;
+ case EXTCAP_ARG_MULTICHECK:
+ if ((tree_widget = (GtkWidget *) g_object_get_data(
+ G_OBJECT(list_widget),
+ EXTCAP_GTK_DATA_KEY_TREEVIEW)) == NULL) {
+ continue;
+ }
+
+ treeselection = gtk_tree_view_get_selection(
+ GTK_TREE_VIEW(tree_widget));
+ treemodel = gtk_tree_view_get_model(GTK_TREE_VIEW(tree_widget));
+
+ multi_num = 0;
+
+ /* Count the # of items enabled */
+ multi_valid = gtk_tree_model_get_iter_first(treemodel, &treeiter);
+ while (multi_valid) {
+ gtk_tree_model_get(treemodel, &treeiter,
+ EXTCAP_GTK_MULTI_COL_CHECK, &multi_enabled, -1);
+
+ if (multi_enabled)
+ multi_num++;
+
+ multi_valid = gtk_tree_model_iter_next(treemodel, &treeiter);
+ }
+
+ multi_list = g_new(gchar *, multi_num + 1);
+
+ multi_num = 0;
+
+ /* Count the # of items enabled */
+ multi_valid = gtk_tree_model_get_iter_first(treemodel, &treeiter);
+ while (multi_valid) {
+ gtk_tree_model_get(treemodel, &treeiter,
+ EXTCAP_GTK_MULTI_COL_CHECK, &multi_enabled,
+ EXTCAP_GTK_MULTI_COL_VALUE, &value, -1);
+
+ if (multi_enabled) {
+ multi_list[multi_num] = g_strdup(value->call);
+ multi_num++;
+ }
+
+ multi_valid = gtk_tree_model_iter_next(treemodel, &treeiter);
+ }
+ multi_list[multi_num] = NULL;
+
+ call_string = g_strjoinv(",", multi_list);
+
+ g_strfreev(multi_list);
+
+ break;
+ default:
+ break;
+ }
+
+ if (parsed_complex == NULL && call_string == NULL)
+ continue;
+
+ /* Comparing if the user has changed the value at all, and ignoring it if so */
+ if (extcap_compare_is_default(arg, parsed_complex))
+ continue;
+
+ /* Flags are set as is, and have not true/false switch */
+ if ((arg->arg_type == EXTCAP_ARG_BOOLFLAG)
+ && (extcap_complex_get_bool(parsed_complex) == TRUE)) {
+ call_string = g_strdup(" ");
+ }
+
+ if (parsed_complex != NULL && call_string == NULL)
+ call_string = extcap_get_complex_as_string(parsed_complex);
+
+ g_hash_table_insert(ret_hash, g_strdup(arg->call),
+ g_strdup(call_string));
+
+ g_free(call_string);
+ call_string = NULL;
+
+ g_free(parsed_complex);
+ parsed_complex = NULL;
+ }
+
+ return ret_hash;
+}
+
+static void extcap_gtk_treeview_vscroll_map_handler(GtkTreeView *treeView,
+ gpointer data) {
+ GtkWidget *padBox = (GtkWidget*) data;
+ gint x, y;
+
+ g_assert(GTK_IS_BOX(padBox));
+
+ /* Set the padding above the scrollbar to the height of the tree header window */
+ gtk_tree_view_convert_bin_window_to_widget_coords(GTK_TREE_VIEW(treeView),
+ 0, 0, &x, &y);
+ gtk_widget_set_size_request(padBox, -1, y);
+}
+
+static GtkWidget *extcap_gtk_wrap_scroll_treeview(GtkWidget *view) {
+ GtkWidget *vscroll, *padbox, *hbox, *vbox;
+ GtkAdjustment *padj;
+
+#if GTK_CHECK_VERSION(3, 0, 0)
+ padj = gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(view));
+#if GTK_CHECK_VERSION(3, 2, 0)
+ vscroll = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, padj);
+#else
+ vscroll = gtk_vscrollbar_new(padj);
+#endif
+#else
+ padj = gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(view));
+ vscroll = gtk_vscrollbar_new(padj);
+#endif
+
+ hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0, FALSE);
+
+ /* First insert the tree view */
+ gtk_box_pack_start(GTK_BOX(hbox), view, TRUE, TRUE, 0);
+ gtk_widget_show(view);
+
+ /* Pack to the right a vbox containing a box for padding at top and scrollbar */
+ vbox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
+ gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
+ gtk_widget_show(vbox);
+
+ padbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0, FALSE);
+ gtk_box_pack_start(GTK_BOX(vbox), padbox, FALSE, FALSE, 0);
+ gtk_widget_show(padbox);
+
+ gtk_box_pack_start(GTK_BOX(vbox), vscroll, TRUE, TRUE, 0);
+ gtk_widget_show(vscroll);
+
+ g_object_set_data(G_OBJECT(hbox), EXTCAP_GTK_DATA_KEY_TREEVIEW, view);
+
+ g_signal_connect(view, "map",
+ G_CALLBACK(extcap_gtk_treeview_vscroll_map_handler), padbox);
+
+ return hbox;
+}
+
+GtkWidget *extcap_create_gtk_listwidget(extcap_arg *argument,
+ GHashTable *prev_map) {
+ GtkCellRenderer *renderer;
+ GtkTreeModel *model;
+ GtkWidget *view, *retview;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ extcap_value *v = NULL;
+ GList * walker = NULL;
+ gchar *prev_item = NULL;
+
+ if (g_list_length(argument->values) == 0)
+ return NULL ;
+
+ view = gtk_tree_view_new();
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
+
+ store = gtk_list_store_new(EXTCAP_GTK_NUM_COLS, G_TYPE_STRING,
+ G_TYPE_POINTER);
+
+ model = GTK_TREE_MODEL(store);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(view), model);
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
+
+ if (prev_map != NULL)
+ prev_item = (gchar *) g_hash_table_lookup(prev_map, argument->call);
+
+ for (walker = g_list_first(argument->values); walker != NULL ; walker =
+ walker->next) {
+ v = (extcap_value *) walker->data;
+ if (v->display == NULL)
+ break;
+
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, EXTCAP_GTK_COL_DISPLAY, v->display,
+ EXTCAP_GTK_COL_VALUE, v, -1);
+
+ if (prev_item != NULL) {
+ if (g_ascii_strcasecmp(prev_item, v->call) == 0) {
+ gtk_tree_selection_select_iter(selection, &iter);
+ }
+ } else if (v->is_default) {
+ gtk_tree_selection_select_iter(selection, &iter);
+ }
+ }
+
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "Name",
+ renderer, "text", EXTCAP_GTK_COL_DISPLAY, NULL);
+
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
+
+ retview = extcap_gtk_wrap_scroll_treeview(view);
+
+ if (gtk_tree_model_iter_n_children(model, NULL) > 3)
+ gtk_widget_set_size_request(retview, 0, 100);
+
+ /* Tree view has own reference */
+ g_object_unref(model);
+
+ return retview;
+}
+
+GtkWidget *extcap_create_gtk_radiowidget(extcap_arg *argument,
+ GHashTable *prev_map) {
+ GtkWidget *radiobox = NULL, *last_radio = NULL;
+ extcap_value *v = NULL;
+ GList * walker = NULL;
+ gchar *prev_item = NULL;
+
+ if (g_list_length(argument->values) == 0)
+ return NULL ;
+
+ if (prev_map != NULL)
+ prev_item = (gchar *) g_hash_table_lookup(prev_map, argument->call);
+
+ radiobox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 1, FALSE);
+
+ for (walker = g_list_first(argument->values); walker != NULL ; walker =
+ walker->next) {
+ v = (extcap_value *) walker->data;
+
+ if (last_radio == NULL) {
+ last_radio = gtk_radio_button_new_with_label(NULL, v->display);
+ /* Set a pointer to the first radio button */
+ g_object_set_data(G_OBJECT(radiobox),
+ EXTCAP_GTK_DATA_KEY_FIRSTRADIO, last_radio);
+ } else {
+ last_radio = gtk_radio_button_new_with_label_from_widget(
+ GTK_RADIO_BUTTON(last_radio), v->display);
+ }
+
+ /* Set a pointer to the value used in this radio */
+ g_object_set_data(G_OBJECT(last_radio), EXTCAP_GTK_DATA_KEY_VALPTR, v);
+
+ if (prev_item != NULL) {
+ if (g_ascii_strcasecmp(prev_item, v->call) == 0) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(last_radio),
+ TRUE);
+ }
+ } else if (v->is_default) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(last_radio), TRUE);
+ }
+
+ gtk_box_pack_start(GTK_BOX(radiobox), last_radio, TRUE, TRUE, 0);
+ gtk_widget_show(last_radio);
+ }
+
+ return radiobox;
+}
+
+static void extcap_gtk_multicheck_toggled(GtkCellRendererToggle *cell _U_,
+ gchar *path_str, gpointer data) {
+ GtkTreeModel *model = (GtkTreeModel *) data;
+ GtkTreeIter iter;
+ GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
+ gboolean enabled;
+
+ gtk_tree_model_get_iter(model, &iter, path);
+ gtk_tree_model_get(model, &iter, EXTCAP_GTK_MULTI_COL_CHECK, &enabled, -1);
+
+ enabled ^= 1;
+
+ gtk_list_store_set(GTK_LIST_STORE(model), &iter, EXTCAP_GTK_MULTI_COL_CHECK,
+ enabled, -1);
+
+ gtk_tree_path_free(path);
+}
+
+GtkWidget *extcap_create_gtk_rangewidget(extcap_arg *argument,
+ GHashTable *prev_map _U_) {
+ GtkWidget *spinButton;
+ GtkAdjustment *adjustment;
+
+ gfloat def = 0.0, min = 0.0, max = 0.0;
+
+ switch (argument->arg_type) {
+ case EXTCAP_ARG_INTEGER:
+ def = (gfloat) extcap_complex_get_int(argument->default_complex);
+ min = (gfloat) extcap_complex_get_int(argument->range_start);
+ max = (gfloat) extcap_complex_get_int(argument->range_end);
+ break;
+ case EXTCAP_ARG_UNSIGNED:
+ def = (gfloat) extcap_complex_get_uint(argument->default_complex);
+ min = (gfloat) extcap_complex_get_uint(argument->range_start);
+ max = (gfloat) extcap_complex_get_uint(argument->range_end);
+ break;
+ case EXTCAP_ARG_LONG:
+ def = (gfloat) extcap_complex_get_long(argument->default_complex);
+ min = (gfloat) extcap_complex_get_long(argument->range_start);
+ max = (gfloat) extcap_complex_get_long(argument->range_end);
+ break;
+ case EXTCAP_ARG_DOUBLE:
+ def = (gfloat) extcap_complex_get_double(argument->default_complex);
+ min = (gfloat) extcap_complex_get_double(argument->range_start);
+ max = (gfloat) extcap_complex_get_double(argument->range_end);
+ break;
+ default:
+ return NULL ;
+ break;
+ }
+
+ adjustment = (GtkAdjustment *)gtk_adjustment_new(def, min, max, 1.0, 10.0, 0.0);
+
+ spinButton = gtk_spin_button_new(adjustment, 0, 0);
+ gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(spinButton), TRUE);
+ gtk_widget_set_size_request(spinButton, 80, -1);
+
+ return spinButton;
+}
+
+static void extcap_file_selectiondialog( GtkWidget *widget _U_, gpointer data )
+{
+ GtkWidget * filechooser = NULL;
+ GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE;
+ gchar *filename = NULL;
+ gint res = 0;
+ extcap_arg *argument = NULL;
+
+ if ( GTK_ENTRY(data) == NULL )
+ return;
+
+ argument = (extcap_arg *)g_object_get_data(G_OBJECT(data), EXTCAP_GTK_DATA_KEY_ARGUMENT);
+ if ( argument != NULL && argument->fileexists == TRUE )
+ action = GTK_FILE_CHOOSER_ACTION_OPEN;
+
+ filechooser = gtk_file_chooser_dialog_new("Select file path", NULL, action,
+ "_Cancel", GTK_RESPONSE_CANCEL, "_Open", GTK_RESPONSE_ACCEPT, NULL);
+
+ res = gtk_dialog_run (GTK_DIALOG (filechooser));
+ if (res == GTK_RESPONSE_ACCEPT)
+ {
+ GtkFileChooser *chooser = GTK_FILE_CHOOSER (filechooser);
+ filename = gtk_file_chooser_get_filename (chooser);
+
+ /* this check might not be necessary, but just to be on the safe side */
+ if ( action == GTK_FILE_CHOOSER_ACTION_OPEN && ! file_exists ( filename ) )
+ filename = g_strdup ( " " );
+
+ gtk_entry_set_text(GTK_ENTRY(data), filename);
+ }
+
+ gtk_widget_destroy (filechooser);
+}
+
+static GtkWidget *extcap_create_gtk_fileselect(extcap_arg *argument,
+ GHashTable *prev_map _U_, gchar * file _U_) {
+ GtkWidget * entry = NULL;
+ GtkWidget * button = NULL;
+ GtkWidget * ret_box = NULL;
+
+ button = gtk_button_new_with_label ("...");
+ entry = gtk_entry_new();
+ if (file != NULL)
+ gtk_entry_set_text(GTK_ENTRY(entry), file);
+ gtk_editable_set_editable (GTK_EDITABLE (entry), FALSE);
+ g_object_set_data(G_OBJECT(entry), EXTCAP_GTK_DATA_KEY_ARGUMENT, argument);
+
+#if GTK_CHECK_VERSION(3, 0, 0)
+ ret_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
+#else
+ ret_box = gtk_hbox_new(FALSE, 3);
+#endif
+
+ gtk_box_pack_start ( GTK_BOX(ret_box), entry, TRUE, TRUE, 5 );
+ gtk_widget_show(entry);
+ gtk_box_pack_end ( GTK_BOX(ret_box), button, FALSE, FALSE, 5 );
+ gtk_widget_show(button);
+
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (extcap_file_selectiondialog), (gpointer) entry);
+
+ g_object_set_data(G_OBJECT(ret_box), EXTCAP_GTK_DATA_KEY_FILENAME, entry);
+
+ return ret_box;
+}
+
+GtkWidget *extcap_create_gtk_multicheckwidget(extcap_arg *argument,
+ GHashTable *prev_map) {
+ GtkCellRenderer *renderer, *togglerenderer;
+ GtkTreeModel *model;
+ GtkWidget *view, *retview;
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+ extcap_value *v = NULL;
+ GList * walker = NULL;
+ gchar *prev_item = NULL;
+ gchar **prev_list = NULL, **prev_iter = NULL;
+ gboolean prev_value, prev_matched;
+
+ if (g_list_length(argument->values) == 0)
+ return NULL ;
+
+ view = gtk_tree_view_new();
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
+
+ store = gtk_list_store_new(EXTCAP_GTK_MULTI_NUM_COLS, G_TYPE_BOOLEAN,
+ G_TYPE_STRING, G_TYPE_POINTER);
+
+ model = GTK_TREE_MODEL(store);
+ gtk_tree_view_set_model(GTK_TREE_VIEW(view), model);
+ gtk_tree_selection_set_mode(selection, GTK_SELECTION_NONE);
+
+ if (prev_map != NULL)
+ prev_item = (gchar *) g_hash_table_lookup(prev_map, argument->call);
+
+ if (prev_item != NULL)
+ prev_list = g_strsplit(prev_item, ",", 0);
+
+ for (walker = g_list_first(argument->values); walker != NULL ; walker =
+ walker->next) {
+ v = (extcap_value *) walker->data;
+ if (v->display == NULL)
+ break;
+
+ prev_value = FALSE;
+ prev_matched = FALSE;
+ gtk_list_store_append(store, &iter);
+
+ if (prev_list != NULL) {
+ prev_matched = FALSE;
+ prev_iter = prev_list;
+
+ while (*prev_iter != NULL ) {
+ if (g_strcmp0(*prev_iter, v->call) == 0) {
+ prev_matched = TRUE;
+ prev_value = TRUE;
+ break;
+ }
+
+ prev_iter++;
+ }
+ }
+
+ if (prev_matched == FALSE)
+ prev_value = v->enabled;
+
+ gtk_list_store_set(store, &iter, EXTCAP_GTK_MULTI_COL_CHECK, prev_value,
+ EXTCAP_GTK_MULTI_COL_DISPLAY, v->display,
+ EXTCAP_GTK_MULTI_COL_VALUE, v, -1);
+ }
+
+ if (prev_list != NULL)
+ g_strfreev(prev_list);
+
+ renderer = gtk_cell_renderer_text_new();
+ togglerenderer = gtk_cell_renderer_toggle_new();
+ g_signal_connect(togglerenderer, "toggled",
+ G_CALLBACK(extcap_gtk_multicheck_toggled), model);
+ gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1,
+ "Enabled", togglerenderer, "active", EXTCAP_GTK_MULTI_COL_CHECK,
+ NULL);
+ gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, "Name",
+ renderer, "text", EXTCAP_GTK_MULTI_COL_DISPLAY,
+ NULL);
+
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
+
+ retview = extcap_gtk_wrap_scroll_treeview(view);
+
+ if (gtk_tree_model_iter_n_children(model, NULL) > 3)
+ gtk_widget_set_size_request(retview, 0, 100);
+
+ /* Tree view has own reference */
+ g_object_unref(model);
+
+ return retview;
+}
+
+void extcap_gtk_free_args(GtkWidget *vbox) {
+ GList *arguments = (GList *) g_object_get_data(G_OBJECT(vbox),
+ EXTCAP_GTK_DATA_KEY_ARGPTR);
+ extcap_free_arg_list(arguments);
+ g_object_set_data(G_OBJECT(vbox), EXTCAP_GTK_DATA_KEY_ARGPTR, NULL);
+}
+
+GSList *extcap_populate_gtk_vbox(GList *arguments, GtkWidget *vbox,
+ GHashTable *prev_map) {
+ GSList *widget_toplist = NULL;
+
+ extcap_arg *arg_iter = NULL;
+
+ extcap_complex *prev_complex = NULL;
+ gchar *prev_call, *default_str;
+
+ GList * arg_list = g_list_first(arguments);
+ if ( arg_list == NULL )
+ return NULL;
+ arg_iter = (extcap_arg*) (arg_list->data);
+
+ g_object_set_data(G_OBJECT(vbox), EXTCAP_GTK_DATA_KEY_ARGPTR, arguments);
+
+ while (arg_list != NULL ) {
+ GtkWidget *hbox = NULL, *label = NULL, *item = NULL;
+
+ arg_iter = (extcap_arg*) (arg_list->data);
+
+ /* A new storage box for label + element */
+
+ hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5, FALSE);
+
+ if (prev_map != NULL
+ && (prev_call = (gchar *) g_hash_table_lookup(prev_map,
+ arg_iter->call)) != NULL) {
+ prev_complex = extcap_parse_complex(arg_iter->arg_type, prev_call);
+ } else {
+ prev_complex = NULL;
+ }
+
+ switch (arg_iter->arg_type) {
+ case EXTCAP_ARG_INTEGER:
+ case EXTCAP_ARG_UNSIGNED:
+ case EXTCAP_ARG_LONG:
+ case EXTCAP_ARG_DOUBLE:
+ label = gtk_label_new(arg_iter->display);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.1f);
+ item = extcap_create_gtk_rangewidget(arg_iter, prev_map);
+ if (item == NULL) {
+ item = gtk_entry_new();
+
+ if (prev_complex != NULL) {
+ default_str = extcap_get_complex_as_string(prev_complex);
+ gtk_entry_set_text(GTK_ENTRY(item), default_str);
+ g_free(default_str);
+ } else if (arg_iter->default_complex != NULL) {
+ default_str = extcap_get_complex_as_string(
+ arg_iter->default_complex);
+ gtk_entry_set_text(GTK_ENTRY(item), default_str);
+ g_free(default_str);
+ }
+ }
+ break;
+ case EXTCAP_ARG_STRING:
+ label = gtk_label_new(arg_iter->display);
+
+ item = gtk_entry_new();
+ default_str = NULL;
+
+ if (prev_complex != NULL)
+ default_str = extcap_get_complex_as_string(prev_complex);
+ else if (arg_iter->default_complex != NULL)
+ default_str = extcap_get_complex_as_string(
+ arg_iter->default_complex);
+
+ if (default_str != NULL) {
+ gtk_entry_set_text(GTK_ENTRY(item), default_str);
+ g_free(default_str);
+ }
+
+ break;
+ case EXTCAP_ARG_FILESELECT:
+ label = gtk_label_new(arg_iter->display);
+ default_str = NULL;
+
+ if (prev_complex != NULL)
+ default_str = extcap_get_complex_as_string(prev_complex);
+ else if (arg_iter->default_complex != NULL)
+ default_str = extcap_get_complex_as_string(
+ arg_iter->default_complex);
+
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.1f);
+ item = extcap_create_gtk_fileselect(arg_iter, prev_map, default_str);
+ if (default_str != NULL)
+ g_free(default_str);
+ break;
+ case EXTCAP_ARG_BOOLEAN:
+ case EXTCAP_ARG_BOOLFLAG:
+ item = gtk_check_button_new_with_label(arg_iter->display);
+
+ if (prev_complex != NULL) {
+ if (extcap_complex_get_bool(prev_complex))
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item), TRUE);
+ } else if (arg_iter->default_complex != NULL
+ && extcap_complex_get_bool(arg_iter->default_complex)) {
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item), TRUE);
+ }
+
+ break;
+ case EXTCAP_ARG_MENU:
+ break;
+ case EXTCAP_ARG_RADIO:
+ label = gtk_label_new(arg_iter->display);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.1f);
+ item = extcap_create_gtk_radiowidget(arg_iter, prev_map);
+ break;
+ case EXTCAP_ARG_SELECTOR:
+ label = gtk_label_new(arg_iter->display);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.1f);
+ item = extcap_create_gtk_listwidget(arg_iter, prev_map);
+ break;
+ case EXTCAP_ARG_MULTICHECK:
+ label = gtk_label_new(arg_iter->display);
+ gtk_misc_set_alignment(GTK_MISC(label), 0.0f, 0.1f);
+ item = extcap_create_gtk_multicheckwidget(arg_iter, prev_map);
+ break;
+ default:
+ break;
+ }
+
+ if (prev_complex != NULL)
+ extcap_free_complex(prev_complex);
+
+ if (label != NULL) {
+ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
+ gtk_widget_show(label);
+ }
+
+ if (item != NULL) {
+ gtk_box_pack_start(GTK_BOX(hbox), item, TRUE, TRUE, 0);
+ gtk_widget_show(item);
+ g_object_set_data(G_OBJECT(item), EXTCAP_GTK_DATA_KEY_ARGPTR,
+ arg_iter);
+
+ if (arg_iter->tooltip != NULL) {
+ gtk_widget_set_tooltip_text(item, arg_iter->tooltip);
+ }
+
+ widget_toplist = g_slist_append(widget_toplist, item);
+ }
+
+ gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 1);
+
+ gtk_widget_show(hbox);
+
+ arg_list = arg_list->next;
+ }
+
+ return widget_toplist;
+}
+
+/*
+ * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=4 noexpandtab:
+ * :indentSize=4:tabSize=4:noTabs=false:
+ */
diff --git a/ui/gtk/extcap_gtk.h b/ui/gtk/extcap_gtk.h
new file mode 100644
index 0000000000..07a8f21e7b
--- /dev/null
+++ b/ui/gtk/extcap_gtk.h
@@ -0,0 +1,122 @@
+/* extcap_gtk.c
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __EXTCAP_GTK_H__
+#define __EXTCAP_GTK_H__
+
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include <extcap_parser.h>
+
+/*
+ * GObject data keys for linking argument records to the gtk
+ * UI
+ */
+#define EXTCAP_GTK_DATA_KEY_ARGPTR "EXTCAP_ARGPTR"
+#define EXTCAP_GTK_DATA_KEY_VALPTR "EXTCAP_VALPTR"
+#define EXTCAP_GTK_DATA_KEY_FIRSTRADIO "EXTCAP_FIRSTRADIO"
+#define EXTCAP_GTK_DATA_KEY_WIDGETLIST "EXTCAP_WIDGETLIST"
+#define EXTCAP_GTK_DATA_KEY_TREEVIEW "EXTCAP_TREEVIEW"
+#define EXTCAP_GTK_DATA_KEY_FILENAME "EXTCAP_FILENAME"
+#define EXTCAP_GTK_DATA_KEY_ARGUMENT "EXTCAP_ARGUMENT"
+
+/*
+ * GTK UI / EXTCAP Linkage:
+ *
+ * Packed vbox of widgets
+ * Contains EXTCAP_WIDGETLIST pointing to enclosed widget list
+ *
+ * GSList gtk_ui_widgets
+ * Linked list of drawable widgets in the UI
+ *
+ * GtkWidget contained in GSList
+ * Drawn GTK UI element. If UI element is directly linked
+ * to argument, will contain EXTCAP_ARGPTR.
+ *
+ * Top-level GTK widgets will include text boxes, sliders
+ * (if supported), and checkboxes.
+ *
+ * If the top level widget contains radio buttons, it will
+ * contain an EXTCAP_ARGPTR *and* an EXTCAP_FIRSTRADIO
+ *
+ * Radio buttons
+ * Each radio button will contain an EXTCAP_VALPTR reference
+ * to the extcap_value * value being used.
+ *
+ * Selectors
+ * Each selector row contains a pointer to the value, in the
+ * column COL_VALUE
+ *
+ */
+
+enum extcap_gtk_col_types {
+ EXTCAP_GTK_COL_DISPLAY = 0, EXTCAP_GTK_COL_VALUE = 1, EXTCAP_GTK_NUM_COLS
+};
+
+enum extcap_gtk_multi_col_types {
+ EXTCAP_GTK_MULTI_COL_CHECK = 0,
+ EXTCAP_GTK_MULTI_COL_DISPLAY = 1,
+ EXTCAP_GTK_MULTI_COL_VALUE = 2,
+ EXTCAP_GTK_MULTI_NUM_COLS
+};
+
+/* Get a hash map of calls and values from the top widget */
+GHashTable *extcap_gtk_get_state(GtkWidget *widget);
+
+GtkWidget *extcap_create_gtk_rangewidget(extcap_arg *argument,
+ GHashTable *prev_map);
+GtkWidget *extcap_create_gtk_listwidget(extcap_arg *argument,
+ GHashTable *prev_map);
+GtkWidget *extcap_create_gtk_radiowidget(extcap_arg *argument,
+ GHashTable *prev_map);
+GtkWidget *extcap_create_gtk_multicheckwidget(extcap_arg *argument,
+ GHashTable *prev_map);
+
+/*
+ * Populate a (pre-created) container widget based on an arguments record.
+ * For secondary repopulations, a saved state can be passed to populate
+ * with known values. This should occur when setting interface options
+ * repeatedly, for example
+ */
+GSList *extcap_populate_gtk_vbox(GList *arguments, GtkWidget *vbox,
+ GHashTable *prev_map);
+
+/* Free args associated with a GTK item */
+void extcap_gtk_free_args(GtkWidget *vbox);
+
+#endif
+
+/*
+ * Editor modelines - http://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: t
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=4 noexpandtab:
+ * :indentSize=4:tabSize=4:noTabs=false:
+ */
diff --git a/ui/iface_lists.c b/ui/iface_lists.c
index a9dd10c72b..1cf3dfeffb 100644
--- a/ui/iface_lists.c
+++ b/ui/iface_lists.c
@@ -136,6 +136,9 @@ scan_local_interfaces(void (*update_cb)(void))
temp->vendor_description = g_strdup(if_info->vendor_description);
temp->loopback = if_info->loopback;
temp->type = if_info->type;
+#ifdef HAVE_EXTCAP
+ temp->extcap = g_strdup(if_info->extcap);
+#endif
/* Is this interface hidden and, if so, should we include it anyway? */
/* Do we have a user-supplied description? */
@@ -318,6 +321,11 @@ scan_local_interfaces(void (*update_cb)(void))
}
}
}
+
+#ifdef HAVE_EXTCAP
+ /* Extcap devices start with no cached args */
+ device.external_cap_args_settings = NULL;
+#endif
if (global_capture_opts.all_ifaces->len <= count) {
g_array_append_val(global_capture_opts.all_ifaces, device);
count = global_capture_opts.all_ifaces->len;
@@ -374,6 +382,9 @@ scan_local_interfaces(void (*update_cb)(void))
device.if_info.vendor_description = g_strdup(interface_opts.descr);
device.if_info.addrs = NULL;
device.if_info.loopback = FALSE;
+#ifdef HAVE_EXTCAP
+ device.if_info.extcap = g_strdup(interface_opts.extcap);
+#endif
g_array_append_val(global_capture_opts.all_ifaces, device);
global_capture_opts.num_selected++;
diff --git a/ui/qt/QtShark.pro b/ui/qt/QtShark.pro
index f50f94e0ae..291ee8512f 100644
--- a/ui/qt/QtShark.pro
+++ b/ui/qt/QtShark.pro
@@ -192,6 +192,8 @@ SOURCES_WS_C = \
../../capture_opts.c \
../../cfile.c \
../../color_filters.c \
+ ../../extcap.c \
+ ../../extcap_parser.c \
../../file.c \
../../fileset.c \
../../frame_tvbuff.c \
diff --git a/ui/qt/about_dialog.cpp b/ui/qt/about_dialog.cpp
index 231645ee4f..1d54f6690b 100644
--- a/ui/qt/about_dialog.cpp
+++ b/ui/qt/about_dialog.cpp
@@ -239,6 +239,19 @@ AboutDialog::AboutDialog(QWidget *parent) :
g_free(path);
#endif
+#ifdef HAVE_EXTCAP
+ /* Extcap */
+ constpath = get_extcap_dir();
+
+ resultArray = g_strsplit(constpath, G_SEARCHPATH_SEPARATOR_S, 10);
+
+ for(i = 0; resultArray[i]; i++) {
+ message += about_folders_row("Extcap path", g_strstrip(resultArray[i]),
+ "Extcap Plugins search path");
+ }
+ g_strfreev(resultArray);
+#endif
+
message += "</table>";
ui->label_folders->setText(message);
diff --git a/wsutil/filesystem.c b/wsutil/filesystem.c
index 6125236bd7..99b3e5340e 100644
--- a/wsutil/filesystem.c
+++ b/wsutil/filesystem.c
@@ -1062,6 +1062,117 @@ get_plugin_dir(void)
#endif
}
+#if defined(HAVE_EXTCAP)
+/*
+ * Find the directory where the extcap hooks are stored.
+ *
+ * On Windows, we use the "extcap" subdirectory of the datafile directory.
+ *
+ * On UN*X, we use the EXTCAP_DIR value supplied by the configure
+ * script, unless we think we're being run from the build directory,
+ * in which case we use the "extcap" subdirectory of the datafile directory.
+ *
+ * In both cases, we then use the subdirectory of that directory whose
+ * name is the version number.
+ *
+ * XXX - if we think we're being run from the build directory, perhaps we
+ * should have the extcap code not look in the version subdirectory
+ * of the extcap directory, but look in all of the subdirectories
+ * of the extcap directory, so it can just fetch the extcap hooks built
+ * as part of the build process.
+ */
+static const char *extcap_dir = NULL;
+
+static void init_extcap_dir(void) {
+#ifdef _WIN32
+ /*
+ * On Windows, the data file directory is the installation
+ * directory; the extcap hooks are stored under it.
+ *
+ * Assume we're running the installed version of Wireshark;
+ * on Windows, the data file directory is the directory
+ * in which the Wireshark binary resides.
+ */
+ extcap_dir = g_strdup_printf("%s\\extcap\\%s", get_datafile_dir(),
+ VERSION);
+
+ /*
+ * Make sure that pathname refers to a directory.
+ */
+ if (test_for_directory(extcap_dir) != EISDIR) {
+ /*
+ * Either it doesn't refer to a directory or it
+ * refers to something that doesn't exist.
+ *
+ * Assume that means we're running a version of
+ * Wireshark we've built in a build directory,
+ * in which case {datafile dir}\plugins is the
+ * top-level extcap hooks source directory, and use
+ * that directory and set the "we're running in
+ * a build directory" flag, so the plugin
+ * scanner will check all subdirectories of that
+ * directory for extcap hooks.
+ */
+ g_free( (gpointer) extcap_dir);
+ extcap_dir = g_strdup_printf("%s\\extcap", get_datafile_dir());
+ running_in_build_directory_flag = TRUE;
+ }
+#else
+ if (running_in_build_directory_flag) {
+ /*
+ * We're (probably) being run from the build directory and
+ * weren't started with special privileges, so we'll use
+ * the "extcap hooks" subdirectory of the directory where the program
+ * we're running is (that's the build directory).
+ */
+ extcap_dir = g_strdup_printf("%s/extcap", get_progfile_dir());
+ } else {
+ if (getenv("WIRESHARK_EXTCAP_DIR") && !started_with_special_privs()) {
+ /*
+ * The user specified a different directory for extcap hooks
+ * and we aren't running with special privileges.
+ */
+ extcap_dir = g_strdup(getenv("WIRESHARK_EXTCAP_DIR"));
+ }
+#ifdef __APPLE__
+ /*
+ * If we're running from an app bundle and weren't started
+ * with special privileges, use the Contents/Resources/lib/wireshark/extcap
+ * subdirectory of the app bundle.
+ *
+ * (appbundle_dir is not set to a non-null value if we're
+ * started with special privileges, so we need only check
+ * it; we don't need to call started_with_special_privs().)
+ */
+ else if (appbundle_dir != NULL) {
+ extcap_dir = g_strdup_printf("%s/Contents/Resources/lib/wireshark/extcap",
+ appbundle_dir);
+ }
+#endif
+ else {
+ extcap_dir = EXTCAP_DIR;
+ }
+ }
+#endif
+}
+#endif /* HAVE_EXTCAP */
+
+/*
+ * Get the directory in which the extcap hooks are stored.
+ *
+ * XXX - A fix instead of HAVE_EXTCAP must be found
+ */
+const char *
+get_extcap_dir(void) {
+#if defined(HAVE_EXTCAP)
+ if (!extcap_dir)
+ init_extcap_dir();
+ return extcap_dir;
+#else
+ return NULL;
+#endif
+}
+
/*
* Get the flag indicating whether we're running from a build
* directory.
diff --git a/wsutil/filesystem.h b/wsutil/filesystem.h
index 6a5bfe54d0..bb066d37e3 100644
--- a/wsutil/filesystem.h
+++ b/wsutil/filesystem.h
@@ -55,6 +55,13 @@ WS_DLL_PUBLIC const char *get_progfile_dir(void);
WS_DLL_PUBLIC const char *get_plugin_dir(void);
/*
+ * Get the directory in which extcap hooks are stored; this must not be called
+ * before init_progfile_dir() is called, as they might be stored in a
+ * subdirectory of the program file directory.
+ */
+WS_DLL_PUBLIC const char *get_extcap_dir(void);
+
+/*
* Get the flag indicating whether we're running from a build
* directory.
*/