OS X: Remove GTK+ packaging.
[metze/wireshark/wip.git] / extcap.c
index a608787dc3e98527f3955abab408e758118c57f9..d60d6aa0b505eb6e4940eb2db8692e1ff4f1fc57 100644 (file)
--- a/extcap.c
+++ b/extcap.c
@@ -40,6 +40,9 @@
 #include <glib.h>
 #include <log.h>
 
+#include <epan/prefs.h>
+#include <epan/prefs-int.h>
+
 #include <wsutil/file_util.h>
 #include <wsutil/filesystem.h>
 #include <wsutil/tempfile.h>
@@ -69,7 +72,7 @@ static GHashTable *ifaces = NULL;
 static GHashTable *tools = NULL;
 
 /* Callback definition for extcap_foreach */
-typedef gboolean (*extcap_cb_t)(const gchar *extcap, gchar *output, void *data,
+typedef gboolean (*extcap_cb_t)(const gchar *extcap, const gchar *ifname, gchar *output, void *data,
         gchar **err_str);
 
 /* #define ARG_DEBUG */
@@ -113,6 +116,16 @@ extcap_if_add(const gchar *ifname, const gchar *extcap)
         g_hash_table_insert(ifaces, g_strdup(ifname), g_strdup(extcap));
 }
 
+static void
+extcap_free_info (gpointer data) {
+    extcap_info * info = (extcap_info *)data;
+
+    g_free (info->basename);
+    g_free (info->full_path);
+    g_free (info->version);
+    g_free (info);
+}
+
 static void
 extcap_tool_add(const gchar *extcap, const extcap_interface *interface)
 {
@@ -174,7 +187,6 @@ static void extcap_foreach(gint argc, gchar **args, extcap_cb_t cb,
             gchar *command_output = NULL;
             gboolean status = FALSE;
             gint exit_status = 0;
-            GError *error = NULL;
             gchar **envp = NULL;
 
             /* full path to extcap binary */
@@ -198,10 +210,10 @@ static void extcap_foreach(gint argc, gchar **args, extcap_cb_t cb,
 
             status = g_spawn_sync(dirname, argv, envp,
                 (GSpawnFlags) 0, NULL, NULL,
-                    &command_output, NULL, &exit_status, &error);
+                    &command_output, NULL, &exit_status, NULL);
 
             if (status && exit_status == 0)
-            keep_going = cb(extcap_path->str, command_output, cb_data, err_str);
+            keep_going = cb(extcap_path->str, ifname, command_output, cb_data, err_str);
 
             g_free(argv[0]);
             g_free(command_output);
@@ -217,7 +229,7 @@ static void extcap_foreach(gint argc, gchar **args, extcap_cb_t cb,
     g_free(argv);
 }
 
-static gboolean dlt_cb(const gchar *extcap _U_, gchar *output, void *data,
+static gboolean dlt_cb(const gchar *extcap _U_, const gchar *ifname _U_, gchar *output, void *data,
         char **err_str) {
     extcap_token_sentence *tokens;
     extcap_dlt *dlts, *dlt_iter, *next;
@@ -280,7 +292,7 @@ extcap_get_if_dlts(const gchar *ifname, char **err_str) {
     gint i;
     if_capabilities_t *caps = NULL;
 
-    if (ifname != NULL && err_str != NULL)
+    if (err_str != NULL)
         *err_str = NULL;
 
     if ( extcap_if_exists(ifname) )
@@ -289,8 +301,6 @@ extcap_get_if_dlts(const gchar *ifname, char **err_str) {
         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)
@@ -300,7 +310,7 @@ extcap_get_if_dlts(const gchar *ifname, char **err_str) {
     return caps;
 }
 
-static gboolean interfaces_cb(const gchar *extcap, gchar *output, void *data,
+static gboolean interfaces_cb(const gchar *extcap, const gchar *ifname _U_, gchar *output, void *data,
         char **err_str _U_) {
     GList **il = (GList **) data;
     extcap_token_sentence *tokens;
@@ -331,14 +341,16 @@ static gboolean interfaces_cb(const gchar *extcap, gchar *output, void *data,
             g_log(LOG_DOMAIN_CAPTURE, G_LOG_LEVEL_DEBUG, "  Extcap [%s] ", int_iter->call);
 
         if ( int_iter->if_type == EXTCAP_SENTENCE_INTERFACE ) {
-            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 (il != NULL) {
+                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->type = IF_EXTCAP;
 
-            if_info->extcap = g_strdup(extcap);
-            *il = g_list_append(*il, if_info);
+                if_info->extcap = g_strdup(extcap);
+                *il = g_list_append(*il, if_info);
+            }
 
             extcap_if_add(int_iter->call, extcap);
         }
@@ -367,32 +379,12 @@ if_info_compare(gconstpointer a, gconstpointer b)
     return comp;
 }
 
-GHashTable *
-extcap_tools_list(void) {
-    if ( tools == NULL || g_hash_table_size(tools) == 0 )
-        extcap_interface_list(NULL);
-
-    return tools;
-}
-
 static void
-extcap_free_info (gpointer data) {
-    extcap_info * info = (extcap_info *)data;
-
-    g_free (info->basename);
-    g_free (info->full_path);
-    g_free (info->version);
-    g_free (info);
-}
-
-GList *
-extcap_interface_list(char **err_str) {
+extcap_reload_interface_list(GList **retp, char **err_str) {
     gchar *argv;
-    /* gint i; */
-    GList *ret = NULL;
 
     if (err_str != NULL)
-    *err_str = NULL;
+        *err_str = NULL;
 
     /* ifaces is used as cache, do not destroy its contents when
      * returning or no extcap interfaces can be queried for options */
@@ -408,13 +400,65 @@ extcap_interface_list(char **err_str) {
 
     argv = g_strdup(EXTCAP_ARGUMENT_LIST_INTERFACES);
 
-    if (err_str)
-    *err_str = NULL;
-    extcap_foreach(1, &argv, interfaces_cb, &ret, err_str, NULL);
+    extcap_foreach(1, &argv, interfaces_cb, retp, err_str, NULL);
 
     g_free(argv);
+}
+
+GHashTable *
+extcap_tools_list(void) {
+    if ( tools == NULL || g_hash_table_size(tools) == 0 )
+        extcap_reload_interface_list(NULL, NULL);
 
-    return g_list_sort ( ret, if_info_compare );
+    return tools;
+}
+
+GList *
+append_extcap_interface_list(GList *list, char **err_str) {
+    GList *ret = NULL;
+    GList *entry;
+    void *data;
+
+    /* Update the extcap interfaces and get a list of their if_infos */
+    extcap_reload_interface_list(&ret, err_str);
+
+    /* Sort that list */
+    ret = g_list_sort(ret, if_info_compare);
+
+    /* Append the interfaces in that list to the list we're handed. */
+    while (ret != NULL) {
+        entry = g_list_first(ret);
+        data = entry->data;
+        ret = g_list_delete_link(ret, entry);
+        list = g_list_append(list, data);
+    }
+    return list;
+}
+
+static void extcap_free_arg_elem(gpointer data, gpointer user_data _U_) {
+    extcap_free_arg((extcap_arg *) data);
+    g_free(data);
+}
+
+void extcap_register_preferences(void)
+{
+    GList * interfaces = NULL;
+
+    module_t * dev_module = prefs_find_module("extcap");
+
+    if ( !dev_module )
+        return;
+
+    if ( ! ifaces || g_hash_table_size(ifaces) == 0 )
+        extcap_reload_interface_list(NULL, NULL);
+
+    interfaces = g_hash_table_get_keys(ifaces);
+
+    while ( interfaces ) {
+        extcap_get_if_configuration((gchar *)interfaces->data);
+
+        interfaces = g_list_next(interfaces);
+    }
 }
 
 static void extcap_free_if_configuration(GList *list)
@@ -426,18 +470,47 @@ static void extcap_free_if_configuration(GList *list)
         if (elem->data != NULL) {
             /* g_list_free_full() only exists since 2.28. */
             sl = g_list_first((GList *)elem->data);
-            g_list_foreach(sl, (GFunc)g_free, NULL);
+            g_list_foreach(sl, (GFunc)extcap_free_arg_elem, NULL);
             g_list_free(sl);
         }
     }
     g_list_free(list);
 }
 
-static gboolean search_cb(const gchar *extcap _U_, gchar *output, void *data,
+gchar * extcap_settings_key(const gchar * ifname, const gchar * setting)
+{
+    gchar * setting_nohyphen;
+    gchar * ifname_underscore;
+    gchar * ifname_lower;
+    gchar * key;
+    GRegex * regex = g_regex_new ("(?![a-zA-Z1-9_]).", (GRegexCompileFlags) 0, (GRegexMatchFlags) 0, NULL );
+
+    if (!regex)
+        return NULL;
+
+    setting_nohyphen =
+        g_regex_replace_literal(regex, setting, strlen(setting), 0,
+            "", (GRegexMatchFlags) 0, NULL );
+    ifname_underscore =
+        g_regex_replace_literal(regex, ifname, strlen(ifname), 0,
+            "_", (GRegexMatchFlags) 0, NULL );
+    ifname_lower = g_utf8_strdown(ifname_underscore, -1);
+    key = g_strconcat(ifname_lower, ".", setting_nohyphen, NULL);
+
+    g_free(setting_nohyphen);
+    g_free(ifname_underscore);
+    g_free(ifname_lower);
+    g_regex_unref(regex);
+
+    return key;
+}
+
+static gboolean search_cb(const gchar *extcap _U_, const gchar *ifname _U_, gchar *output, void *data,
         char **err_str _U_) {
     extcap_token_sentence *tokens = NULL;
     GList *arguments = NULL;
     GList **il = (GList **) data;
+    module_t * dev_module = NULL;
 
     tokens = extcap_tokenize_sentences(output);
     arguments = extcap_parse_args(tokens);
@@ -448,6 +521,37 @@ static gboolean search_cb(const gchar *extcap _U_, gchar *output, void *data,
     extcap_debug_arguments ( arguments );
 #endif
 
+    dev_module = prefs_find_module("extcap");
+
+    if ( dev_module ) {
+        GList * walker = arguments;
+
+        while ( walker != NULL ) {
+            extcap_arg * arg = (extcap_arg *)walker->data;
+
+            if ( arg->save ) {
+                struct preference * pref = NULL;
+                gchar * pref_ifname = extcap_settings_key(ifname, arg->call);
+
+                if ( ( pref = prefs_find_preference(dev_module, pref_ifname) ) == NULL ) {
+                    /* Set an initial value */
+                    if ( ! arg->storeval && arg->default_complex )
+                        arg->storeval = g_strdup(arg->default_complex->_val);
+
+                    prefs_register_string_preference(dev_module, g_strdup(pref_ifname),
+                            arg->display, arg->display, (const gchar **)&(arg->storeval));
+                } else {
+                    /* Been here before, restore stored value */
+                    if (! arg->storeval && pref->varp.string)
+                        arg->storeval = g_strdup(*(pref->varp.string));
+                    }
+                g_free(pref_ifname);
+            }
+
+            walker = g_list_next(walker);
+        }
+    }
+
     *il = g_list_append(*il, arguments);
 
     /* By returning false, extcap_foreach will break on first found */
@@ -489,22 +593,45 @@ extcap_has_configuration(const char * ifname, gboolean is_required) {
     arguments = extcap_get_if_configuration((const char *)( ifname ) );
     walker = g_list_first(arguments);
 
-    while ( walker != NULL && ! found )
-    {
+    while ( walker != NULL && ! found ) {
         item = g_list_first((GList *)(walker->data));
-        while ( item != NULL && ! found )
-        {
-            if ( (extcap_arg *)(item->data) != NULL )
-            {
+        while ( item != NULL && ! found ) {
+            if ( (extcap_arg *)(item->data) != NULL ) {
+                extcap_arg * arg = (extcap_arg *)(item->data);
                 /* Should required options be present, or any kind of options */
-                if ( ! is_required || ((extcap_arg *)(item->data))->is_required )
+                if ( ! is_required )
                     found = TRUE;
+                else if ( arg->is_required ) {
+                    gchar * stored = NULL;
+                    gchar * defval = NULL;
+
+                    if ( arg->storeval != NULL )
+                        stored = arg->storeval;
+
+                    if ( arg->default_complex != NULL && arg->default_complex->_val != NULL )
+                        defval = arg->default_complex->_val;
+
+                    if ( arg->is_required ) {
+                        /* If stored and defval is identical and the argument is required,
+                         * configuration is needed */
+                        if ( defval && stored && g_strcmp0(stored, defval) == 0 )
+                            found = TRUE;
+                        else if ( ! defval && (!stored || strlen(g_strchomp(stored)) <= (size_t)0) )
+                            found = TRUE;
+                    }
+
+                    if ( arg->arg_type == EXTCAP_ARG_FILESELECT ) {
+                        if ( arg->fileexists && ! ( file_exists(defval) || file_exists(stored) ) )
+                            found = TRUE;
+                    }
+                }
             }
 
             item = item->next;
         }
         walker = walker->next;
     }
+    extcap_free_if_configuration(arguments);
 
     return found;
 }
@@ -547,12 +674,6 @@ void extcap_cleanup(capture_options * capture_opts) {
                 "Extcap [%s] - Closing spawned PID: %d", interface_opts.name,
                 interface_opts.extcap_pid);
 
-        if (interface_opts.extcap_child_watch > 0)
-        {
-            g_source_remove(interface_opts.extcap_child_watch);
-            interface_opts.extcap_child_watch = 0;
-        }
-
         if (interface_opts.extcap_pid != INVALID_EXTCAP_PID)
         {
 #ifdef _WIN32
@@ -568,8 +689,8 @@ void extcap_cleanup(capture_options * capture_opts) {
     }
 }
 
-static void
-extcap_arg_cb(gpointer key, gpointer value, gpointer data) {
+static gboolean
+extcap_add_arg_and_remove_cb(gpointer key, gpointer value, gpointer data) {
     GPtrArray *args = (GPtrArray *)data;
 
     if ( key != NULL )
@@ -578,7 +699,11 @@ extcap_arg_cb(gpointer key, gpointer value, gpointer data) {
 
         if ( value != NULL )
             g_ptr_array_add(args, g_strdup((const gchar*)value));
+
+        return TRUE;
     }
+
+    return FALSE;
 }
 
 static void extcap_child_watch_cb(GPid pid, gint status _U_, gpointer user_data)
@@ -597,7 +722,9 @@ static void extcap_child_watch_cb(GPid pid, gint status _U_, gpointer user_data)
         if (interface_opts.extcap_pid == pid)
         {
             interface_opts.extcap_pid = INVALID_EXTCAP_PID;
+            g_source_remove(interface_opts.extcap_child_watch);
             interface_opts.extcap_child_watch = 0;
+
             capture_opts->ifaces = g_array_remove_index(capture_opts->ifaces, i);
             g_array_insert_val(capture_opts->ifaces, i, interface_opts);
             break;
@@ -644,7 +771,7 @@ extcap_init_interfaces(capture_options *capture_opts)
         }
         add_arg(EXTCAP_ARGUMENT_RUN_PIPE);
         add_arg(interface_opts.extcap_fifo);
-        if (interface_opts.extcap_args == NULL)
+        if (interface_opts.extcap_args == NULL || g_hash_table_size(interface_opts.extcap_args) == 0)
         {
             /* User did not perform interface configuration.
              *
@@ -666,18 +793,29 @@ extcap_init_interfaces(capture_options *capture_opts)
                 }
 
                 arg_list = g_list_first((GList *)elem->data);
-                while (arg_list != NULL)
-                {
+                while (arg_list != NULL) {
+                    gchar * stored = NULL, * defval = NULL;
                     /* In case of boolflags only first element in arg_list is relevant. */
                     arg_iter = (extcap_arg*) (arg_list->data);
-
-                    if  (arg_iter->arg_type == EXTCAP_ARG_BOOLFLAG)
-                    {
-                        if (arg_iter->default_complex != NULL
-                            && extcap_complex_get_bool(arg_iter->default_complex))
-                        {
-                            add_arg(arg_iter->call);
+                    if ( arg_iter->storeval != NULL )
+                        stored = arg_iter->storeval;
+
+                    if ( arg_iter->default_complex != NULL && arg_iter->default_complex->_val != NULL )
+                        defval = arg_iter->default_complex->_val;
+
+                    /* Different data in storage then set for default */
+                    if ( g_strcmp0(stored, defval) != 0 ) {
+                        if ( arg_iter->arg_type == EXTCAP_ARG_BOOLFLAG ) {
+                            if ( g_strcmp0(stored, "true") == 0 )
+                                add_arg(arg_iter->call);
+                        } else {
+                            gchar * call = g_strconcat(arg_iter->call, " ", stored, NULL);
+                            add_arg(call);
+                            g_free(call);
                         }
+                    } else if  (arg_iter->arg_type == EXTCAP_ARG_BOOLFLAG) {
+                        if (extcap_complex_get_bool(arg_iter->default_complex))
+                            add_arg(arg_iter->call);
                     }
 
                     arg_list = arg_list->next;
@@ -688,7 +826,7 @@ extcap_init_interfaces(capture_options *capture_opts)
         }
         else
         {
-            g_hash_table_foreach(interface_opts.extcap_args, extcap_arg_cb, args);
+            g_hash_table_foreach_remove(interface_opts.extcap_args, extcap_add_arg_and_remove_cb, args);
         }
         add_arg(NULL);
 #undef add_arg