/*
     This file is part of GNUnet.
     Copyright (C) 2010-2014, 2016 GNUnet e.V.

     GNUnet is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published
     by the Free Software Foundation; either version 3, or (at your
     option) any later version.

     GNUnet is distributed in the hope that it will be useful, but
     WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with GNUnet; see the file COPYING.  If not, write to the
     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     Boston, MA 02110-1301, USA.
*/

/**
 * @file src/setup/gnunet-setup-options.c
 * @brief configuration details
 * @author Christian Grothoff
 */
#include "gnunet-setup.h"
#include "gnunet-setup-options.h"
#include "gnunet-setup-exit-services.h"
#include <gnunet/gnunet_util_lib.h>
#include <gdk/gdkkeysyms.h>


/**
 * Regular expression for YES
 */
#define REX_YES "^YES$"

/**
 * Regular expression for NO
 */
#define REX_NO "^NO$"


/**
 * Structs of this type specify connection between widgets storing
 * port numbers and widgets controlling status of respective transport.
 */
struct PortSpecification
{
  /**
   * Name of the GtkSpinButton storing the port value.
   */
  const char *spinbutton_name;

  /**
   * Name of the GtkCheckButton controlling whether the respective
   * transport/daemon is enabled.
   */
  const char *checkbutton_name;

  /**
   * Name of the transport/daemon which owns this port.
   */
  const char *owner_name;
};


/**
 * Initialize a toggle button based on an options 'yes/no' value.
 *
 * @param cls closure
 * @param section section with the value
 * @param option option name
 * @param value value as a string
 * @param widget widget to initialize
 * @param cfg configuration handle
 * @return #GNUNET_OK on success, #GNUNET_SYSERR if there was a problem
 */
static int
load_yes_no (const void *cls,
             const char *section,
             const char *option,
             const char *value,
             GObject *widget,
             const struct GNUNET_CONFIGURATION_Handle *cfg)
{
  GtkToggleButton *button;

  button = GTK_TOGGLE_BUTTON (widget);
  if (NULL == button)
    return GNUNET_SYSERR;
  gtk_toggle_button_set_active (button,
                                (0 == strcasecmp (value, "YES")) ? TRUE
                                                                 : FALSE);
  return GNUNET_OK;
}


/**
 * Set a yes/no option based on a toggle button.
 *
 * @param cls closure
 * @param section section with the value
 * @param option option name
 * @param widget widget to initialize
 * @param cfg configuration handle to update
 * @return #GNUNET_OK on success, #GNUNET_SYSERR if there was a problem
 */
static int
save_yes_no (const void *cls,
             const char *section,
             const char *option,
             GObject *widget,
             struct GNUNET_CONFIGURATION_Handle *cfg)
{
  GtkToggleButton *button;
  gboolean mode;

  button = GTK_TOGGLE_BUTTON (widget);
  if (button == NULL)
    return GNUNET_SYSERR;
  mode = gtk_toggle_button_get_active (button);
  GNUNET_CONFIGURATION_set_value_string (cfg,
                                         section,
                                         option,
                                         mode == TRUE ? "YES" : "NO");
  return GNUNET_OK;
}


/**
 * Initialize a GtkFileChooser based on a filename option.
 *
 * @param cls closure
 * @param section section with the value
 * @param option option name
 * @param value value as a string
 * @param widget widget to initialize
 * @param cfg configuration handle
 * @return #GNUNET_OK on success, #GNUNET_SYSERR if there was a problem
 */
static int
load_filename (const void *cls,
               const char *section,
               const char *option,
               const char *value,
               GObject *widget,
               const struct GNUNET_CONFIGURATION_Handle *cfg)
{
  GtkFileChooser *chooser;

  chooser = GTK_FILE_CHOOSER (widget);
  if (chooser == NULL)
    return GNUNET_SYSERR;
  gtk_file_chooser_set_filename (chooser, value);
  return GNUNET_OK;
}


/**
 * Set filename option based on a file chooser.
 *
 * @param cls closure
 * @param section section with the value
 * @param option option name
 * @param widget widget to initialize
 * @param cfg configuration handle to update
 * @return #GNUNET_OK on success, #GNUNET_SYSERR if there was a problem
 */
static int
save_filename (const void *cls,
               const char *section,
               const char *option,
               GObject *widget,
               struct GNUNET_CONFIGURATION_Handle *cfg)
{
  GtkFileChooser *chooser;
  gchar *fn;

  chooser = GTK_FILE_CHOOSER (widget);
  if (NULL == chooser)
    return GNUNET_SYSERR;
  fn = gtk_file_chooser_get_filename (chooser);
  if (fn == NULL)
    fn = g_strdup ("");
  GNUNET_CONFIGURATION_set_value_string (cfg, section, option, fn);
  g_free (fn);
  return GNUNET_OK;
}


/**
 * Initialize a GtkEntry based on a text option.
 *
 * @param cls closure
 * @param section section with the value
 * @param option option name
 * @param value value as a string
 * @param widget widget to initialize
 * @param cfg configuration handle
 * @return #GNUNET_OK on success, #GNUNET_SYSERR if there was a problem
 */
static int
load_text (const void *cls,
           const char *section,
           const char *option,
           const char *value,
           GObject *widget,
           const struct GNUNET_CONFIGURATION_Handle *cfg)
{
  GtkEntry *entry;

  entry = GTK_ENTRY (widget);
  if (NULL == entry)
    return GNUNET_SYSERR;
  gtk_entry_set_text (entry, value);
  return GNUNET_OK;
}


/**
 * Set text option based on a GtkEntry.
 *
 * @param cls closure
 * @param section section with the value
 * @param option option name
 * @param widget widget to initialize
 * @param cfg configuration handle to update
 * @return #GNUNET_OK on success, #GNUNET_SYSERR if there was a problem
 */
static int
save_text (const void *cls,
           const char *section,
           const char *option,
           GObject *widget,
           struct GNUNET_CONFIGURATION_Handle *cfg)
{
  GtkEntry *entry;
  const gchar *txt;

  entry = GTK_ENTRY (widget);
  if (NULL == entry)
    return GNUNET_SYSERR;
  txt = gtk_entry_get_text (entry);
  GNUNET_CONFIGURATION_set_value_string (cfg, section, option, txt);
  return GNUNET_OK;
}


#if 0
/**
 * Initialize a GtkComboBox based on a text option.
 *
 * @param cls closure
 * @param section section with the value
 * @param option option name
 * @param value value as a string
 * @param widget widget to initialize
 * @param cfg configuration handle
 * @return #GNUNET_OK on success, #GNUNET_SYSERR if there was a problem
 */
static int
load_combo_text (const void *cls,
		 const char *section,
		 const char *option,
                 const char *value,
		 GObject * widget,
                 const struct GNUNET_CONFIGURATION_Handle *cfg)
{
  GtkComboBoxText *cb;

  cb = GTK_COMBO_BOX_TEXT (widget);
  if (NULL == cb)
    return GNUNET_SYSERR;
  gtk_combo_box_text_append_text (cb, value);
  return GNUNET_OK;
}


/**
 * Set text option based on a GtkComboBox.
 *
 * @param cls closure
 * @param section section with the value
 * @param option option name
 * @param widget widget to initialize
 * @param cfg configuration handle to update
 * @return #GNUNET_OK on success, #GNUNET_SYSERR if there was a problem
 */
static int
save_combo_text (const void *cls,
		 const char *section,
		 const char *option,
                 GObject * widget,
		 struct GNUNET_CONFIGURATION_Handle *cfg)
{
  GtkComboBox *cb;
  gchar *c;

  cb = GTK_COMBO_BOX (widget);
  if (NULL == cb)
    return GNUNET_SYSERR;
  c = gtk_combo_box_get_active_text (cb);
  GNUNET_CONFIGURATION_set_value_string (cfg, section, option, c);
  g_free (c);
  return GNUNET_OK;
}
#endif


/**
 * Initialize a GtkSpinButton based on a numeric option.
 *
 * @param cls closure
 * @param section section with the value
 * @param option option name
 * @param value value as a string
 * @param widget widget to initialize
 * @param cfg configuration handle
 * @return #GNUNET_OK on success, #GNUNET_SYSERR if there was a problem
 */
static int
load_number (const void *cls,
             const char *section,
             const char *option,
             const char *value,
             GObject *widget,
             const struct GNUNET_CONFIGURATION_Handle *cfg)
{
  GtkSpinButton *spin;
  unsigned int number;

  spin = GTK_SPIN_BUTTON (widget);
  if (spin == NULL)
    return GNUNET_SYSERR;
  if (1 != sscanf (value, "%u", &number))
    return GNUNET_SYSERR;
  gtk_spin_button_set_value (spin, number);
  return GNUNET_OK;
}


/**
 * Set numeric option based on a spin button.
 *
 * @param cls closure
 * @param section section with the value
 * @param option option name
 * @param widget widget to initialize
 * @param cfg configuration handle to update
 * @return #GNUNET_OK on success, #GNUNET_SYSERR if there was a problem
 */
static int
save_number (const void *cls,
             const char *section,
             const char *option,
             GObject *widget,
             struct GNUNET_CONFIGURATION_Handle *cfg)
{
  GtkSpinButton *spin;

  spin = GTK_SPIN_BUTTON (widget);
  if (spin == NULL)
    return GNUNET_SYSERR;
  GNUNET_CONFIGURATION_set_value_number (cfg,
                                         section,
                                         option,
                                         gtk_spin_button_get_value_as_int (
                                           spin));
  return GNUNET_OK;
}


/**
 * NULL-terminated list of port specifications, which should be checked
 * for collisions.
 */
static struct PortSpecification port_specifications[] =
  {{
     "GNUNET_setup_hostlist_server_port_spin_button",
     "GNUNET_setup_hostlist_offer_hostlist_checkbutton",
     gettext_noop ("the hostlist server"),
   },
   {
     "GNUNET_setup_transport_tcp_port_spinbutton",
     "GNUNET_setup_transport_tcp_checkbutton",
     gettext_noop ("the TCP transport plugin"),
   },
   {
     "GNUNET_setup_transport_http_port_spinbutton",
     "GNUNET_setup_transport_http_server_checkbutton",
     gettext_noop ("the HTTP transport plugin"),
   },
   {
     "GNUNET_setup_transport_https_port_spinbutton",
     "GNUNET_setup_transport_https_server_checkbutton",
     gettext_noop ("the HTTPS transport plugin"),
   },
   {NULL, NULL}};


/**
 * Find spinbutton associated with a port specification.
 *
 * @param i index of the respective port specification
 * @return spinbutton or NULL in case of an error or if respecitve transport is
 * disabled
 */
static GtkSpinButton *
get_port_spinbutton (unsigned int i)
{
  const char *sb_name;
  const char *cb_name;
  GtkWidget *widget;
  GtkToggleButton *checkbt;
  GtkSpinButton *spinbt;
  gboolean mode;

  if ((NULL == port_specifications[i].spinbutton_name) ||
      (NULL == port_specifications[i].checkbutton_name))
    return NULL;
  cb_name = port_specifications[i].checkbutton_name;
  widget = GTK_WIDGET (GNUNET_SETUP_get_object (cb_name));
  if (widget == NULL)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                _ ("Widget `%s' not found\n"),
                cb_name);
    return NULL;
  }
  checkbt = GTK_TOGGLE_BUTTON (widget);
  if (checkbt == NULL)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                _ ("Specified widget `%s' is not a checkbutton\n"),
                cb_name);
    return NULL;
  }
  mode = gtk_toggle_button_get_active (checkbt);
  if (TRUE != mode)
    return NULL;

  sb_name = port_specifications[i].spinbutton_name;
  widget = GTK_WIDGET (GNUNET_SETUP_get_object (sb_name));
  if (widget == NULL)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                _ ("Widget `%s' not found\n"),
                sb_name);
    return NULL;
  }
  spinbt = GTK_SPIN_BUTTON (widget);
  if (spinbt == NULL)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                _ ("Specified widget `%s' is not a spinbutton\n"),
                sb_name);
    return NULL;
  }
  return spinbt;
}


/**
 * Check port numbers for collisions and highlight conflicting ports, if any.
 *
 * @param cls closure (unused)
 * @param widget widget whose state was changed (unused)
 */
static void
highlight_port_collisions (const void *cls, GObject *widget)
{
  static GtkCssProvider *conflict_provider;
  unsigned int i;
  unsigned int j;
  unsigned int port;
  GtkSpinButton *spinbt_i;
  GtkSpinButton *spinbt_j;
  int found;
  char *tooltip;

  if (NULL == conflict_provider)
  {
    conflict_provider = gtk_css_provider_new ();

    gtk_css_provider_load_from_data (
      conflict_provider,
      ".conflict {\n color: rgba(255, 0.0, 0.0, 255);\n}",
      -1,
      NULL);
    for (i = 0; NULL != port_specifications[i].spinbutton_name; i++)
    {
      spinbt_i = get_port_spinbutton (i);
      if (NULL == spinbt_i)
        continue;
      gtk_style_context_add_provider (gtk_widget_get_style_context (
                                        GTK_WIDGET (spinbt_i)),
                                      GTK_STYLE_PROVIDER (conflict_provider),
                                      GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
    }
  }
  for (i = 0; NULL != port_specifications[i].spinbutton_name; i++)
  {
    spinbt_i = get_port_spinbutton (i);
    if (NULL == spinbt_i)
      continue;
    port = gtk_spin_button_get_value_as_int (spinbt_i);

    found = GNUNET_NO;
    for (j = 0; NULL != port_specifications[j].spinbutton_name; j++)
    {
      if (i == j)
        continue;
      spinbt_j = get_port_spinbutton (j);
      if (NULL == spinbt_j)
        continue;
      if (port == gtk_spin_button_get_value_as_int (spinbt_j))
      {
        found = GNUNET_YES;
        break;
      }
    }
    if (GNUNET_YES == found)
    {
      gtk_style_context_add_class (gtk_widget_get_style_context (
                                     GTK_WIDGET (spinbt_i)),
                                   "conflict");
      GNUNET_asprintf (&tooltip,
                       _ ("This port is already occupied by %s."),
                       port_specifications[j].owner_name);
      gtk_widget_set_tooltip_text (GTK_WIDGET (spinbt_i), tooltip);
      GNUNET_free (tooltip);
    }
    else
    {
      gtk_style_context_remove_class (gtk_widget_get_style_context (
                                        GTK_WIDGET (spinbt_i)),
                                      "conflict");
      gtk_widget_set_tooltip_text (GTK_WIDGET (spinbt_i), "");
    }
  }
}


/**
 * Check IPv4 exit policy for syntactic correctness.
 *
 * @param cls closure (unused)
 * @param widget widget whose state was changed
 */
static void
validate_v4_policy (const void *cls, GObject *widget)
{
  static GtkCssProvider *invalid_syntax_provider;
  GtkEntry *entry;
  gboolean invalid;
  const char *text;
  char *r;
  char *tooltip;

  entry = GTK_ENTRY (widget);
  text = gtk_entry_get_text (entry);
  r = GNUNET_TUN_ipv4policy2regex (text);
  invalid =
    ((strlen (text) > 0) && ((NULL == r) || (text[strlen (text) - 1] != ';')));
  if (invalid)
  {
    if (NULL == invalid_syntax_provider)
    {
      invalid_syntax_provider = gtk_css_provider_new ();

      gtk_css_provider_load_from_data (
        invalid_syntax_provider,
        ".syntax_error {\n color: rgba(255, 0.0, 0.0, 255);\n}",
        -1,
        NULL);
      gtk_style_context_add_provider (gtk_widget_get_style_context (
                                        GTK_WIDGET (entry)),
                                      GTK_STYLE_PROVIDER (
                                        invalid_syntax_provider),
                                      GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
    }
    gtk_style_context_add_class (gtk_widget_get_style_context (
                                   GTK_WIDGET (entry)),
                                 "syntax_error");
    GNUNET_asprintf (&tooltip, _ ("Invalid policy."));
    gtk_widget_set_tooltip_text (GTK_WIDGET (widget), tooltip);
    GNUNET_free (tooltip);
  }
  else
  {
    if (NULL != invalid_syntax_provider)
    {
      gtk_style_context_remove_class (gtk_widget_get_style_context (
                                        GTK_WIDGET (entry)),
                                      "syntax_error");
    }
    gtk_widget_set_tooltip_text (GTK_WIDGET (widget), "");
  }
  GNUNET_free (r);
}


/**
 * Check IPv6 exit policy for syntactic correctness.
 *
 * @param cls closure (unused)
 * @param widget widget whose state was changed
 */
static void
validate_v6_policy (const void *cls, GObject *widget)
{
  static GtkCssProvider *invalid_syntax_provider;
  GtkEntry *entry;
  gboolean invalid;
  const char *text;
  char *r;
  char *tooltip;

  entry = GTK_ENTRY (widget);
  text = gtk_entry_get_text (entry);
  r = GNUNET_TUN_ipv6policy2regex (text);
  invalid =
    ((strlen (text) > 0) && ((NULL == r) || (text[strlen (text) - 1] != ';')));
  if (invalid)
  {
    if (NULL == invalid_syntax_provider)
    {
      invalid_syntax_provider = gtk_css_provider_new ();
      gtk_css_provider_load_from_data (
        invalid_syntax_provider,
        ".syntax_error {\n color: rgba(255, 0.0, 0.0, 255);\n}",
        -1,
        NULL);
      gtk_style_context_add_provider (gtk_widget_get_style_context (
                                        GTK_WIDGET (entry)),
                                      GTK_STYLE_PROVIDER (
                                        invalid_syntax_provider),
                                      GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
    }
    gtk_style_context_add_class (gtk_widget_get_style_context (
                                   GTK_WIDGET (entry)),
                                 "syntax_error");
    GNUNET_asprintf (&tooltip, _ ("Invalid policy."));
    gtk_widget_set_tooltip_text (GTK_WIDGET (widget), tooltip);
    GNUNET_free (tooltip);
  }
  else
  {
    if (NULL != invalid_syntax_provider)
    {
      gtk_style_context_remove_class (gtk_widget_get_style_context (
                                        GTK_WIDGET (entry)),
                                      "syntax_error");
    }
    gtk_widget_set_tooltip_text (GTK_WIDGET (widget), "");
  }
  GNUNET_free (r);
}


/**
 * Initialize a toggle button based on the existence of a word
 * in an option value.
 *
 * @param cls word to test for (conat char *)
 * @param section section with the value
 * @param option option name
 * @param value value as a string
 * @param widget widget to initialize
 * @param cfg configuration handle
 * @return #GNUNET_OK on success, #GNUNET_SYSERR if there was a problem
 */
static int
load_option_list (const void *cls,
                  const char *section,
                  const char *option,
                  const char *value,
                  GObject *widget,
                  const struct GNUNET_CONFIGURATION_Handle *cfg)
{
  const char *word = cls;
  char *t;
  char *w;
  GtkToggleButton *button;
  int found;

  button = GTK_TOGGLE_BUTTON (widget);
  if (button == NULL)
    return GNUNET_SYSERR;
  found = GNUNET_NO;
  t = GNUNET_strdup (value);
  w = strtok (t, " ");
  while (w != NULL)
  {
    if (0 == strcmp (w, word))
    {
      found = GNUNET_YES;
      break;
    }
    w = strtok (NULL, " ");
  }
  GNUNET_free (t);
  gtk_toggle_button_set_active (button, found);
  return GNUNET_OK;
}


/**
 * Add or remove a word from a sentence based on a toggle button's yes/no value.
 *
 * @param cls  word to add or remove for (conat char *)
 * @param section section with the value
 * @param option option name
 * @param widget widget to initialize
 * @param cfg configuration handle to update
 * @return #GNUNET_OK on success, #GNUNET_SYSERR if there was a problem
 */
static int
save_option_list (const void *cls,
                  const char *section,
                  const char *option,
                  GObject *widget,
                  struct GNUNET_CONFIGURATION_Handle *cfg)
{
  const char *word = cls;
  GtkToggleButton *button;
  gboolean mode;

  button = GTK_TOGGLE_BUTTON (widget);
  if (button == NULL)
    return GNUNET_SYSERR;
  mode = gtk_toggle_button_get_active (button);
  if (mode == TRUE)
    GNUNET_CONFIGURATION_append_value_filename (cfg, section, option, word);
  else
    GNUNET_CONFIGURATION_remove_value_filename (cfg, section, option, word);
  return GNUNET_OK;
}


/**
 * User pressed a key in a sensitive tree view with a list store.
 * Check if it was the 'delete' key and if so remove the selected
 * row.
 *
 * @param tv tree view emitting the signal
 * @param event key stroke data
 * @param user_data not used
 * @return TRUE to stop other handlers from being invoked
 */
gboolean
GNUNET_setup_treeview_key_press_event_cb (GtkTreeView *tv,
                                          GdkEventKey *event,
                                          gpointer user_data)
{
  GtkListStore *ls;
  GtkTreeModel *tm;
  GtkTreeSelection *sel;
  GtkTreeIter iter;
  gboolean editable;

  if ((event->type != GDK_KEY_PRESS) || (event->state != 0) ||
#ifdef GDK_Delete
      (event->keyval != GDK_Delete)
#else
      (event->keyval != GDK_KEY_Delete)
#endif
  )
    return FALSE;
  ls = GTK_LIST_STORE (gtk_tree_view_get_model (tv));
  if (ls == NULL)
  {
    GNUNET_break (0);
    return FALSE;
  }
  sel = gtk_tree_view_get_selection (tv);
  if (TRUE != gtk_tree_selection_get_selected (sel, &tm, &iter))
    return FALSE;
  gtk_tree_model_get (tm,
                      &iter,
                      GNUNET_GTK_SETUP_HOSTLIST_URL_MC_EDITABLE,
                      &editable,
                      -1);
  if (TRUE == editable)
    return FALSE; /* likely currently editing... */
  gtk_list_store_remove (ls, &iter);
  if (TRUE == gtk_tree_model_get_iter_first (tm, &iter))
    gtk_tree_selection_select_iter (sel, &iter);
  return FALSE;
}


/**
 * Initialize a GtkListStore by tokenizing the value into strings.
 *
 * @param cls closure (unused)
 * @param section section with the value
 * @param option option name
 * @param value value as a string
 * @param widget widget to initialize
 * @param cfg configuration handle
 * @return #GNUNET_OK on success, #GNUNET_SYSERR if there was a problem
 */
static int
load_string_list_store (const void *cls,
                        const char *section,
                        const char *option,
                        const char *value,
                        GObject *widget,
                        const struct GNUNET_CONFIGURATION_Handle *cfg)
{
  char *t;
  char *w;
  GtkListStore *ls;
  GtkTreeIter iter;

  ls = GTK_LIST_STORE (widget);
  if (ls == NULL)
    return GNUNET_SYSERR;
  t = GNUNET_strdup (value);
  for (w = strtok (t, " "); w != NULL; w = strtok (NULL, " "))
    gtk_list_store_insert_with_values (ls,
                                       &iter,
                                       G_MAXINT,
                                       GNUNET_GTK_SETUP_HOSTLIST_URL_MC_URL,
                                       w,
                                       GNUNET_GTK_SETUP_HOSTLIST_URL_MC_EDITABLE,
                                       FALSE,
                                       -1);
  GNUNET_free (t);
  gtk_list_store_insert_with_values (ls,
                                     &iter,
                                     G_MAXINT,
                                     GNUNET_GTK_SETUP_HOSTLIST_URL_MC_URL,
                                     "",
                                     GNUNET_GTK_SETUP_HOSTLIST_URL_MC_EDITABLE,
                                     TRUE,
                                     -1);
  return GNUNET_OK;
}


/**
 * The GtkCellRenderer has emmited the 'edited' signal.
 *
 *
 * @param cls closure (unused)
 * @param section section with the value (NULL)
 * @param option option name (NULL)
 * @param widget the cell renderer
 * @param cfg configuration handle to update
 * @return #GNUNET_OK on success, #GNUNET_SYSERR if there was a problem
 */
static int
save_string_list_store (const void *cls,
                        const char *section,
                        const char *option,
                        GObject *widget,
                        struct GNUNET_CONFIGURATION_Handle *cfg)
{
  GtkTreeModel *tm;
  GtkTreeIter iter;
  char *value;
  char *n;
  gchar *val;

  tm = GTK_TREE_MODEL (widget);
  if (tm == NULL)
    return GNUNET_SYSERR;
  value = NULL;
  if (TRUE == gtk_tree_model_get_iter_first (tm, &iter))
  {
    do
    {
      gtk_tree_model_get (tm,
                          &iter,
                          GNUNET_GTK_SETUP_HOSTLIST_URL_MC_URL,
                          &val,
                          -1);
      if (0 < strlen (val))
      {
        if (value == NULL)
        {
          value = GNUNET_strdup (val);
        }
        else
        {
          GNUNET_asprintf (&n, "%s %s", value, val);
          GNUNET_free (value);
          value = n;
        }
      }
      g_free (val);
    } while (TRUE == gtk_tree_model_iter_next (tm, &iter));
  }
  if (value == NULL)
    value = GNUNET_strdup ("");
  GNUNET_CONFIGURATION_set_value_string (cfg, section, option, value);
  GNUNET_free (value);
  return GNUNET_OK;
}


/**
 * Hide "min connected friends" option if in F2F-only mode.
 */
static struct GNUNET_SETUP_VisibilitySpecification
  hide_min_connected_friends[] =
    {{"GNUNET_setup_minimum_friends_label", NULL, REX_YES},
     {"GNUNET_setup_minimum_friends_spinbutton", NULL, REX_YES},
     {NULL, NULL, NULL}};


/**
 * Hide "hostlist" options if hostlist is not in use.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_hostlist_tab[] =
  {{"GNUNET_setup_hostlist_vbox", "YES", NULL},
   {"GNUNET_setup_hostlist_label", "YES", NULL},
   {NULL, NULL, NULL}};


/**
 * Hide "exit" options if VPN exit is not in use.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_exit_options[] =
  {{"GNUNET_setup_exit_label", "YES", NULL},
   {"GNUNET_setup_exit_vbox", "YES", NULL},
   {NULL, NULL, NULL}};


/**
 * Hide DNS "exit" options if DNS exit is not in use.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_dns_exit_options[] =
  {{"GNUNET_setup_dns_resolver_hbox", "YES", NULL}, {NULL, NULL, NULL}};


/**
 * Hide IPv4 "exit" options if IPv4 exit is not in use.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_v4_exit_options[] =
  {{"GNUNET_SETUP_exit_policy_v4_hbox", "YES", NULL}, {NULL, NULL, NULL}};


/**
 * Hide IPv6 "exit" options if IPv6 exit is not in use.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_v6_exit_options[] =
  {{"GNUNET_SETUP_exit_policy_v6_hbox", "YES", NULL}, {NULL, NULL, NULL}};


/**
 * Hide "hostlist" server options if hostlist server is not in use.
 */
static struct GNUNET_SETUP_VisibilitySpecification
  hide_hostlist_server_options[] =
    {{"GNUNET_setup_hostlist_advertise_checkbutton", "(^| )-p($| )", NULL},
     {"GNUNET_setup_hostlist_port_label", "(^| )-p($| )", NULL},
     {"GNUNET_setup_hostlist_server_port_spin_button", "(^| )-p($| )", NULL},
     {NULL, NULL, NULL}};


/**
 * Hide "hostlist" proxy options if proxy is not in use.
 */
static struct GNUNET_SETUP_VisibilitySpecification
  hide_hostlist_proxy_options[] =
    {{"GNUNET_setup_hostlist_proxy_hostname_hbox", NULL, "NONE"},
     {"GNUNET_setup_hostlist_proxy_username_hbox", NULL, "NONE"},
     {"GNUNET_setup_hostlist_proxy_password_hbox", NULL, "NONE"},
     {NULL, NULL, NULL}};


/**
 * Hide "http-client" proxy options if proxy is not in use.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_http_proxy_options[] =
  {{"GNUNET_setup_transport_http_client_proxy_hostname_hbox", NULL, "NONE"},
   {"GNUNET_setup_transport_http_client_proxy_username_hbox", NULL, "NONE"},
   {"GNUNET_setup_transport_http_client_proxy_password_hbox", NULL, "NONE"},
   {NULL, NULL, NULL}};

/**
 * Hide "https-client" proxy options if proxy is not in use.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_https_proxy_options[] =
  {{"GNUNET_setup_transport_https_client_proxy_hostname_hbox", NULL, "NONE"},
   {"GNUNET_setup_transport_https_client_proxy_username_hbox", NULL, "NONE"},
   {"GNUNET_setup_transport_https_client_proxy_password_hbox", NULL, "NONE"},
   {NULL, NULL, NULL}};

/**
 * Hide "fs" tab if FS not active.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_fs_tab[] =
  {{"GNUNET_setup_fs_main_vbox", "YES", NULL}, {NULL, NULL, NULL}};


/**
 * Hide "vpn" tab and "pt" checkbutton if VPN is not active.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_vpn_options[] =
  {{"GNUNET_setup_general_services_pt_checkbutton", "YES", NULL},
   {"GNUNET_setup_vpn_vbox", "YES", NULL},
   {"GNUNET_setup_vpn_label", "YES", NULL},
   {NULL, NULL, NULL}};


/**
 * Hides "vpn" option if PT is active (so that it cannot be disabled),
 * and shows PT-specific options.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_pt_options[] =
  {{"GNUNET_setup_pt_frame", "YES", NULL},
   {"GNUNET_setup_general_services_vpn_checkbutton", "NO", NULL},
   {NULL, NULL, NULL}};


/**
 * Hide "namestore" tab if GNS not active.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_gns_tabs[] =
  {{"GNUNET_setup_namestore_vbox", "YES", NULL},
   {"GNUNET_setup_namestore_label", "YES", NULL},
   {NULL, NULL, NULL}};


/**
 * Hide "tcp" tab if TCP not active.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_tcp_tab[] =
  {{"GNUNET_setup_transport_tcp_vbox", "(^| )tcp($| )", NULL},
   {NULL, NULL, NULL}};


/**
 * Hide "udp" tab if UDP not active.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_udp_tab[] =
  {{"GNUNET_setup_transport_udp_vbox", "(^| )udp($| )", NULL},
   {NULL, NULL, NULL}};


/**
 * Hide "http-client" tab if HTTP client not active.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_http_client_tab[] =
  {{"GNUNET_setup_transport_http_client_vbox", "(^| )http_client($| )", NULL},
   {NULL, NULL, NULL}};

/**
 * Hide "http server" tab if HTTP server not active.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_http_server_tab[] =
  {{"GNUNET_setup_transport_http_server_vbox", "(^| )http_server($| )", NULL},
   {NULL, NULL, NULL}};

/**
 * Hide "https-client" tab if HTTPS client not active.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_https_client_tab[] =
  {{"GNUNET_setup_transport_https_client_vbox", "(^| )https_client($| )", NULL},
   {NULL, NULL, NULL}};

/**
 * Hide "https server" tab if HTTPS server not active.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_https_server_tab[] =
  {{"GNUNET_setup_transport_https_server_vbox", "(^| )https_server($| )", NULL},
   {NULL, NULL, NULL}};


/**
 * Hide "dv" tab if DV not active.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_dv_tab[] =
  {{"GNUNET_setup_transport_dv_vbox", "(^| )dv($| )", NULL},
   {NULL, NULL, NULL}};


/**
 * Hide "wlan" tab if WLAN not active.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_wlan_tab[] =
  {{"GNUNET_setup_transport_wlan_vbox", "(^| )wlan($| )", NULL},
   {NULL, NULL, NULL}};


/**
 * Hide "sqlite datastore" tab if sqlite not active.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_sqlite_datastore_tab[] =
  {{"GNUNET_setup_fs_datastore_sqlite_label", "^sqlite$", NULL},
   {NULL, NULL, NULL}};


/**
 * Hide "mysql datastore" tab if mysql not active.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_mysql_datastore_tab[] =
  {{"GNUNET_setup_datastore_mysql_vbox", "^mysql$", NULL}, {NULL, NULL, NULL}};


/**
 * Hide "postgres datastore" tab if postgres not active.
 */
static struct GNUNET_SETUP_VisibilitySpecification
  hide_postgres_datastore_tab[] =
    {{"GNUNET_setup_datastore_postgres_config_hbox", "^postgres$", NULL},
     {NULL, NULL, NULL}};


/**
 * Hide "sqlite datacache" tab if sqlite not active.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_sqlite_datacache_tab[] =
  {{"GNUNET_setup_fs_datacache_sqlite_body_label", "^sqlite$", NULL},
   {NULL, NULL, NULL}};


/**
 * Hide "heap datacache" tab if heap not active.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_heap_datacache_tab[] =
  {{"GNUNET_setup_datacache_heap_vbox", "^heap$", NULL}, {NULL, NULL, NULL}};


/**
 * Hide "postgres datacache" tab if postgres not active.
 */
static struct GNUNET_SETUP_VisibilitySpecification
  hide_postgres_datacache_tab[] = {{"GNUNET_setup_datacache_postgres_hbox",
                                    "^postgres$",
                                    NULL},
                                   {NULL, NULL, NULL}};


/**
 * Hide "sqlite namestore" tab if sqlite not active.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_sqlite_namestore_tab[] =
  {{"GNUNET_setup_namestore_sqlite_label", "^sqlite$", NULL},
   {NULL, NULL, NULL}};


/**
 * Hide "postgres datastore" tab if postgres not active.
 */
static struct GNUNET_SETUP_VisibilitySpecification
  hide_postgres_namestore_tab[] = {{"GNUNET_setup_namestore_postgres_hbox",
                                    "^postgres$",
                                    NULL},
                                   {NULL, NULL, NULL}};


/**
 * Hide advertised TCP port if port is zero.
 */
static struct GNUNET_SETUP_VisibilitySpecification hide_all_tcp_options[] =
  {{"GNUNET_setup_transport_tcp_adv_port_hbox", NULL, "^0$"},
   {NULL, NULL, NULL}};


/**
 * Option specification data.
 */
const struct GNUNET_SETUP_OptionSpecification option_specifications[] = {

  /* GENERAL TAB */

  {"GNUNET_setup_friends_only_checkbutton",
   "toggled",
   "topology",
   "FRIENDS-ONLY",
   gettext_noop ("Should GNUnet exclusively connect to friends?"),
   "https://docs.gnunet.org/#Configuring-the-Friend_002dto_002dFriend-_0028F2F_0029-mode",
   &load_yes_no,
   &save_yes_no,
   NULL,
   NULL,
   NULL,
   hide_min_connected_friends},

  {"GNUNET_setup_minimum_friends_spinbutton",
   "value-changed",
   "topology",
   "MINIMUM-FRIENDS",
   gettext_noop ("Minimum number of friendly connections"),
   "https://docs.gnunet.org/#Configuring-the-Friend_002dto_002dFriend-_0028F2F_0029-mode",
   &load_number,
   &save_number,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_general_services_topology_checkbutton",
   "toggled",
   "topology",
   "FORCESTART",
   gettext_noop ("Topology should always be loaded"),
   "",
   &load_yes_no,
   &save_yes_no,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_general_services_hostlist_checkbutton",
   "toggled",
   "hostlist",
   "FORCESTART",
   gettext_noop (
     "Should hostlist support be started automatically on startup?"),
   "https://docs.gnunet.org/#Configuring-the-hostlist-to-bootstrap",
   &load_yes_no,
   &save_yes_no,
   NULL,
   NULL,
   NULL,
   hide_hostlist_tab},

  {"GNUNET_setup_general_services_fs_checkbutton",
   "toggled",
   "fs",
   "FORCESTART",
   gettext_noop ("Should file-sharing be started automatically on startup?"),
   "https://docs.gnunet.org/#Configuring-the-file_002dsharing-service",
   &load_yes_no,
   &save_yes_no,
   NULL,
   NULL,
   NULL,
   hide_fs_tab},

  {"GNUNET_setup_hostlist_client_enable_checkbutton",
   "toggled",
   "hostlist",
   "OPTIONS",
   gettext_noop ("Should GNUnet learn about other peers using hostlists"),
   "https://docs.gnunet.org/#Configuring-the-hostlist-to-bootstrap",
   &load_option_list,
   &save_option_list,
   "-b",
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_hostlist_client_learn_checkbutton",
   "toggled",
   "hostlist",
   "OPTIONS",
   gettext_noop ("Should GNUnet learn hostlists from other peers"),
   "https://docs.gnunet.org/#Configuring-the-hostlist-to-bootstrap",
   &load_option_list,
   &save_option_list,
   "-e",
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_hostlist_offer_hostlist_checkbutton",
   "toggled",
   "hostlist",
   "OPTIONS",
   gettext_noop ("Should this peer offer a hostlist to other peers"),
   "https://docs.gnunet.org/#Configuring-your-peer-to-provide-a-hostlist",
   &load_option_list,
   &save_option_list,
   "-p",
   &highlight_port_collisions,
   NULL,
   hide_hostlist_server_options},

  {"GNUNET_setup_hostlist_advertise_checkbutton",
   "toggled",
   "hostlist",
   "OPTIONS",
   gettext_noop ("Should this peer advertise its hostlist to other peers"),
   "https://docs.gnunet.org/#Configuring-your-peer-to-provide-a-hostlist",
   &load_option_list,
   &save_option_list,
   "-a",
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_hostlist_server_port_spin_button",
   "value-changed",
   "hostlist",
   "HTTPPORT",
   gettext_noop ("Port this peers hostlist should be offered on"),
   "https://docs.gnunet.org/#Configuring-your-peer-to-provide-a-hostlist",
   &load_number,
   &save_number,
   NULL,
   &highlight_port_collisions,
   NULL,
   NULL},

  {"GNUNET_setup_hostlist_url_liststore",
   "row-changed",
   "hostlist",
   "SERVERS",
   NULL,
   NULL,
   &load_string_list_store,
   &save_string_list_store,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_hostlist_url_liststore",
   "row-deleted",
   "hostlist",
   "SERVERS",
   NULL,
   NULL,
   NULL,
   &save_string_list_store,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_hostlist_url_treeview",
   NULL,
   NULL,
   NULL, /* FIXME */
   gettext_noop ("Known hostlist URLs"),
   "https://docs.gnunet.org/#Configuring-the-hostlist-to-bootstrap",
   NULL,
   NULL,
   NULL, /* FIXME */
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_hostlist_proxy_type_none_radiobutton",
   "toggled",
   "hostlist",
   "PROXY_TYPE",
   gettext_noop ("Do not use a proxy to download hostlists"),
   "https://docs.gnunet.org/#Configuration-of-the-HOSTLIST-proxy-settings",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "NONE",
   NULL,
   NULL,
   hide_hostlist_proxy_options},

  {"GNUNET_setup_hostlist_proxy_type_http_radiobutton",
   "toggled",
   "hostlist",
   "PROXY_TYPE",
   gettext_noop ("Use an HTTP proxy"),
   "https://docs.gnunet.org/#Configuration-of-the-HOSTLIST-proxy-settings",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "HTTP",
   NULL,
   NULL,
   hide_hostlist_proxy_options},

  {"GNUNET_setup_hostlist_proxy_type_http_10_radiobutton",
   "toggled",
   "hostlist",
   "PROXY_TYPE",
   gettext_noop ("Use an HTTP v1.0 (only) proxy"),
   "https://docs.gnunet.org/#Configuration-of-the-HOSTLIST-proxy-settings",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "HTTP_1_0",
   NULL,
   NULL,
   hide_hostlist_proxy_options},

  {"GNUNET_setup_hostlist_proxy_type_socks4_radiobutton",
   "toggled",
   "hostlist",
   "PROXY_TYPE",
   gettext_noop ("Use a SOCKS v4 proxy"),
   "https://docs.gnunet.org/#Configuration-of-the-HOSTLIST-proxy-settings",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "SOCKS4",
   NULL,
   NULL,
   hide_hostlist_proxy_options},

  {"GNUNET_setup_hostlist_proxy_type_socks4a_radiobutton",
   "toggled",
   "hostlist",
   "PROXY_TYPE",
   gettext_noop ("Use a SOCKS v4a proxy"),
   "https://docs.gnunet.org/#Configuration-of-the-HOSTLIST-proxy-settings",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "SOCKS4a",
   NULL,
   NULL,
   hide_hostlist_proxy_options},

  {"GNUNET_setup_hostlist_proxy_type_socks5_radiobutton",
   "toggled",
   "hostlist",
   "PROXY_TYPE",
   gettext_noop ("Use a SOCKS v5 proxy"),
   "https://docs.gnunet.org/#Configuration-of-the-HOSTLIST-proxy-settings",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "SOCKS5",
   NULL,
   NULL,
   hide_hostlist_proxy_options},

  {"GNUNET_setup_hostlist_proxy_type_socks5_hostname_radiobutton",
   "toggled",
   "hostlist",
   "PROXY_TYPE",
   gettext_noop ("Use a SOCKS v5 proxy with hostname"),
   "https://docs.gnunet.org/#Configuration-of-the-HOSTLIST-proxy-settings",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "SOCKS5_HOSTNAME",
   NULL,
   NULL,
   hide_hostlist_proxy_options},

  {"GNUNET_setup_hostlist_proxy_hostname_entry",
   "changed",
   "hostlist",
   "PROXY",
   gettext_noop ("Specify hostname or IP (and optionally) port of the proxy"),
   "https://docs.gnunet.org/#Configuration-of-the-HOSTLIST-proxy-settings",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_hostlist_proxy_username_entry",
   "changed",
   "hostlist",
   "PROXY_USERNAME",
   gettext_noop ("Specify username for the proxy (if needed)"),
   "https://docs.gnunet.org/#Configuration-of-the-HOSTLIST-proxy-settings",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_hostlist_proxy_password_entry",
   "changed",
   "hostlist",
   "PROXY_PASSWORD",
   gettext_noop ("Specify password for the proxy (if needed)"),
   "https://docs.gnunet.org/#Configuration-of-the-HOSTLIST-proxy-settings",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_bandwidth_out_spinbutton",
   "value-changed",
   "ats",
   "WAN_QUOTA_OUT",
   gettext_noop ("How many bytes per second are we allowed to transmit?"),
   "https://docs.gnunet.org/#Bandwidth-Configuration",
   &load_number,
   &save_number,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_bandwidth_in_spinbutton",
   "value-changed",
   "ats",
   "WAN_QUOTA_IN",
   gettext_noop ("How many bytes per second are we allowed to receive?"),
   "https://old.gnunet.org/configuration-bandwidth",
   &load_number,
   &save_number,
   NULL,
   NULL,
   NULL,
   NULL},

  /* Transport TAB */

  {"GNUNET_setup_transport_tcp_checkbutton",
   "toggled",
   "transport",
   "PLUGINS",
   gettext_noop ("Enable communication via TCP"),
   "https://docs.gnunet.org/#Configuring-the-transport-service-and-plugins",
   &load_option_list,
   &save_option_list,
   "tcp",
   &highlight_port_collisions,
   NULL,
   hide_tcp_tab},

  {"GNUNET_setup_transport_udp_checkbutton",
   "toggled",
   "transport",
   "PLUGINS",
   gettext_noop ("Enable communication via UDP"),
   "https://docs.gnunet.org/#Configuring-the-transport-service-and-plugins",
   &load_option_list,
   &save_option_list,
   "udp",
   NULL,
   NULL,
   hide_udp_tab},

  {"GNUNET_setup_transport_http_server_checkbutton",
   "toggled",
   "transport",
   "PLUGINS",
   gettext_noop ("Enable communication via HTTP as a server"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_option_list,
   &save_option_list,
   "http_server",
   &highlight_port_collisions,
   NULL,
   hide_http_server_tab},

  {"GNUNET_setup_transport_http_client_checkbutton",
   "toggled",
   "transport",
   "PLUGINS",
   gettext_noop ("Enable communication via HTTP as a client"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_option_list,
   &save_option_list,
   "http_client",
   &highlight_port_collisions,
   NULL,
   hide_http_client_tab},

  {"GNUNET_setup_transport_https_server_checkbutton",
   "toggled",
   "transport",
   "PLUGINS",
   gettext_noop ("Enable communication via HTTPS as a server"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_option_list,
   &save_option_list,
   "https_server",
   &highlight_port_collisions,
   NULL,
   hide_https_server_tab},

  {"GNUNET_setup_transport_https_client_checkbutton",
   "toggled",
   "transport",
   "PLUGINS",
   gettext_noop ("Enable communication via HTTPS as a client"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_option_list,
   &save_option_list,
   "https_client",
   &highlight_port_collisions,
   NULL,
   hide_https_client_tab},

  {"GNUNET_setup_transport_dv_checkbutton",
   "toggled",
   "transport",
   "PLUGINS",
   gettext_noop ("Enable communication via DV"),
   "https://docs.gnunet.org/#Distance_002dVector-plugin",
   &load_option_list,
   &save_option_list,
   "dv",
   NULL,
   NULL,
   hide_dv_tab},

  {"GNUNET_setup_transport_wlan_checkbutton",
   "toggled",
   "transport",
   "PLUGINS",
   gettext_noop ("Enable communication via WLAN"),
   "https://docs.gnunet.org/#WLAN-plugin",
   &load_option_list,
   &save_option_list,
   "wlan",
   NULL,
   NULL,
   hide_wlan_tab},

  {"GNUNET_setup_transport_tcp_port_spinbutton",
   "value-changed",
   "transport-tcp",
   "PORT",
   gettext_noop ("Port we bind to for TCP"),
   "https://docs.gnunet.org/#Configuring-the-transport-service-and-plugins",
   &load_number,
   &save_number,
   NULL,
   &highlight_port_collisions,
   NULL,
   hide_all_tcp_options},

  {"GNUNET_setup_transport_tcp_adv_port_spinbutton",
   "value-changed",
   "transport-tcp",
   "ADVERTISED_PORT",
   gettext_noop ("Port visible to other peers"),
   "https://docs.gnunet.org/#Configuring-the-transport-service-and-plugins",
   &load_number,
   &save_number,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_transport_udp_port_spinbutton",
   "value-changed",
   "transport-udp",
   "PORT",
   gettext_noop ("Port for inbound UDP packets, use 0 if behind NAT"),
   "https://docs.gnunet.org/#Configuring-the-transport-service-and-plugins",
   &load_number,
   &save_number,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_transport_udp_advertised_port_spinbutton",
   "value-changed",
   "transport-udp",
   "ADVERTISED_PORT",
   gettext_noop ("Port visible to other peers"),
   "https://docs.gnunet.org/#Configuring-the-transport-service-and-plugins",
   &load_number,
   &save_number,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_transport_http_port_spinbutton",
   "value-changed",
   "transport-http_server",
   "PORT",
   gettext_noop ("Port for inbound HTTP connections, use 0 if behind NAT"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_number,
   &save_number,
   NULL,
   &highlight_port_collisions,
   NULL,
   NULL},

  {"GNUNET_setup_transport_http_advertised_port_spinbutton",
   "value-changed",
   "transport-http_server",
   "ADVERTISED_PORT",
   gettext_noop ("Port visible to other peers"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_number,
   &save_number,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_transport_https_port_spinbutton",
   "value-changed",
   "transport-https_server",
   "PORT",
   gettext_noop ("Port for inbound HTTPS connections, use 0 if behind NAT"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_number,
   &save_number,
   NULL,
   &highlight_port_collisions,
   NULL,
   NULL},

  {"GNUNET_setup_transport_https_advertised_port_spinbutton",
   "value-changed",
   "transport-https_server",
   "ADVERTISED_PORT",
   gettext_noop ("Port visible to other peers"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_number,
   &save_number,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_transport_http_client_proxy_type_none_radiobutton",
   "toggled",
   "transport-http_client",
   "PROXY_TYPE",
   gettext_noop ("Do not use a proxy with the HTTP plugin"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "NONE",
   NULL,
   NULL,
   hide_http_proxy_options},

  {"GNUNET_setup_transport_http_client_proxy_type_http_radiobutton",
   "toggled",
   "transport-http_client",
   "PROXY_TYPE",
   gettext_noop ("Use an HTTP proxy"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "HTTP",
   NULL,
   NULL,
   hide_http_proxy_options},

  {"GNUNET_setup_transport_http_client_proxy_type_socks4_radiobutton",
   "toggled",
   "transport-http_client",
   "PROXY_TYPE",
   gettext_noop ("Use a SOCKS v4 proxy"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "SOCKS4",
   NULL,
   NULL,
   hide_http_proxy_options},

  {"GNUNET_setup_transport_http_client_proxy_type_socks4a_radiobutton",
   "toggled",
   "transport-http_client",
   "PROXY_TYPE",
   gettext_noop ("Use a SOCKS v4a proxy"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "SOCKS4a",
   NULL,
   NULL,
   hide_http_proxy_options},

  {"GNUNET_setup_transport_http_client_proxy_type_socks5_radiobutton",
   "toggled",
   "transport-http_client",
   "PROXY_TYPE",
   gettext_noop ("Use a SOCKS v5 proxy"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "SOCKS5",
   NULL,
   NULL,
   hide_http_proxy_options},

  {"GNUNET_setup_transport_http_client_proxy_type_socks5_hostname_radiobutton",
   "toggled",
   "transport-http_client",
   "PROXY_TYPE",
   gettext_noop ("Use a SOCKS v5 proxy with hostname"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "SOCKS5_HOSTNAME",
   NULL,
   NULL,
   hide_http_proxy_options},

  {"GNUNET_setup_transport_http_client_proxy_hostname_entry",
   "changed",
   "transport-http_client",
   "PROXY",
   gettext_noop ("Specify hostname or IP (and optionally) port of the proxy"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_transport_http_client_proxy_username_entry",
   "changed",
   "transport-http_client",
   "PROXY_USERNAME",
   gettext_noop ("Specify username for the proxy (if needed)"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_transport_http_client_proxy_password_entry",
   "changed",
   "transport-http_client",
   "PROXY_PASSWORD",
   gettext_noop ("Specify password for the proxy (if needed)"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},


  /* HTTPS client */

  {"GNUNET_setup_transport_https_client_proxy_type_none_radiobutton",
   "toggled",
   "transport-https_client",
   "PROXY_TYPE",
   gettext_noop ("Do not use a proxy for HTTPS plugin"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "NONE",
   NULL,
   NULL,
   hide_https_proxy_options},

  {"GNUNET_setup_transport_https_client_proxy_type_http_radiobutton",
   "toggled",
   "transport-https_client",
   "PROXY_TYPE",
   gettext_noop ("Use an HTTP proxy"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "HTTP",
   NULL,
   NULL,
   hide_https_proxy_options},

  {"GNUNET_setup_transport_https_client_proxy_type_socks4_radiobutton",
   "toggled",
   "transport-https_client",
   "PROXY_TYPE",
   gettext_noop ("Use a SOCKS v4 proxy"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "SOCKS4",
   NULL,
   NULL,
   hide_https_proxy_options},

  {"GNUNET_setup_transport_https_client_proxy_type_socks4a_radiobutton",
   "toggled",
   "transport-https_client",
   "PROXY_TYPE",
   gettext_noop ("Use a SOCKS v4a proxy"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "SOCKS4a",
   NULL,
   NULL,
   hide_https_proxy_options},

  {"GNUNET_setup_transport_https_client_proxy_type_socks5_radiobutton",
   "toggled",
   "transport-https_client",
   "PROXY_TYPE",
   gettext_noop ("Use a SOCKS v5 proxy"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "SOCKS5",
   NULL,
   NULL,
   hide_https_proxy_options},

  {"GNUNET_setup_transport_https_client_proxy_type_socks5_hostname_radiobutton",
   "toggled",
   "transport-https_client",
   "PROXY_TYPE",
   gettext_noop ("Use a SOCKS v5 proxy with hostname"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "SOCKS5_HOSTNAME",
   NULL,
   NULL,
   hide_https_proxy_options},

  {"GNUNET_setup_transport_https_client_proxy_hostname_entry",
   "changed",
   "transport-https_client",
   "PROXY",
   gettext_noop ("Specify hostname or IP (and optionally) port of the proxy"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_transport_https_client_proxy_username_entry",
   "changed",
   "transport-https_client",
   "PROXY_USERNAME",
   gettext_noop ("Specify username for the proxy (if needed)"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_transport_https_client_proxy_password_entry",
   "changed",
   "transport-https_client",
   "PROXY_PASSWORD",
   gettext_noop ("Specify password for the proxy (if needed)"),
   "https://docs.gnunet.org/#Configuration-of-the-HTTP-and-HTTPS-transport-plugins",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  /* FS TAB */

  {"GNUNET_setup_fs_datastore_quota_spinbutton",
   "value-changed",
   "datastore",
   "QUOTA",
   gettext_noop (
     "How many bytes are we allowed to store in the local datastore?"),
   "https://docs.gnunet.org/#Configuring-the-datastore",
   &load_number,
   &save_number,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_fs_datastore_sqlite_radiobutton",
   "toggled",
   "datastore",
   "DATABASE",
   gettext_noop ("Use sqLite to store file-sharing content"),
   "https://docs.gnunet.org/#Configuring-the-datastore",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "sqlite",
   NULL,
   NULL,
   hide_sqlite_datastore_tab},

  {"GNUNET_setup_fs_datastore_mysql_radiobutton",
   "toggled",
   "datastore",
   "DATABASE",
   gettext_noop ("Use MySQL to store file-sharing content"),
   "https://docs.gnunet.org/#Configuring-the-datastore",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "mysql",
   NULL,
   NULL,
   hide_mysql_datastore_tab},

  {"GNUNET_setup_fs_datastore_postgres_radiobutton",
   "toggled",
   "datastore",
   "DATABASE",
   gettext_noop ("Use Postgres to store file-sharing content"),
   "https://docs.gnunet.org/#Configuring-the-datastore",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "postgres",
   NULL,
   NULL,
   hide_postgres_datastore_tab},

  {"GNUNET_setup_datastore_mysql_database_name_entry",
   "changed",
   "datastore-mysql",
   "DATABASE",
   gettext_noop ("Name for the MySQL database"),
   "https://docs.gnunet.org/#Configuring-the-datastore",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_datastore_mysql_config_file_filechooserbutton",
   "selection-changed",
   "datastore-mysql",
   "CONFIG",
   gettext_noop ("Configuration file for MySQL access"),
   "http://dev.mysql.com/doc/refman/5.5/en/option-files.html",
   &load_filename,
   &save_filename,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_datastore_mysql_username_entry",
   "changed",
   "datastore-mysql",
   "USER",
   gettext_noop ("Username for MySQL access"),
   "https://docs.gnunet.org/#Configuring-the-datastore",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_datastore_mysql_password_entry",
   "changed",
   "datastore-mysql",
   "PASSWORD",
   gettext_noop ("Password for MySQL access"),
   "https://docs.gnunet.org/#Configuring-the-datastore",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_datastore_mysql_hostname_entry",
   "changed",
   "datastore-mysql",
   "HOST",
   gettext_noop ("Name of host running MySQL database"),
   "https://docs.gnunet.org/#Configuring-the-datastore",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_datastore_mysql_port_spinbutton",
   "value-changed",
   "datastore-mysql",
   "PORT",
   gettext_noop ("Port of MySQL database"),
   "https://docs.gnunet.org/#Configuring-the-datastore",
   &load_number,
   &save_number,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_datastore_postgres_config_entry",
   "changed",
   "datastore-postgres",
   "CONFIG",
   gettext_noop ("Configuration for Postgres (passed to PQconnectdb)"),
   "http://www.postgresql.org/docs/8.1/static/libpq.html#LIBPQ-CONNECT",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},


  {"GNUNET_setup_fs_migration_from_checkbutton",
   "toggled",
   "fs",
   "CONTENT_PUSHING",
   gettext_noop ("Should we try to push our content to other peers?"),
   "https://docs.gnunet.org/#Configuring-the-file_002dsharing-service",
   &load_yes_no,
   &save_yes_no,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_fs_migration_to_checkbutton",
   "toggled",
   "fs",
   "CONTENT_CACHING",
   gettext_noop ("Are we allowed to cache content received from other peers?"),
   "https://docs.gnunet.org/#Configuring-the-file_002dsharing-service",
   &load_yes_no,
   &save_yes_no,
   NULL,
   NULL,
   NULL,
   NULL},


  {"GNUNET_setup_fs_datacache_quota_spinbutton",
   "value-changed",
   "dhtcache",
   "QUOTA",
   gettext_noop (
     "How many bytes are we allowed to store in the local datacache?"),
   "https://docs.gnunet.org/#Configuring-the-datacache",
   &load_number,
   &save_number,
   NULL,
   NULL,
   NULL,
   NULL},


  {"GNUNET_setup_fs_datacache_sqlite_radiobutton",
   "toggled",
   "dhtcache",
   "DATABASE",
   gettext_noop ("Use sqLite to cache DHT data"),
   "https://docs.gnunet.org/#Configuring-the-datacache",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "sqlite",
   NULL,
   NULL,
   hide_sqlite_datacache_tab},

  {"GNUNET_setup_fs_datacache_heap_radiobutton",
   "toggled",
   "dhtcache",
   "DATABASE",
   gettext_noop ("Use memory to cache DHT data"),
   "https://docs.gnunet.org/#Configuring-the-datacache",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "heap",
   NULL,
   NULL,
   hide_heap_datacache_tab},

  {"GNUNET_setup_fs_datacache_postgres_radiobutton",
   "toggled",
   "dhtcache",
   "DATABASE",
   gettext_noop ("Use Postgres to cache DHT data"),
   "https://docs.gnunet.org/#Configuring-the-datacache",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "postgres",
   NULL,
   NULL,
   hide_postgres_datacache_tab},

  {"GNUNET_setup_transport_wlan_interface_entry",
   "changed",
   "transport-wlan",
   "INTERFACE",
   gettext_noop ("Name of monitoring interface to use (monX)"),
   "https://docs.gnunet.org/#Configuring-the-WLAN-transport-plugin",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_datacache_postgres_config_entry",
   "changed",
   "datacache-postgres",
   "CONFIG",
   gettext_noop ("Configuration for Postgres (passed to PQconnectdb)"),
   "http://www.postgresql.org/docs/8.1/static/libpq.html#LIBPQ-CONNECT",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  /* VPN/PT service */

  {"GNUNET_setup_general_services_vpn_checkbutton",
   "toggled",
   "vpn",
   "FORCESTART",
   gettext_noop ("Should the VPN be started automatically on startup?"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN",
   &load_yes_no,
   &save_yes_no,
   NULL,
   NULL,
   NULL,
   hide_vpn_options},

  {"GNUNET_setup_general_services_pt_checkbutton",
   "toggled",
   "pt",
   "FORCESTART",
   gettext_noop ("Should the PT be started automatically on startup?"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN",
   &load_yes_no,
   &save_yes_no,
   NULL,
   NULL,
   NULL,
   hide_pt_options},


  {"GNUNET_setup_pt_ipv4_checkbutton",
   "toggled",
   "pt",
   "TUNNEL_IPV4",
   gettext_noop ("Tunnel IPv4 traffic over GNUnet"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN",
   &load_yes_no,
   &save_yes_no,
   NULL,
   NULL,
   NULL,
   NULL},


  {"GNUNET_setup_pt_ipv6_checkbutton",
   "toggled",
   "pt",
   "TUNNEL_IPV6",
   gettext_noop ("Tunnel IPv6 traffic over GNUnet"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN",
   &load_yes_no,
   &save_yes_no,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_pt_dns_checkbutton",
   "toggled",
   "pt",
   "TUNNEL_DNS",
   gettext_noop ("Tunnel DNS traffic over GNUnet"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN",
   &load_yes_no,
   &save_yes_no,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_vpn_interface_entry",
   "changed",
   "vpn",
   "IFNAME",
   gettext_noop ("Name of the virtual interface the GNUnet VPN should create"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_vpn_interface_v4_ip_entry",
   "changed",
   "vpn",
   "IPV4ADDR",
   gettext_noop ("IPv4 address to use for the VPN interface"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_vpn_interface_v4_mask_entry",
   "changed",
   "vpn",
   "IPV4MASK",
   gettext_noop ("IPv4 network mask to use for the VPN interface"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_vpn_interface_v6_ip_entry",
   "changed",
   "vpn",
   "IPV6ADDR",
   gettext_noop ("IPv6 address to use for the VPN interface"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_vpn_interface_v6_mask_spinbutton",
   "value-changed",
   "vpn",
   "IPV6PREFIX",
   gettext_noop ("IPv6 network prefix length to use for the VPN interface"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN",
   &load_number,
   &save_number,
   NULL,
   NULL,
   NULL,
   NULL},


  /* exit daemon */

  {"GNUNET_setup_general_services_exit_checkbutton",
   "toggled",
   "exit",
   "FORCESTART",
   gettext_noop (
     "Activate the VPN exit to provide services and/or to enable others to use your Internet connection"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN-Exit-Service",
   &load_yes_no,
   &save_yes_no,
   NULL,
   NULL,
   NULL,
   hide_exit_options},

  {"GNUNET_setup_dns_resolver_ip_entry",
   "changed",
   "exit",
   "DNS_RESOLVER",
   gettext_noop (
     "IP address of the external DNS resolver to use (values from your resolve.conf are usually appropriate))"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN-Exit-Service",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_dns_enable_exit_checkbutton",
   "toggled",
   "exit",
   "EXIT_DNS",
   gettext_noop (
     "Allow other peers to perform DNS resolutions using your Internet connection"),
   "https://docs.gnunet.org/#IP-Address-of-external-DNS-resolver",
   &load_yes_no,
   &save_yes_no,
   NULL,
   NULL,
   NULL,
   hide_dns_exit_options},

  {"GNUNET_setup_exit_interface_name_entry",
   "changed",
   "exit",
   "TUN_IFNAME",
   gettext_noop (
     "Name of the virtual interface the GNUnet exit service should create for traffic exiting the VPN to the Internet"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN-Exit-Service",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_exit_interface_v4_ip_entry",
   "changed",
   "exit",
   "IPV4ADDR",
   gettext_noop ("IPv4 address to use for the Exit interface"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN-Exit-Service",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_exit_interface_v4_mask_entry",
   "changed",
   "exit",
   "IPV4MASK",
   gettext_noop ("IPv4 network mask to use for the Exit interface"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN-Exit-Service",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_exit_interface_v6_ip_entry",
   "changed",
   "exit",
   "IPV6ADDR",
   gettext_noop ("IPv6 address to use for the Exit interface"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN-Exit-Service",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_setup_exit_interface_v6_mask_spinbutton",
   "value-changed",
   "exit",
   "IPV6PREFIX",
   gettext_noop ("IPv6 network prefix length to use for the Exit interface"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN-Exit-Service",
   &load_number,
   &save_number,
   NULL,
   NULL,
   NULL,
   NULL},


  {"GNUNET_setup_exit_enable_ipv4_exit_checkbutton",
   "toggled",
   "exit",
   "EXIT_IPV4",
   gettext_noop (
     "Allow other users to use your Internet connection for UDP traffic (via the Exit interface)"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN-Exit-Service",
   &load_yes_no,
   &save_yes_no,
   NULL,
   NULL,
   NULL,
   hide_v4_exit_options},

  {"GNUNET_setup_exit_enable_ipv6_exit_checkbutton",
   "toggled",
   "exit",
   "EXIT_IPV6",
   gettext_noop (
     "Allow other users to use your Internet connection for TCP traffic (via the Exit interface)"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN-Exit-Service",
   &load_yes_no,
   &save_yes_no,
   NULL,
   NULL,
   NULL,
   hide_v6_exit_options},


  {"GNUNET_SETUP_exit_policy_v4_entry",
   "changed",
   "exit",
   "EXIT_RANGE_IPV4_POLICY",
   gettext_noop (
     "Which IPv4 addresses and ports do you allow other users to send traffic towards (via the Exit interface)"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN-Exit-Service",
   &load_text,
   &save_text,
   NULL,
   &validate_v4_policy,
   NULL,
   NULL},


  {"GNUNET_SETUP_exit_policy_v6_entry",
   "changed",
   "exit",
   "EXIT_RANGE_IPV6_POLICY",
   gettext_noop (
     "Which IPv6 addresses and ports do you allow other users to send traffic towards (via the Exit interface)"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN-Exit-Service",
   &load_text,
   &save_text,
   NULL,
   &validate_v6_policy,
   NULL,
   NULL},


  /* Namestore TAB */

  {"GNUNET_setup_namestore_sqlite_radiobutton",
   "toggled",
   "namestore",
   "DATABASE",
   gettext_noop ("Use sqLite to store names"),
   "",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "sqlite",
   NULL,
   NULL,
   hide_sqlite_namestore_tab},

  {"GNUNET_setup_namestore_postgres_radiobutton",
   "toggled",
   "namestore",
   "DATABASE",
   gettext_noop ("Use PostGres to store names"),
   "",
   &load_option_list /* abuse! */,
   &save_option_list /* abuse! */,
   "postgres",
   NULL,
   NULL,
   hide_postgres_namestore_tab},

  {"GNUNET_setup_namestore_postgres_config_entry",
   "changed",
   "namestore-postgres",
   "CONFIG",
   gettext_noop ("Configuration for Postgres (passed to PQconnectdb)"),
   "http://www.postgresql.org/docs/8.1/static/libpq.html#LIBPQ-CONNECT",
   &load_text,
   &save_text,
   NULL,
   NULL,
   NULL,
   NULL},


  /* GNS */

  {"GNUNET_setup_general_services_gns_checkbutton",
   "toggled",
   "gns",
   "FORCESTART",
   gettext_noop ("Should the GNS be started automatically on startup?"),
   "https://docs.gnunet.org/#Configuring-the-GNU-Name-System",
   &load_yes_no,
   &save_yes_no,
   NULL,
   NULL,
   NULL,
   hide_gns_tabs},


  /* EXIT services */

  {"GNUNET_setup_hosted_service_treeview",
   NULL,
   NULL,
   NULL,
   gettext_noop ("Specification of .gnunet hosted services"),
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN-Exit-Service",
   &load_hosted_service_configuration,
   NULL,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_SETUP_hosted_service_identifier_cellrenderertext",
   "editing-started",
   NULL,
   NULL,
   NULL,
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN-Exit-Service",
   NULL,
   &hosted_service_name_install_edited_handler,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_SETUP_hosted_service_is_udp_cellrenderertoggle",
   "toggled",
   NULL,
   NULL,
   NULL,
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN-Exit-Service",
   NULL,
   &hosted_service_is_udp_install_toggled_handler,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_SETUP_hosted_service_port_cellrenderertext",
   "editing-started",
   NULL,
   NULL,
   NULL,
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN-Exit-Service",
   NULL,
   &hosted_service_visible_port_install_edited_handler,
   NULL,
   NULL,
   NULL,
   NULL},

  {"GNUNET_SETUP_hosted_service_destination_cellrenderertext",
   "editing-started",
   NULL,
   NULL,
   NULL,
   "https://docs.gnunet.org/#Configuring-the-GNUnet-VPN-Exit-Service",
   NULL,
   &hosted_service_destination_install_edited_handler,
   NULL,
   NULL,
   NULL,
   NULL},


  /* END of list */

  {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}};


/* end of gnunet-setup-options.c */
