aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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.
*/